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 = 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
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3065 * The when any image loads
3066 * @param {Roo.EventObject} e
3072 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3074 imgResponsive: true,
3083 backgroundContain : false,
3085 getAutoCreate : function()
3087 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3088 return this.createSingleImg();
3093 cls: 'roo-image-responsive-group',
3098 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100 if(!_this[size + 'Url']){
3106 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3107 html: _this.html || cfg.html,
3108 src: _this[size + 'Url']
3111 img.cls += ' roo-image-responsive-' + size;
3113 var s = ['xs', 'sm', 'md', 'lg'];
3115 s.splice(s.indexOf(size), 1);
3117 Roo.each(s, function(ss){
3118 img.cls += ' hidden-' + ss;
3121 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3122 cfg.cls += ' img-' + _this.border;
3126 cfg.alt = _this.alt;
3139 a.target = _this.target;
3143 cfg.cn.push((_this.href) ? a : img);
3150 createSingleImg : function()
3154 cls: (this.imgResponsive) ? 'img-responsive' : '',
3156 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3159 if (this.backgroundContain) {
3160 cfg.cls += ' background-contain';
3163 cfg.html = this.html || cfg.html;
3165 if (this.backgroundContain) {
3166 cfg.style="background-image: url(" + this.src + ')';
3168 cfg.src = this.src || cfg.src;
3171 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3172 cfg.cls += ' img-' + this.border;
3189 a.target = this.target;
3194 return (this.href) ? a : cfg;
3197 initEvents: function()
3200 this.el.on('click', this.onClick, this);
3202 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3203 this.el.on('load', this.onImageLoad, this);
3205 // not sure if this works.. not tested
3206 this.el.select('img', true).on('load', this.onImageLoad, this);
3211 onClick : function(e)
3213 Roo.log('img onclick');
3214 this.fireEvent('click', this, e);
3216 onImageLoad: function(e)
3218 Roo.log('img load');
3219 this.fireEvent('load', this, e);
3223 * Sets the url of the image - used to update it
3224 * @param {String} url the url of the image
3227 setSrc : function(url)
3231 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3232 this.el.dom.src = url;
3236 this.el.select('img', true).first().dom.src = url;
3252 * @class Roo.bootstrap.Link
3253 * @extends Roo.bootstrap.Component
3254 * Bootstrap Link Class
3255 * @cfg {String} alt image alternative text
3256 * @cfg {String} href a tag href
3257 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3258 * @cfg {String} html the content of the link.
3259 * @cfg {String} anchor name for the anchor link
3260 * @cfg {String} fa - favicon
3262 * @cfg {Boolean} preventDefault (true | false) default false
3266 * Create a new Input
3267 * @param {Object} config The config object
3270 Roo.bootstrap.Link = function(config){
3271 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277 * The img click event for the img.
3278 * @param {Roo.EventObject} e
3284 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3288 preventDefault: false,
3294 getAutoCreate : function()
3296 var html = this.html || '';
3298 if (this.fa !== false) {
3299 html = '<i class="fa fa-' + this.fa + '"></i>';
3304 // anchor's do not require html/href...
3305 if (this.anchor === false) {
3307 cfg.href = this.href || '#';
3309 cfg.name = this.anchor;
3310 if (this.html !== false || this.fa !== false) {
3313 if (this.href !== false) {
3314 cfg.href = this.href;
3318 if(this.alt !== false){
3323 if(this.target !== false) {
3324 cfg.target = this.target;
3330 initEvents: function() {
3332 if(!this.href || this.preventDefault){
3333 this.el.on('click', this.onClick, this);
3337 onClick : function(e)
3339 if(this.preventDefault){
3342 //Roo.log('img onclick');
3343 this.fireEvent('click', this, e);
3356 * @class Roo.bootstrap.Header
3357 * @extends Roo.bootstrap.Component
3358 * Bootstrap Header class
3359 * @cfg {String} html content of header
3360 * @cfg {Number} level (1|2|3|4|5|6) default 1
3363 * Create a new Header
3364 * @param {Object} config The config object
3368 Roo.bootstrap.Header = function(config){
3369 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3372 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3380 getAutoCreate : function(){
3385 tag: 'h' + (1 *this.level),
3386 html: this.html || ''
3398 * Ext JS Library 1.1.1
3399 * Copyright(c) 2006-2007, Ext JS, LLC.
3401 * Originally Released Under LGPL - original licence link has changed is not relivant.
3404 * <script type="text/javascript">
3408 * @class Roo.bootstrap.MenuMgr
3409 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3412 Roo.bootstrap.MenuMgr = function(){
3413 var menus, active, groups = {}, attached = false, lastShow = new Date();
3415 // private - called when first menu is created
3418 active = new Roo.util.MixedCollection();
3419 Roo.get(document).addKeyListener(27, function(){
3420 if(active.length > 0){
3428 if(active && active.length > 0){
3429 var c = active.clone();
3439 if(active.length < 1){
3440 Roo.get(document).un("mouseup", onMouseDown);
3448 var last = active.last();
3449 lastShow = new Date();
3452 Roo.get(document).on("mouseup", onMouseDown);
3457 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3458 m.parentMenu.activeChild = m;
3459 }else if(last && last.isVisible()){
3460 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3465 function onBeforeHide(m){
3467 m.activeChild.hide();
3469 if(m.autoHideTimer){
3470 clearTimeout(m.autoHideTimer);
3471 delete m.autoHideTimer;
3476 function onBeforeShow(m){
3477 var pm = m.parentMenu;
3478 if(!pm && !m.allowOtherMenus){
3480 }else if(pm && pm.activeChild && active != m){
3481 pm.activeChild.hide();
3485 // private this should really trigger on mouseup..
3486 function onMouseDown(e){
3487 Roo.log("on Mouse Up");
3489 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3490 Roo.log("MenuManager hideAll");
3499 function onBeforeCheck(mi, state){
3501 var g = groups[mi.group];
3502 for(var i = 0, l = g.length; i < l; i++){
3504 g[i].setChecked(false);
3513 * Hides all menus that are currently visible
3515 hideAll : function(){
3520 register : function(menu){
3524 menus[menu.id] = menu;
3525 menu.on("beforehide", onBeforeHide);
3526 menu.on("hide", onHide);
3527 menu.on("beforeshow", onBeforeShow);
3528 menu.on("show", onShow);
3530 if(g && menu.events["checkchange"]){
3534 groups[g].push(menu);
3535 menu.on("checkchange", onCheck);
3540 * Returns a {@link Roo.menu.Menu} object
3541 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3542 * be used to generate and return a new Menu instance.
3544 get : function(menu){
3545 if(typeof menu == "string"){ // menu id
3547 }else if(menu.events){ // menu instance
3550 /*else if(typeof menu.length == 'number'){ // array of menu items?
3551 return new Roo.bootstrap.Menu({items:menu});
3552 }else{ // otherwise, must be a config
3553 return new Roo.bootstrap.Menu(menu);
3560 unregister : function(menu){
3561 delete menus[menu.id];
3562 menu.un("beforehide", onBeforeHide);
3563 menu.un("hide", onHide);
3564 menu.un("beforeshow", onBeforeShow);
3565 menu.un("show", onShow);
3567 if(g && menu.events["checkchange"]){
3568 groups[g].remove(menu);
3569 menu.un("checkchange", onCheck);
3574 registerCheckable : function(menuItem){
3575 var g = menuItem.group;
3580 groups[g].push(menuItem);
3581 menuItem.on("beforecheckchange", onBeforeCheck);
3586 unregisterCheckable : function(menuItem){
3587 var g = menuItem.group;
3589 groups[g].remove(menuItem);
3590 menuItem.un("beforecheckchange", onBeforeCheck);
3602 * @class Roo.bootstrap.Menu
3603 * @extends Roo.bootstrap.Component
3604 * Bootstrap Menu class - container for MenuItems
3605 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3606 * @cfg {bool} hidden if the menu should be hidden when rendered.
3607 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3608 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3609 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3610 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3614 * @param {Object} config The config object
3618 Roo.bootstrap.Menu = function(config){
3620 if (config.type == 'treeview') {
3621 // normally menu's are drawn attached to the document to handle layering etc..
3622 // however treeview (used by the docs menu is drawn into the parent element)
3623 this.container_method = 'getChildContainer';
3626 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3627 if (this.registerMenu && this.type != 'treeview') {
3628 Roo.bootstrap.MenuMgr.register(this);
3635 * Fires before this menu is displayed (return false to block)
3636 * @param {Roo.menu.Menu} this
3641 * Fires before this menu is hidden (return false to block)
3642 * @param {Roo.menu.Menu} this
3647 * Fires after this menu is displayed
3648 * @param {Roo.menu.Menu} this
3653 * Fires after this menu is hidden
3654 * @param {Roo.menu.Menu} this
3659 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3660 * @param {Roo.menu.Menu} this
3661 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3662 * @param {Roo.EventObject} e
3667 * Fires when the mouse is hovering over this menu
3668 * @param {Roo.menu.Menu} this
3669 * @param {Roo.EventObject} e
3670 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3675 * Fires when the mouse exits this menu
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.EventObject} e
3678 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3683 * Fires when a menu item contained in this menu is clicked
3684 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3685 * @param {Roo.EventObject} e
3689 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3692 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3696 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3699 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3701 registerMenu : true,
3703 menuItems :false, // stores the menu items..
3713 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3715 hideTrigger : false,
3720 getChildContainer : function() {
3724 getAutoCreate : function(){
3726 //if (['right'].indexOf(this.align)!==-1) {
3727 // cfg.cn[1].cls += ' pull-right'
3732 cls : 'dropdown-menu shadow' ,
3733 style : 'z-index:1000'
3737 if (this.type === 'submenu') {
3738 cfg.cls = 'submenu active';
3740 if (this.type === 'treeview') {
3741 cfg.cls = 'treeview-menu';
3746 initEvents : function() {
3748 // Roo.log("ADD event");
3749 // Roo.log(this.triggerEl.dom);
3750 if (this.triggerEl) {
3752 this.triggerEl.on('click', this.onTriggerClick, this);
3754 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3756 if (!this.hideTrigger) {
3757 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3758 // dropdown toggle on the 'a' in BS4?
3759 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3761 this.triggerEl.addClass('dropdown-toggle');
3767 this.el.on('touchstart' , this.onTouch, this);
3769 this.el.on('click' , this.onClick, this);
3771 this.el.on("mouseover", this.onMouseOver, this);
3772 this.el.on("mouseout", this.onMouseOut, this);
3776 findTargetItem : function(e)
3778 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3782 //Roo.log(t); Roo.log(t.id);
3784 //Roo.log(this.menuitems);
3785 return this.menuitems.get(t.id);
3787 //return this.items.get(t.menuItemId);
3793 onTouch : function(e)
3795 Roo.log("menu.onTouch");
3796 //e.stopEvent(); this make the user popdown broken
3800 onClick : function(e)
3802 Roo.log("menu.onClick");
3804 var t = this.findTargetItem(e);
3805 if(!t || t.isContainer){
3810 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3811 if(t == this.activeItem && t.shouldDeactivate(e)){
3812 this.activeItem.deactivate();
3813 delete this.activeItem;
3817 this.setActiveItem(t, true);
3825 Roo.log('pass click event');
3829 this.fireEvent("click", this, t, e);
3833 if(!t.href.length || t.href == '#'){
3834 (function() { _this.hide(); }).defer(100);
3839 onMouseOver : function(e){
3840 var t = this.findTargetItem(e);
3843 // if(t.canActivate && !t.disabled){
3844 // this.setActiveItem(t, true);
3848 this.fireEvent("mouseover", this, e, t);
3850 isVisible : function(){
3851 return !this.hidden;
3853 onMouseOut : function(e){
3854 var t = this.findTargetItem(e);
3857 // if(t == this.activeItem && t.shouldDeactivate(e)){
3858 // this.activeItem.deactivate();
3859 // delete this.activeItem;
3862 this.fireEvent("mouseout", this, e, t);
3867 * Displays this menu relative to another element
3868 * @param {String/HTMLElement/Roo.Element} element The element to align to
3869 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3870 * the element (defaults to this.defaultAlign)
3871 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3873 show : function(el, pos, parentMenu)
3875 if (false === this.fireEvent("beforeshow", this)) {
3876 Roo.log("show canceled");
3879 this.parentMenu = parentMenu;
3883 this.el.addClass('show'); // show otherwise we do not know how big we are..
3885 var xy = this.el.getAlignToXY(el, pos);
3887 // bl-tl << left align below
3888 // tl-bl << left align
3890 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3891 // if it goes to far to the right.. -> align left.
3892 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3895 // was left align - go right?
3896 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3899 // goes down the bottom
3900 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3902 var a = this.align.replace('?', '').split('-');
3903 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3907 this.showAt( xy , parentMenu, false);
3910 * Displays this menu at a specific xy position
3911 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3912 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3914 showAt : function(xy, parentMenu, /* private: */_e){
3915 this.parentMenu = parentMenu;
3920 this.fireEvent("beforeshow", this);
3921 //xy = this.el.adjustForConstraints(xy);
3925 this.hideMenuItems();
3926 this.hidden = false;
3927 if (this.triggerEl) {
3928 this.triggerEl.addClass('open');
3931 this.el.addClass('show');
3935 // reassign x when hitting right
3937 // reassign y when hitting bottom
3939 // but the list may align on trigger left or trigger top... should it be a properity?
3941 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3946 this.fireEvent("show", this);
3952 this.doFocus.defer(50, this);
3956 doFocus : function(){
3958 this.focusEl.focus();
3963 * Hides this menu and optionally all parent menus
3964 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3966 hide : function(deep)
3968 if (false === this.fireEvent("beforehide", this)) {
3969 Roo.log("hide canceled");
3972 this.hideMenuItems();
3973 if(this.el && this.isVisible()){
3975 if(this.activeItem){
3976 this.activeItem.deactivate();
3977 this.activeItem = null;
3979 if (this.triggerEl) {
3980 this.triggerEl.removeClass('open');
3983 this.el.removeClass('show');
3985 this.fireEvent("hide", this);
3987 if(deep === true && this.parentMenu){
3988 this.parentMenu.hide(true);
3992 onTriggerClick : function(e)
3994 Roo.log('trigger click');
3996 var target = e.getTarget();
3998 Roo.log(target.nodeName.toLowerCase());
4000 if(target.nodeName.toLowerCase() === 'i'){
4006 onTriggerPress : function(e)
4008 Roo.log('trigger press');
4009 //Roo.log(e.getTarget());
4010 // Roo.log(this.triggerEl.dom);
4012 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4013 var pel = Roo.get(e.getTarget());
4014 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4015 Roo.log('is treeview or dropdown?');
4019 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4023 if (this.isVisible()) {
4029 this.show(this.triggerEl, this.align, false);
4032 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039 hideMenuItems : function()
4041 Roo.log("hide Menu Items");
4046 this.el.select('.open',true).each(function(aa) {
4048 aa.removeClass('open');
4052 addxtypeChild : function (tree, cntr) {
4053 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4055 this.menuitems.add(comp);
4067 this.getEl().dom.innerHTML = '';
4068 this.menuitems.clear();
4082 * @class Roo.bootstrap.MenuItem
4083 * @extends Roo.bootstrap.Component
4084 * Bootstrap MenuItem class
4085 * @cfg {String} html the menu label
4086 * @cfg {String} href the link
4087 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4088 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4089 * @cfg {Boolean} active used on sidebars to highlight active itesm
4090 * @cfg {String} fa favicon to show on left of menu item.
4091 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4095 * Create a new MenuItem
4096 * @param {Object} config The config object
4100 Roo.bootstrap.MenuItem = function(config){
4101 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4106 * The raw click event for the entire grid.
4107 * @param {Roo.bootstrap.MenuItem} this
4108 * @param {Roo.EventObject} e
4114 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4118 preventDefault: false,
4119 isContainer : false,
4123 getAutoCreate : function(){
4125 if(this.isContainer){
4128 cls: 'dropdown-menu-item '
4138 cls : 'dropdown-item',
4143 if (this.fa !== false) {
4146 cls : 'fa fa-' + this.fa
4155 cls: 'dropdown-menu-item',
4158 if (this.parent().type == 'treeview') {
4159 cfg.cls = 'treeview-menu';
4162 cfg.cls += ' active';
4167 anc.href = this.href || cfg.cn[0].href ;
4168 ctag.html = this.html || cfg.cn[0].html ;
4172 initEvents: function()
4174 if (this.parent().type == 'treeview') {
4175 this.el.select('a').on('click', this.onClick, this);
4179 this.menu.parentType = this.xtype;
4180 this.menu.triggerEl = this.el;
4181 this.menu = this.addxtype(Roo.apply({}, this.menu));
4185 onClick : function(e)
4187 Roo.log('item on click ');
4189 if(this.preventDefault){
4192 //this.parent().hideMenuItems();
4194 this.fireEvent('click', this, e);
4213 * @class Roo.bootstrap.MenuSeparator
4214 * @extends Roo.bootstrap.Component
4215 * Bootstrap MenuSeparator class
4218 * Create a new MenuItem
4219 * @param {Object} config The config object
4223 Roo.bootstrap.MenuSeparator = function(config){
4224 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4227 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4229 getAutoCreate : function(){
4248 * @class Roo.bootstrap.Modal
4249 * @extends Roo.bootstrap.Component
4250 * Bootstrap Modal class
4251 * @cfg {String} title Title of dialog
4252 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4253 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4254 * @cfg {Boolean} specificTitle default false
4255 * @cfg {Array} buttons Array of buttons or standard button set..
4256 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4257 * @cfg {Boolean} animate default true
4258 * @cfg {Boolean} allow_close default true
4259 * @cfg {Boolean} fitwindow default false
4260 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4261 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4262 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4263 * @cfg {String} size (sm|lg|xl) default empty
4264 * @cfg {Number} max_width set the max width of modal
4265 * @cfg {Boolean} editableTitle can the title be edited
4270 * Create a new Modal Dialog
4271 * @param {Object} config The config object
4274 Roo.bootstrap.Modal = function(config){
4275 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4280 * The raw btnclick event for the button
4281 * @param {Roo.EventObject} e
4286 * Fire when dialog resize
4287 * @param {Roo.bootstrap.Modal} this
4288 * @param {Roo.EventObject} e
4292 * @event titlechanged
4293 * Fire when the editable title has been changed
4294 * @param {Roo.bootstrap.Modal} this
4295 * @param {Roo.EventObject} value
4297 "titlechanged" : true
4300 this.buttons = this.buttons || [];
4303 this.tmpl = Roo.factory(this.tmpl);
4308 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4310 title : 'test dialog',
4320 specificTitle: false,
4322 buttonPosition: 'right',
4344 editableTitle : false,
4346 onRender : function(ct, position)
4348 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4351 var cfg = Roo.apply({}, this.getAutoCreate());
4354 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4356 //if (!cfg.name.length) {
4360 cfg.cls += ' ' + this.cls;
4363 cfg.style = this.style;
4365 this.el = Roo.get(document.body).createChild(cfg, position);
4367 //var type = this.el.dom.type;
4370 if(this.tabIndex !== undefined){
4371 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4374 this.dialogEl = this.el.select('.modal-dialog',true).first();
4375 this.bodyEl = this.el.select('.modal-body',true).first();
4376 this.closeEl = this.el.select('.modal-header .close', true).first();
4377 this.headerEl = this.el.select('.modal-header',true).first();
4378 this.titleEl = this.el.select('.modal-title',true).first();
4379 this.footerEl = this.el.select('.modal-footer',true).first();
4381 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4383 //this.el.addClass("x-dlg-modal");
4385 if (this.buttons.length) {
4386 Roo.each(this.buttons, function(bb) {
4387 var b = Roo.apply({}, bb);
4388 b.xns = b.xns || Roo.bootstrap;
4389 b.xtype = b.xtype || 'Button';
4390 if (typeof(b.listeners) == 'undefined') {
4391 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4394 var btn = Roo.factory(b);
4396 btn.render(this.getButtonContainer());
4400 // render the children.
4403 if(typeof(this.items) != 'undefined'){
4404 var items = this.items;
4407 for(var i =0;i < items.length;i++) {
4408 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4412 this.items = nitems;
4414 // where are these used - they used to be body/close/footer
4418 //this.el.addClass([this.fieldClass, this.cls]);
4422 getAutoCreate : function()
4424 // we will default to modal-body-overflow - might need to remove or make optional later.
4426 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4427 html : this.html || ''
4432 cls : 'modal-title',
4436 if(this.specificTitle){ // WTF is this?
4441 if (this.allow_close && Roo.bootstrap.version == 3) {
4451 if (this.editableTitle) {
4453 cls: 'form-control roo-editable-title d-none',
4459 if (this.allow_close && Roo.bootstrap.version == 4) {
4469 if(this.size.length){
4470 size = 'modal-' + this.size;
4473 var footer = Roo.bootstrap.version == 3 ?
4475 cls : 'modal-footer',
4479 cls: 'btn-' + this.buttonPosition
4484 { // BS4 uses mr-auto on left buttons....
4485 cls : 'modal-footer'
4496 cls: "modal-dialog " + size,
4499 cls : "modal-content",
4502 cls : 'modal-header',
4517 modal.cls += ' fade';
4523 getChildContainer : function() {
4528 getButtonContainer : function() {
4530 return Roo.bootstrap.version == 4 ?
4531 this.el.select('.modal-footer',true).first()
4532 : this.el.select('.modal-footer div',true).first();
4535 initEvents : function()
4537 if (this.allow_close) {
4538 this.closeEl.on('click', this.hide, this);
4540 Roo.EventManager.onWindowResize(this.resize, this, true);
4541 if (this.editableTitle) {
4542 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4543 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4544 this.headerEditEl.on('keyup', function(e) {
4545 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4546 this.toggleHeaderInput(false)
4549 this.headerEditEl.on('blur', function(e) {
4550 this.toggleHeaderInput(false)
4559 this.maskEl.setSize(
4560 Roo.lib.Dom.getViewWidth(true),
4561 Roo.lib.Dom.getViewHeight(true)
4564 if (this.fitwindow) {
4566 this.dialogEl.setStyle( { 'max-width' : '100%' });
4568 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4569 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4574 if(this.max_width !== 0) {
4576 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4579 this.setSize(w, this.height);
4583 if(this.max_height) {
4584 this.setSize(w,Math.min(
4586 Roo.lib.Dom.getViewportHeight(true) - 60
4592 if(!this.fit_content) {
4593 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4597 this.setSize(w, Math.min(
4599 this.headerEl.getHeight() +
4600 this.footerEl.getHeight() +
4601 this.getChildHeight(this.bodyEl.dom.childNodes),
4602 Roo.lib.Dom.getViewportHeight(true) - 60)
4608 setSize : function(w,h)
4619 if (!this.rendered) {
4622 this.toggleHeaderInput(false);
4623 //this.el.setStyle('display', 'block');
4624 this.el.removeClass('hideing');
4625 this.el.dom.style.display='block';
4627 Roo.get(document.body).addClass('modal-open');
4629 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4632 this.el.addClass('show');
4633 this.el.addClass('in');
4636 this.el.addClass('show');
4637 this.el.addClass('in');
4640 // not sure how we can show data in here..
4642 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4645 Roo.get(document.body).addClass("x-body-masked");
4647 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4648 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4649 this.maskEl.dom.style.display = 'block';
4650 this.maskEl.addClass('show');
4655 this.fireEvent('show', this);
4657 // set zindex here - otherwise it appears to be ignored...
4658 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4661 this.items.forEach( function(e) {
4662 e.layout ? e.layout() : false;
4670 if(this.fireEvent("beforehide", this) !== false){
4672 this.maskEl.removeClass('show');
4674 this.maskEl.dom.style.display = '';
4675 Roo.get(document.body).removeClass("x-body-masked");
4676 this.el.removeClass('in');
4677 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4679 if(this.animate){ // why
4680 this.el.addClass('hideing');
4681 this.el.removeClass('show');
4683 if (!this.el.hasClass('hideing')) {
4684 return; // it's been shown again...
4687 this.el.dom.style.display='';
4689 Roo.get(document.body).removeClass('modal-open');
4690 this.el.removeClass('hideing');
4694 this.el.removeClass('show');
4695 this.el.dom.style.display='';
4696 Roo.get(document.body).removeClass('modal-open');
4699 this.fireEvent('hide', this);
4702 isVisible : function()
4705 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4709 addButton : function(str, cb)
4713 var b = Roo.apply({}, { html : str } );
4714 b.xns = b.xns || Roo.bootstrap;
4715 b.xtype = b.xtype || 'Button';
4716 if (typeof(b.listeners) == 'undefined') {
4717 b.listeners = { click : cb.createDelegate(this) };
4720 var btn = Roo.factory(b);
4722 btn.render(this.getButtonContainer());
4728 setDefaultButton : function(btn)
4730 //this.el.select('.modal-footer').()
4733 resizeTo: function(w,h)
4735 this.dialogEl.setWidth(w);
4737 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4739 this.bodyEl.setHeight(h - diff);
4741 this.fireEvent('resize', this);
4744 setContentSize : function(w, h)
4748 onButtonClick: function(btn,e)
4751 this.fireEvent('btnclick', btn.name, e);
4754 * Set the title of the Dialog
4755 * @param {String} str new Title
4757 setTitle: function(str) {
4758 this.titleEl.dom.innerHTML = str;
4762 * Set the body of the Dialog
4763 * @param {String} str new Title
4765 setBody: function(str) {
4766 this.bodyEl.dom.innerHTML = str;
4769 * Set the body of the Dialog using the template
4770 * @param {Obj} data - apply this data to the template and replace the body contents.
4772 applyBody: function(obj)
4775 Roo.log("Error - using apply Body without a template");
4778 this.tmpl.overwrite(this.bodyEl, obj);
4781 getChildHeight : function(child_nodes)
4785 child_nodes.length == 0
4790 var child_height = 0;
4792 for(var i = 0; i < child_nodes.length; i++) {
4795 * for modal with tabs...
4796 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4798 var layout_childs = child_nodes[i].childNodes;
4800 for(var j = 0; j < layout_childs.length; j++) {
4802 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4804 var layout_body_childs = layout_childs[j].childNodes;
4806 for(var k = 0; k < layout_body_childs.length; k++) {
4808 if(layout_body_childs[k].classList.contains('navbar')) {
4809 child_height += layout_body_childs[k].offsetHeight;
4813 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4815 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4817 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4819 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4820 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4835 child_height += child_nodes[i].offsetHeight;
4836 // Roo.log(child_nodes[i].offsetHeight);
4839 return child_height;
4841 toggleHeaderInput : function(is_edit)
4843 if (!this.editableTitle) {
4844 return; // not editable.
4846 if (is_edit && this.is_header_editing) {
4847 return; // already editing..
4851 this.headerEditEl.dom.value = this.title;
4852 this.headerEditEl.removeClass('d-none');
4853 this.headerEditEl.dom.focus();
4854 this.titleEl.addClass('d-none');
4856 this.is_header_editing = true;
4859 // flip back to not editing.
4860 this.title = this.headerEditEl.dom.value;
4861 this.headerEditEl.addClass('d-none');
4862 this.titleEl.removeClass('d-none');
4863 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4864 this.is_header_editing = false;
4865 this.fireEvent('titlechanged', this, this.title);
4874 Roo.apply(Roo.bootstrap.Modal, {
4876 * Button config that displays a single OK button
4885 * Button config that displays Yes and No buttons
4901 * Button config that displays OK and Cancel buttons
4916 * Button config that displays Yes, No and Cancel buttons
4941 * messagebox - can be used as a replace
4945 * @class Roo.MessageBox
4946 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4950 Roo.Msg.alert('Status', 'Changes saved successfully.');
4952 // Prompt for user data:
4953 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4955 // process text value...
4959 // Show a dialog using config options:
4961 title:'Save Changes?',
4962 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4963 buttons: Roo.Msg.YESNOCANCEL,
4970 Roo.bootstrap.MessageBox = function(){
4971 var dlg, opt, mask, waitTimer;
4972 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4973 var buttons, activeTextEl, bwidth;
4977 var handleButton = function(button){
4979 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4983 var handleHide = function(){
4985 dlg.el.removeClass(opt.cls);
4988 // Roo.TaskMgr.stop(waitTimer);
4989 // waitTimer = null;
4994 var updateButtons = function(b){
4997 buttons["ok"].hide();
4998 buttons["cancel"].hide();
4999 buttons["yes"].hide();
5000 buttons["no"].hide();
5001 dlg.footerEl.hide();
5005 dlg.footerEl.show();
5006 for(var k in buttons){
5007 if(typeof buttons[k] != "function"){
5010 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5011 width += buttons[k].el.getWidth()+15;
5021 var handleEsc = function(d, k, e){
5022 if(opt && opt.closable !== false){
5032 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5033 * @return {Roo.BasicDialog} The BasicDialog element
5035 getDialog : function(){
5037 dlg = new Roo.bootstrap.Modal( {
5040 //constraintoviewport:false,
5042 //collapsible : false,
5047 //buttonAlign:"center",
5048 closeClick : function(){
5049 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5052 handleButton("cancel");
5057 dlg.on("hide", handleHide);
5059 //dlg.addKeyListener(27, handleEsc);
5061 this.buttons = buttons;
5062 var bt = this.buttonText;
5063 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5064 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5065 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5066 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5068 bodyEl = dlg.bodyEl.createChild({
5070 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5071 '<textarea class="roo-mb-textarea"></textarea>' +
5072 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5074 msgEl = bodyEl.dom.firstChild;
5075 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5076 textboxEl.enableDisplayMode();
5077 textboxEl.addKeyListener([10,13], function(){
5078 if(dlg.isVisible() && opt && opt.buttons){
5081 }else if(opt.buttons.yes){
5082 handleButton("yes");
5086 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5087 textareaEl.enableDisplayMode();
5088 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5089 progressEl.enableDisplayMode();
5091 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5092 var pf = progressEl.dom.firstChild;
5094 pp = Roo.get(pf.firstChild);
5095 pp.setHeight(pf.offsetHeight);
5103 * Updates the message box body text
5104 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5105 * the XHTML-compliant non-breaking space character '&#160;')
5106 * @return {Roo.MessageBox} This message box
5108 updateText : function(text)
5110 if(!dlg.isVisible() && !opt.width){
5111 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5112 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5114 msgEl.innerHTML = text || ' ';
5116 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5117 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5119 Math.min(opt.width || cw , this.maxWidth),
5120 Math.max(opt.minWidth || this.minWidth, bwidth)
5123 activeTextEl.setWidth(w);
5125 if(dlg.isVisible()){
5126 dlg.fixedcenter = false;
5128 // to big, make it scroll. = But as usual stupid IE does not support
5131 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5132 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5133 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5135 bodyEl.dom.style.height = '';
5136 bodyEl.dom.style.overflowY = '';
5139 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5141 bodyEl.dom.style.overflowX = '';
5144 dlg.setContentSize(w, bodyEl.getHeight());
5145 if(dlg.isVisible()){
5146 dlg.fixedcenter = true;
5152 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5153 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5154 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5155 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5156 * @return {Roo.MessageBox} This message box
5158 updateProgress : function(value, text){
5160 this.updateText(text);
5163 if (pp) { // weird bug on my firefox - for some reason this is not defined
5164 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5165 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171 * Returns true if the message box is currently displayed
5172 * @return {Boolean} True if the message box is visible, else false
5174 isVisible : function(){
5175 return dlg && dlg.isVisible();
5179 * Hides the message box if it is displayed
5182 if(this.isVisible()){
5188 * Displays a new message box, or reinitializes an existing message box, based on the config options
5189 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5190 * The following config object properties are supported:
5192 Property Type Description
5193 ---------- --------------- ------------------------------------------------------------------------------------
5194 animEl String/Element An id or Element from which the message box should animate as it opens and
5195 closes (defaults to undefined)
5196 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5197 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5198 closable Boolean False to hide the top-right close button (defaults to true). Note that
5199 progress and wait dialogs will ignore this property and always hide the
5200 close button as they can only be closed programmatically.
5201 cls String A custom CSS class to apply to the message box element
5202 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5203 displayed (defaults to 75)
5204 fn Function A callback function to execute after closing the dialog. The arguments to the
5205 function will be btn (the name of the button that was clicked, if applicable,
5206 e.g. "ok"), and text (the value of the active text field, if applicable).
5207 Progress and wait dialogs will ignore this option since they do not respond to
5208 user actions and can only be closed programmatically, so any required function
5209 should be called by the same code after it closes the dialog.
5210 icon String A CSS class that provides a background image to be used as an icon for
5211 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5212 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5213 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5214 modal Boolean False to allow user interaction with the page while the message box is
5215 displayed (defaults to true)
5216 msg String A string that will replace the existing message box body text (defaults
5217 to the XHTML-compliant non-breaking space character ' ')
5218 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5219 progress Boolean True to display a progress bar (defaults to false)
5220 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5221 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5222 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5223 title String The title text
5224 value String The string value to set into the active textbox element if displayed
5225 wait Boolean True to display a progress bar (defaults to false)
5226 width Number The width of the dialog in pixels
5233 msg: 'Please enter your address:',
5235 buttons: Roo.MessageBox.OKCANCEL,
5238 animEl: 'addAddressBtn'
5241 * @param {Object} config Configuration options
5242 * @return {Roo.MessageBox} This message box
5244 show : function(options)
5247 // this causes nightmares if you show one dialog after another
5248 // especially on callbacks..
5250 if(this.isVisible()){
5253 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5254 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5255 Roo.log("New Dialog Message:" + options.msg )
5256 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5257 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5260 var d = this.getDialog();
5262 d.setTitle(opt.title || " ");
5263 d.closeEl.setDisplayed(opt.closable !== false);
5264 activeTextEl = textboxEl;
5265 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5270 textareaEl.setHeight(typeof opt.multiline == "number" ?
5271 opt.multiline : this.defaultTextHeight);
5272 activeTextEl = textareaEl;
5281 progressEl.setDisplayed(opt.progress === true);
5283 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5285 this.updateProgress(0);
5286 activeTextEl.dom.value = opt.value || "";
5288 dlg.setDefaultButton(activeTextEl);
5290 var bs = opt.buttons;
5294 }else if(bs && bs.yes){
5295 db = buttons["yes"];
5297 dlg.setDefaultButton(db);
5299 bwidth = updateButtons(opt.buttons);
5300 this.updateText(opt.msg);
5302 d.el.addClass(opt.cls);
5304 d.proxyDrag = opt.proxyDrag === true;
5305 d.modal = opt.modal !== false;
5306 d.mask = opt.modal !== false ? mask : false;
5308 // force it to the end of the z-index stack so it gets a cursor in FF
5309 document.body.appendChild(dlg.el.dom);
5310 d.animateTarget = null;
5311 d.show(options.animEl);
5317 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5318 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5319 * and closing the message box when the process is complete.
5320 * @param {String} title The title bar text
5321 * @param {String} msg The message box body text
5322 * @return {Roo.MessageBox} This message box
5324 progress : function(title, msg){
5331 minWidth: this.minProgressWidth,
5338 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5339 * If a callback function is passed it will be called after the user clicks the button, and the
5340 * id of the button that was clicked will be passed as the only parameter to the callback
5341 * (could also be the top-right close button).
5342 * @param {String} title The title bar text
5343 * @param {String} msg The message box body text
5344 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5345 * @param {Object} scope (optional) The scope of the callback function
5346 * @return {Roo.MessageBox} This message box
5348 alert : function(title, msg, fn, scope)
5363 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5364 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5365 * You are responsible for closing the message box when the process is complete.
5366 * @param {String} msg The message box body text
5367 * @param {String} title (optional) The title bar text
5368 * @return {Roo.MessageBox} This message box
5370 wait : function(msg, title){
5381 waitTimer = Roo.TaskMgr.start({
5383 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5391 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5392 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5393 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5394 * @param {String} title The title bar text
5395 * @param {String} msg The message box body text
5396 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5397 * @param {Object} scope (optional) The scope of the callback function
5398 * @return {Roo.MessageBox} This message box
5400 confirm : function(title, msg, fn, scope){
5404 buttons: this.YESNO,
5413 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5414 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5415 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5416 * (could also be the top-right close button) and the text that was entered will be passed as the two
5417 * parameters to the callback.
5418 * @param {String} title The title bar text
5419 * @param {String} msg The message box body text
5420 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5421 * @param {Object} scope (optional) The scope of the callback function
5422 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5423 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5424 * @return {Roo.MessageBox} This message box
5426 prompt : function(title, msg, fn, scope, multiline){
5430 buttons: this.OKCANCEL,
5435 multiline: multiline,
5442 * Button config that displays a single OK button
5447 * Button config that displays Yes and No buttons
5450 YESNO : {yes:true, no:true},
5452 * Button config that displays OK and Cancel buttons
5455 OKCANCEL : {ok:true, cancel:true},
5457 * Button config that displays Yes, No and Cancel buttons
5460 YESNOCANCEL : {yes:true, no:true, cancel:true},
5463 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5466 defaultTextHeight : 75,
5468 * The maximum width in pixels of the message box (defaults to 600)
5473 * The minimum width in pixels of the message box (defaults to 100)
5478 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5479 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5482 minProgressWidth : 250,
5484 * An object containing the default button text strings that can be overriden for localized language support.
5485 * Supported properties are: ok, cancel, yes and no.
5486 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5499 * Shorthand for {@link Roo.MessageBox}
5501 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5502 Roo.Msg = Roo.Msg || Roo.MessageBox;
5511 * @class Roo.bootstrap.Navbar
5512 * @extends Roo.bootstrap.Component
5513 * Bootstrap Navbar class
5516 * Create a new Navbar
5517 * @param {Object} config The config object
5521 Roo.bootstrap.Navbar = function(config){
5522 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5526 * @event beforetoggle
5527 * Fire before toggle the menu
5528 * @param {Roo.EventObject} e
5530 "beforetoggle" : true
5534 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5543 getAutoCreate : function(){
5546 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5550 initEvents :function ()
5552 //Roo.log(this.el.select('.navbar-toggle',true));
5553 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5562 var size = this.el.getSize();
5563 this.maskEl.setSize(size.width, size.height);
5564 this.maskEl.enableDisplayMode("block");
5573 getChildContainer : function()
5575 if (this.el && this.el.select('.collapse').getCount()) {
5576 return this.el.select('.collapse',true).first();
5591 onToggle : function()
5594 if(this.fireEvent('beforetoggle', this) === false){
5597 var ce = this.el.select('.navbar-collapse',true).first();
5599 if (!ce.hasClass('show')) {
5609 * Expand the navbar pulldown
5611 expand : function ()
5614 var ce = this.el.select('.navbar-collapse',true).first();
5615 if (ce.hasClass('collapsing')) {
5618 ce.dom.style.height = '';
5620 ce.addClass('in'); // old...
5621 ce.removeClass('collapse');
5622 ce.addClass('show');
5623 var h = ce.getHeight();
5625 ce.removeClass('show');
5626 // at this point we should be able to see it..
5627 ce.addClass('collapsing');
5629 ce.setHeight(0); // resize it ...
5630 ce.on('transitionend', function() {
5631 //Roo.log('done transition');
5632 ce.removeClass('collapsing');
5633 ce.addClass('show');
5634 ce.removeClass('collapse');
5636 ce.dom.style.height = '';
5637 }, this, { single: true} );
5639 ce.dom.scrollTop = 0;
5642 * Collapse the navbar pulldown
5644 collapse : function()
5646 var ce = this.el.select('.navbar-collapse',true).first();
5648 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5649 // it's collapsed or collapsing..
5652 ce.removeClass('in'); // old...
5653 ce.setHeight(ce.getHeight());
5654 ce.removeClass('show');
5655 ce.addClass('collapsing');
5657 ce.on('transitionend', function() {
5658 ce.dom.style.height = '';
5659 ce.removeClass('collapsing');
5660 ce.addClass('collapse');
5661 }, this, { single: true} );
5681 * @class Roo.bootstrap.NavSimplebar
5682 * @extends Roo.bootstrap.Navbar
5683 * Bootstrap Sidebar class
5685 * @cfg {Boolean} inverse is inverted color
5687 * @cfg {String} type (nav | pills | tabs)
5688 * @cfg {Boolean} arrangement stacked | justified
5689 * @cfg {String} align (left | right) alignment
5691 * @cfg {Boolean} main (true|false) main nav bar? default false
5692 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5694 * @cfg {String} tag (header|footer|nav|div) default is nav
5696 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5700 * Create a new Sidebar
5701 * @param {Object} config The config object
5705 Roo.bootstrap.NavSimplebar = function(config){
5706 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5709 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5725 getAutoCreate : function(){
5729 tag : this.tag || 'div',
5730 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5732 if (['light','white'].indexOf(this.weight) > -1) {
5733 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5735 cfg.cls += ' bg-' + this.weight;
5738 cfg.cls += ' navbar-inverse';
5742 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5744 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5753 cls: 'nav nav-' + this.xtype,
5759 this.type = this.type || 'nav';
5760 if (['tabs','pills'].indexOf(this.type) != -1) {
5761 cfg.cn[0].cls += ' nav-' + this.type
5765 if (this.type!=='nav') {
5766 Roo.log('nav type must be nav/tabs/pills')
5768 cfg.cn[0].cls += ' navbar-nav'
5774 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5775 cfg.cn[0].cls += ' nav-' + this.arrangement;
5779 if (this.align === 'right') {
5780 cfg.cn[0].cls += ' navbar-right';
5805 * navbar-expand-md fixed-top
5809 * @class Roo.bootstrap.NavHeaderbar
5810 * @extends Roo.bootstrap.NavSimplebar
5811 * Bootstrap Sidebar class
5813 * @cfg {String} brand what is brand
5814 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5815 * @cfg {String} brand_href href of the brand
5816 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5817 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5818 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5819 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5822 * Create a new Sidebar
5823 * @param {Object} config The config object
5827 Roo.bootstrap.NavHeaderbar = function(config){
5828 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5832 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5839 desktopCenter : false,
5842 getAutoCreate : function(){
5845 tag: this.nav || 'nav',
5846 cls: 'navbar navbar-expand-md',
5852 if (this.desktopCenter) {
5853 cn.push({cls : 'container', cn : []});
5861 cls: 'navbar-toggle navbar-toggler',
5862 'data-toggle': 'collapse',
5867 html: 'Toggle navigation'
5871 cls: 'icon-bar navbar-toggler-icon'
5884 cn.push( Roo.bootstrap.version == 4 ? btn : {
5886 cls: 'navbar-header',
5895 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5899 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5901 if (['light','white'].indexOf(this.weight) > -1) {
5902 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5904 cfg.cls += ' bg-' + this.weight;
5907 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5908 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5910 // tag can override this..
5912 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5915 if (this.brand !== '') {
5916 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5917 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5919 href: this.brand_href ? this.brand_href : '#',
5920 cls: 'navbar-brand',
5928 cfg.cls += ' main-nav';
5936 getHeaderChildContainer : function()
5938 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5939 return this.el.select('.navbar-header',true).first();
5942 return this.getChildContainer();
5945 getChildContainer : function()
5948 return this.el.select('.roo-navbar-collapse',true).first();
5953 initEvents : function()
5955 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5957 if (this.autohide) {
5962 Roo.get(document).on('scroll',function(e) {
5963 var ns = Roo.get(document).getScroll().top;
5964 var os = prevScroll;
5968 ft.removeClass('slideDown');
5969 ft.addClass('slideUp');
5972 ft.removeClass('slideUp');
5973 ft.addClass('slideDown');
5994 * @class Roo.bootstrap.NavSidebar
5995 * @extends Roo.bootstrap.Navbar
5996 * Bootstrap Sidebar class
5999 * Create a new Sidebar
6000 * @param {Object} config The config object
6004 Roo.bootstrap.NavSidebar = function(config){
6005 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6008 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6010 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6012 getAutoCreate : function(){
6017 cls: 'sidebar sidebar-nav'
6039 * @class Roo.bootstrap.NavGroup
6040 * @extends Roo.bootstrap.Component
6041 * Bootstrap NavGroup class
6042 * @cfg {String} align (left|right)
6043 * @cfg {Boolean} inverse
6044 * @cfg {String} type (nav|pills|tab) default nav
6045 * @cfg {String} navId - reference Id for navbar.
6046 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6049 * Create a new nav group
6050 * @param {Object} config The config object
6053 Roo.bootstrap.NavGroup = function(config){
6054 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6057 Roo.bootstrap.NavGroup.register(this);
6061 * Fires when the active item changes
6062 * @param {Roo.bootstrap.NavGroup} this
6063 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6064 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6071 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6083 getAutoCreate : function()
6085 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091 if (Roo.bootstrap.version == 4) {
6092 if (['tabs','pills'].indexOf(this.type) != -1) {
6093 cfg.cls += ' nav-' + this.type;
6095 // trying to remove so header bar can right align top?
6096 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6097 // do not use on header bar...
6098 cfg.cls += ' navbar-nav';
6103 if (['tabs','pills'].indexOf(this.type) != -1) {
6104 cfg.cls += ' nav-' + this.type
6106 if (this.type !== 'nav') {
6107 Roo.log('nav type must be nav/tabs/pills')
6109 cfg.cls += ' navbar-nav'
6113 if (this.parent() && this.parent().sidebar) {
6116 cls: 'dashboard-menu sidebar-menu'
6122 if (this.form === true) {
6125 cls: 'navbar-form form-inline'
6127 //nav navbar-right ml-md-auto
6128 if (this.align === 'right') {
6129 cfg.cls += ' navbar-right ml-md-auto';
6131 cfg.cls += ' navbar-left';
6135 if (this.align === 'right') {
6136 cfg.cls += ' navbar-right ml-md-auto';
6138 cfg.cls += ' mr-auto';
6142 cfg.cls += ' navbar-inverse';
6150 * sets the active Navigation item
6151 * @param {Roo.bootstrap.NavItem} the new current navitem
6153 setActiveItem : function(item)
6156 Roo.each(this.navItems, function(v){
6161 v.setActive(false, true);
6168 item.setActive(true, true);
6169 this.fireEvent('changed', this, item, prev);
6174 * gets the active Navigation item
6175 * @return {Roo.bootstrap.NavItem} the current navitem
6177 getActive : function()
6181 Roo.each(this.navItems, function(v){
6192 indexOfNav : function()
6196 Roo.each(this.navItems, function(v,i){
6207 * adds a Navigation item
6208 * @param {Roo.bootstrap.NavItem} the navitem to add
6210 addItem : function(cfg)
6212 if (this.form && Roo.bootstrap.version == 4) {
6215 var cn = new Roo.bootstrap.NavItem(cfg);
6217 cn.parentId = this.id;
6218 cn.onRender(this.el, null);
6222 * register a Navigation item
6223 * @param {Roo.bootstrap.NavItem} the navitem to add
6225 register : function(item)
6227 this.navItems.push( item);
6228 item.navId = this.navId;
6233 * clear all the Navigation item
6236 clearAll : function()
6239 this.el.dom.innerHTML = '';
6242 getNavItem: function(tabId)
6245 Roo.each(this.navItems, function(e) {
6246 if (e.tabId == tabId) {
6256 setActiveNext : function()
6258 var i = this.indexOfNav(this.getActive());
6259 if (i > this.navItems.length) {
6262 this.setActiveItem(this.navItems[i+1]);
6264 setActivePrev : function()
6266 var i = this.indexOfNav(this.getActive());
6270 this.setActiveItem(this.navItems[i-1]);
6272 clearWasActive : function(except) {
6273 Roo.each(this.navItems, function(e) {
6274 if (e.tabId != except.tabId && e.was_active) {
6275 e.was_active = false;
6282 getWasActive : function ()
6285 Roo.each(this.navItems, function(e) {
6300 Roo.apply(Roo.bootstrap.NavGroup, {
6304 * register a Navigation Group
6305 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6307 register : function(navgrp)
6309 this.groups[navgrp.navId] = navgrp;
6313 * fetch a Navigation Group based on the navigation ID
6314 * @param {string} the navgroup to add
6315 * @returns {Roo.bootstrap.NavGroup} the navgroup
6317 get: function(navId) {
6318 if (typeof(this.groups[navId]) == 'undefined') {
6320 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6322 return this.groups[navId] ;
6337 * @class Roo.bootstrap.NavItem
6338 * @extends Roo.bootstrap.Component
6339 * Bootstrap Navbar.NavItem class
6340 * @cfg {String} href link to
6341 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6342 * @cfg {Boolean} button_outline show and outlined button
6343 * @cfg {String} html content of button
6344 * @cfg {String} badge text inside badge
6345 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6346 * @cfg {String} glyphicon DEPRICATED - use fa
6347 * @cfg {String} icon DEPRICATED - use fa
6348 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6349 * @cfg {Boolean} active Is item active
6350 * @cfg {Boolean} disabled Is item disabled
6351 * @cfg {String} linkcls Link Class
6352 * @cfg {Boolean} preventDefault (true | false) default false
6353 * @cfg {String} tabId the tab that this item activates.
6354 * @cfg {String} tagtype (a|span) render as a href or span?
6355 * @cfg {Boolean} animateRef (true|false) link to element default false
6358 * Create a new Navbar Item
6359 * @param {Object} config The config object
6361 Roo.bootstrap.NavItem = function(config){
6362 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6367 * The raw click event for the entire grid.
6368 * @param {Roo.EventObject} e
6373 * Fires when the active item active state changes
6374 * @param {Roo.bootstrap.NavItem} this
6375 * @param {boolean} state the new state
6381 * Fires when scroll to element
6382 * @param {Roo.bootstrap.NavItem} this
6383 * @param {Object} options
6384 * @param {Roo.EventObject} e
6392 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6401 preventDefault : false,
6409 button_outline : false,
6413 getAutoCreate : function(){
6420 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6423 cfg.cls += ' active' ;
6425 if (this.disabled) {
6426 cfg.cls += ' disabled';
6430 if (this.button_weight.length) {
6431 cfg.tag = this.href ? 'a' : 'button';
6432 cfg.html = this.html || '';
6433 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6435 cfg.href = this.href;
6438 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6440 cfg.cls += " nav-html";
6443 // menu .. should add dropdown-menu class - so no need for carat..
6445 if (this.badge !== '') {
6447 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6452 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6456 href : this.href || "#",
6457 html: this.html || '',
6461 if (this.tagtype == 'a') {
6462 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6466 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6467 } else if (this.fa) {
6468 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6469 } else if(this.glyphicon) {
6470 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6472 cfg.cn[0].cls += " nav-html";
6476 cfg.cn[0].html += " <span class='caret'></span>";
6480 if (this.badge !== '') {
6481 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6489 onRender : function(ct, position)
6491 // Roo.log("Call onRender: " + this.xtype);
6492 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6496 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6497 this.navLink = this.el.select('.nav-link',true).first();
6498 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6503 initEvents: function()
6505 if (typeof (this.menu) != 'undefined') {
6506 this.menu.parentType = this.xtype;
6507 this.menu.triggerEl = this.el;
6508 this.menu = this.addxtype(Roo.apply({}, this.menu));
6511 this.el.on('click', this.onClick, this);
6513 //if(this.tagtype == 'span'){
6514 // this.el.select('span',true).on('click', this.onClick, this);
6517 // at this point parent should be available..
6518 this.parent().register(this);
6521 onClick : function(e)
6523 if (e.getTarget('.dropdown-menu-item')) {
6524 // did you click on a menu itemm.... - then don't trigger onclick..
6529 this.preventDefault ||
6532 Roo.log("NavItem - prevent Default?");
6536 if (this.disabled) {
6540 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6541 if (tg && tg.transition) {
6542 Roo.log("waiting for the transitionend");
6548 //Roo.log("fire event clicked");
6549 if(this.fireEvent('click', this, e) === false){
6553 if(this.tagtype == 'span'){
6557 //Roo.log(this.href);
6558 var ael = this.el.select('a',true).first();
6561 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6562 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6563 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6564 return; // ignore... - it's a 'hash' to another page.
6566 Roo.log("NavItem - prevent Default?");
6568 this.scrollToElement(e);
6572 var p = this.parent();
6574 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6575 if (typeof(p.setActiveItem) !== 'undefined') {
6576 p.setActiveItem(this);
6580 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6581 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6582 // remove the collapsed menu expand...
6583 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6587 isActive: function () {
6590 setActive : function(state, fire, is_was_active)
6592 if (this.active && !state && this.navId) {
6593 this.was_active = true;
6594 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6596 nv.clearWasActive(this);
6600 this.active = state;
6603 this.el.removeClass('active');
6604 this.navLink ? this.navLink.removeClass('active') : false;
6605 } else if (!this.el.hasClass('active')) {
6607 this.el.addClass('active');
6608 if (Roo.bootstrap.version == 4 && this.navLink ) {
6609 this.navLink.addClass('active');
6614 this.fireEvent('changed', this, state);
6617 // show a panel if it's registered and related..
6619 if (!this.navId || !this.tabId || !state || is_was_active) {
6623 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6627 var pan = tg.getPanelByName(this.tabId);
6631 // if we can not flip to new panel - go back to old nav highlight..
6632 if (false == tg.showPanel(pan)) {
6633 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6635 var onav = nv.getWasActive();
6637 onav.setActive(true, false, true);
6646 // this should not be here...
6647 setDisabled : function(state)
6649 this.disabled = state;
6651 this.el.removeClass('disabled');
6652 } else if (!this.el.hasClass('disabled')) {
6653 this.el.addClass('disabled');
6659 * Fetch the element to display the tooltip on.
6660 * @return {Roo.Element} defaults to this.el
6662 tooltipEl : function()
6664 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6667 scrollToElement : function(e)
6669 var c = document.body;
6672 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6674 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6675 c = document.documentElement;
6678 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684 var o = target.calcOffsetsTo(c);
6691 this.fireEvent('scrollto', this, options, e);
6693 Roo.get(c).scrollTo('top', options.value, true);
6698 * Set the HTML (text content) of the item
6699 * @param {string} html content for the nav item
6701 setHtml : function(html)
6704 this.htmlEl.dom.innerHTML = html;
6716 * <span> icon </span>
6717 * <span> text </span>
6718 * <span>badge </span>
6722 * @class Roo.bootstrap.NavSidebarItem
6723 * @extends Roo.bootstrap.NavItem
6724 * Bootstrap Navbar.NavSidebarItem class
6725 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6726 * {Boolean} open is the menu open
6727 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6728 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6729 * {String} buttonSize (sm|md|lg)the extra classes for the button
6730 * {Boolean} showArrow show arrow next to the text (default true)
6732 * Create a new Navbar Button
6733 * @param {Object} config The config object
6735 Roo.bootstrap.NavSidebarItem = function(config){
6736 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6741 * The raw click event for the entire grid.
6742 * @param {Roo.EventObject} e
6747 * Fires when the active item active state changes
6748 * @param {Roo.bootstrap.NavSidebarItem} this
6749 * @param {boolean} state the new state
6757 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6759 badgeWeight : 'default',
6765 buttonWeight : 'default',
6771 getAutoCreate : function(){
6776 href : this.href || '#',
6782 if(this.buttonView){
6785 href : this.href || '#',
6786 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6799 cfg.cls += ' active';
6802 if (this.disabled) {
6803 cfg.cls += ' disabled';
6806 cfg.cls += ' open x-open';
6809 if (this.glyphicon || this.icon) {
6810 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6811 a.cn.push({ tag : 'i', cls : c }) ;
6814 if(!this.buttonView){
6817 html : this.html || ''
6824 if (this.badge !== '') {
6825 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6831 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6834 a.cls += ' dropdown-toggle treeview' ;
6840 initEvents : function()
6842 if (typeof (this.menu) != 'undefined') {
6843 this.menu.parentType = this.xtype;
6844 this.menu.triggerEl = this.el;
6845 this.menu = this.addxtype(Roo.apply({}, this.menu));
6848 this.el.on('click', this.onClick, this);
6850 if(this.badge !== ''){
6851 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6856 onClick : function(e)
6863 if(this.preventDefault){
6867 this.fireEvent('click', this, e);
6870 disable : function()
6872 this.setDisabled(true);
6877 this.setDisabled(false);
6880 setDisabled : function(state)
6882 if(this.disabled == state){
6886 this.disabled = state;
6889 this.el.addClass('disabled');
6893 this.el.removeClass('disabled');
6898 setActive : function(state)
6900 if(this.active == state){
6904 this.active = state;
6907 this.el.addClass('active');
6911 this.el.removeClass('active');
6916 isActive: function ()
6921 setBadge : function(str)
6927 this.badgeEl.dom.innerHTML = str;
6942 Roo.namespace('Roo.bootstrap.breadcrumb');
6946 * @class Roo.bootstrap.breadcrumb.Nav
6947 * @extends Roo.bootstrap.Component
6948 * Bootstrap Breadcrumb Nav Class
6950 * @children Roo.bootstrap.breadcrumb.Item
6953 * Create a new breadcrumb.Nav
6954 * @param {Object} config The config object
6958 Roo.bootstrap.breadcrumb.Nav = function(config){
6959 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6964 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6966 getAutoCreate : function()
6983 initEvents: function()
6985 this.olEl = this.el.select('ol',true).first();
6987 getChildContainer : function()
7003 * @class Roo.bootstrap.breadcrumb.Nav
7004 * @extends Roo.bootstrap.Component
7005 * Bootstrap Breadcrumb Nav Class
7007 * @children Roo.bootstrap.breadcrumb.Component
7008 * @cfg {String} html the content of the link.
7009 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7010 * @cfg {Boolean} active is it active
7014 * Create a new breadcrumb.Nav
7015 * @param {Object} config The config object
7018 Roo.bootstrap.breadcrumb.Item = function(config){
7019 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7024 * The img click event for the img.
7025 * @param {Roo.EventObject} e
7032 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7037 getAutoCreate : function()
7042 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7044 if (this.href !== false) {
7051 cfg.html = this.html;
7057 initEvents: function()
7060 this.el.select('a', true).first().on('click',this.onClick, this)
7064 onClick : function(e)
7067 this.fireEvent('click',this, e);
7080 * @class Roo.bootstrap.Row
7081 * @extends Roo.bootstrap.Component
7082 * Bootstrap Row class (contains columns...)
7086 * @param {Object} config The config object
7089 Roo.bootstrap.Row = function(config){
7090 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7093 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7095 getAutoCreate : function(){
7114 * @class Roo.bootstrap.Pagination
7115 * @extends Roo.bootstrap.Component
7116 * Bootstrap Pagination class
7117 * @cfg {String} size xs | sm | md | lg
7118 * @cfg {Boolean} inverse false | true
7121 * Create a new Pagination
7122 * @param {Object} config The config object
7125 Roo.bootstrap.Pagination = function(config){
7126 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7129 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7135 getAutoCreate : function(){
7141 cfg.cls += ' inverse';
7147 cfg.cls += " " + this.cls;
7165 * @class Roo.bootstrap.PaginationItem
7166 * @extends Roo.bootstrap.Component
7167 * Bootstrap PaginationItem class
7168 * @cfg {String} html text
7169 * @cfg {String} href the link
7170 * @cfg {Boolean} preventDefault (true | false) default true
7171 * @cfg {Boolean} active (true | false) default false
7172 * @cfg {Boolean} disabled default false
7176 * Create a new PaginationItem
7177 * @param {Object} config The config object
7181 Roo.bootstrap.PaginationItem = function(config){
7182 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7187 * The raw click event for the entire grid.
7188 * @param {Roo.EventObject} e
7194 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7198 preventDefault: true,
7203 getAutoCreate : function(){
7209 href : this.href ? this.href : '#',
7210 html : this.html ? this.html : ''
7220 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7224 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230 initEvents: function() {
7232 this.el.on('click', this.onClick, this);
7235 onClick : function(e)
7237 Roo.log('PaginationItem on click ');
7238 if(this.preventDefault){
7246 this.fireEvent('click', this, e);
7262 * @class Roo.bootstrap.Slider
7263 * @extends Roo.bootstrap.Component
7264 * Bootstrap Slider class
7267 * Create a new Slider
7268 * @param {Object} config The config object
7271 Roo.bootstrap.Slider = function(config){
7272 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7275 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7277 getAutoCreate : function(){
7281 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7285 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7297 * Ext JS Library 1.1.1
7298 * Copyright(c) 2006-2007, Ext JS, LLC.
7300 * Originally Released Under LGPL - original licence link has changed is not relivant.
7303 * <script type="text/javascript">
7308 * @class Roo.grid.ColumnModel
7309 * @extends Roo.util.Observable
7310 * This is the default implementation of a ColumnModel used by the Grid. It defines
7311 * the columns in the grid.
7314 var colModel = new Roo.grid.ColumnModel([
7315 {header: "Ticker", width: 60, sortable: true, locked: true},
7316 {header: "Company Name", width: 150, sortable: true},
7317 {header: "Market Cap.", width: 100, sortable: true},
7318 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7319 {header: "Employees", width: 100, sortable: true, resizable: false}
7324 * The config options listed for this class are options which may appear in each
7325 * individual column definition.
7326 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7328 * @param {Object} config An Array of column config objects. See this class's
7329 * config objects for details.
7331 Roo.grid.ColumnModel = function(config){
7333 * The config passed into the constructor
7335 this.config = []; //config;
7338 // if no id, create one
7339 // if the column does not have a dataIndex mapping,
7340 // map it to the order it is in the config
7341 for(var i = 0, len = config.length; i < len; i++){
7342 this.addColumn(config[i]);
7347 * The width of columns which have no width specified (defaults to 100)
7350 this.defaultWidth = 100;
7353 * Default sortable of columns which have no sortable specified (defaults to false)
7356 this.defaultSortable = false;
7360 * @event widthchange
7361 * Fires when the width of a column changes.
7362 * @param {ColumnModel} this
7363 * @param {Number} columnIndex The column index
7364 * @param {Number} newWidth The new width
7366 "widthchange": true,
7368 * @event headerchange
7369 * Fires when the text of a header changes.
7370 * @param {ColumnModel} this
7371 * @param {Number} columnIndex The column index
7372 * @param {Number} newText The new header text
7374 "headerchange": true,
7376 * @event hiddenchange
7377 * Fires when a column is hidden or "unhidden".
7378 * @param {ColumnModel} this
7379 * @param {Number} columnIndex The column index
7380 * @param {Boolean} hidden true if hidden, false otherwise
7382 "hiddenchange": true,
7384 * @event columnmoved
7385 * Fires when a column is moved.
7386 * @param {ColumnModel} this
7387 * @param {Number} oldIndex
7388 * @param {Number} newIndex
7390 "columnmoved" : true,
7392 * @event columlockchange
7393 * Fires when a column's locked state is changed
7394 * @param {ColumnModel} this
7395 * @param {Number} colIndex
7396 * @param {Boolean} locked true if locked
7398 "columnlockchange" : true
7400 Roo.grid.ColumnModel.superclass.constructor.call(this);
7402 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7404 * @cfg {String} header The header text to display in the Grid view.
7407 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7408 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7409 * specified, the column's index is used as an index into the Record's data Array.
7412 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7413 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7416 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7417 * Defaults to the value of the {@link #defaultSortable} property.
7418 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7421 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7424 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7427 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7430 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7433 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7434 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7435 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7436 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7439 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7442 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7445 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7448 * @cfg {String} cursor (Optional)
7451 * @cfg {String} tooltip (Optional)
7454 * @cfg {Number} xs (Optional)
7457 * @cfg {Number} sm (Optional)
7460 * @cfg {Number} md (Optional)
7463 * @cfg {Number} lg (Optional)
7466 * Returns the id of the column at the specified index.
7467 * @param {Number} index The column index
7468 * @return {String} the id
7470 getColumnId : function(index){
7471 return this.config[index].id;
7475 * Returns the column for a specified id.
7476 * @param {String} id The column id
7477 * @return {Object} the column
7479 getColumnById : function(id){
7480 return this.lookup[id];
7485 * Returns the column Object for a specified dataIndex.
7486 * @param {String} dataIndex The column dataIndex
7487 * @return {Object|Boolean} the column or false if not found
7489 getColumnByDataIndex: function(dataIndex){
7490 var index = this.findColumnIndex(dataIndex);
7491 return index > -1 ? this.config[index] : false;
7495 * Returns the index for a specified column id.
7496 * @param {String} id The column id
7497 * @return {Number} the index, or -1 if not found
7499 getIndexById : function(id){
7500 for(var i = 0, len = this.config.length; i < len; i++){
7501 if(this.config[i].id == id){
7509 * Returns the index for a specified column dataIndex.
7510 * @param {String} dataIndex The column dataIndex
7511 * @return {Number} the index, or -1 if not found
7514 findColumnIndex : function(dataIndex){
7515 for(var i = 0, len = this.config.length; i < len; i++){
7516 if(this.config[i].dataIndex == dataIndex){
7524 moveColumn : function(oldIndex, newIndex){
7525 var c = this.config[oldIndex];
7526 this.config.splice(oldIndex, 1);
7527 this.config.splice(newIndex, 0, c);
7528 this.dataMap = null;
7529 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7532 isLocked : function(colIndex){
7533 return this.config[colIndex].locked === true;
7536 setLocked : function(colIndex, value, suppressEvent){
7537 if(this.isLocked(colIndex) == value){
7540 this.config[colIndex].locked = value;
7542 this.fireEvent("columnlockchange", this, colIndex, value);
7546 getTotalLockedWidth : function(){
7548 for(var i = 0; i < this.config.length; i++){
7549 if(this.isLocked(i) && !this.isHidden(i)){
7550 this.totalWidth += this.getColumnWidth(i);
7556 getLockedCount : function(){
7557 for(var i = 0, len = this.config.length; i < len; i++){
7558 if(!this.isLocked(i)){
7563 return this.config.length;
7567 * Returns the number of columns.
7570 getColumnCount : function(visibleOnly){
7571 if(visibleOnly === true){
7573 for(var i = 0, len = this.config.length; i < len; i++){
7574 if(!this.isHidden(i)){
7580 return this.config.length;
7584 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7585 * @param {Function} fn
7586 * @param {Object} scope (optional)
7587 * @return {Array} result
7589 getColumnsBy : function(fn, scope){
7591 for(var i = 0, len = this.config.length; i < len; i++){
7592 var c = this.config[i];
7593 if(fn.call(scope||this, c, i) === true){
7601 * Returns true if the specified column is sortable.
7602 * @param {Number} col The column index
7605 isSortable : function(col){
7606 if(typeof this.config[col].sortable == "undefined"){
7607 return this.defaultSortable;
7609 return this.config[col].sortable;
7613 * Returns the rendering (formatting) function defined for the column.
7614 * @param {Number} col The column index.
7615 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7617 getRenderer : function(col){
7618 if(!this.config[col].renderer){
7619 return Roo.grid.ColumnModel.defaultRenderer;
7621 return this.config[col].renderer;
7625 * Sets the rendering (formatting) function for a column.
7626 * @param {Number} col The column index
7627 * @param {Function} fn The function to use to process the cell's raw data
7628 * to return HTML markup for the grid view. The render function is called with
7629 * the following parameters:<ul>
7630 * <li>Data value.</li>
7631 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7632 * <li>css A CSS style string to apply to the table cell.</li>
7633 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7634 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7635 * <li>Row index</li>
7636 * <li>Column index</li>
7637 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7639 setRenderer : function(col, fn){
7640 this.config[col].renderer = fn;
7644 * Returns the width for the specified column.
7645 * @param {Number} col The column index
7648 getColumnWidth : function(col){
7649 return this.config[col].width * 1 || this.defaultWidth;
7653 * Sets the width for a column.
7654 * @param {Number} col The column index
7655 * @param {Number} width The new width
7657 setColumnWidth : function(col, width, suppressEvent){
7658 this.config[col].width = width;
7659 this.totalWidth = null;
7661 this.fireEvent("widthchange", this, col, width);
7666 * Returns the total width of all columns.
7667 * @param {Boolean} includeHidden True to include hidden column widths
7670 getTotalWidth : function(includeHidden){
7671 if(!this.totalWidth){
7672 this.totalWidth = 0;
7673 for(var i = 0, len = this.config.length; i < len; i++){
7674 if(includeHidden || !this.isHidden(i)){
7675 this.totalWidth += this.getColumnWidth(i);
7679 return this.totalWidth;
7683 * Returns the header for the specified column.
7684 * @param {Number} col The column index
7687 getColumnHeader : function(col){
7688 return this.config[col].header;
7692 * Sets the header for a column.
7693 * @param {Number} col The column index
7694 * @param {String} header The new header
7696 setColumnHeader : function(col, header){
7697 this.config[col].header = header;
7698 this.fireEvent("headerchange", this, col, header);
7702 * Returns the tooltip for the specified column.
7703 * @param {Number} col The column index
7706 getColumnTooltip : function(col){
7707 return this.config[col].tooltip;
7710 * Sets the tooltip for a column.
7711 * @param {Number} col The column index
7712 * @param {String} tooltip The new tooltip
7714 setColumnTooltip : function(col, tooltip){
7715 this.config[col].tooltip = tooltip;
7719 * Returns the dataIndex for the specified column.
7720 * @param {Number} col The column index
7723 getDataIndex : function(col){
7724 return this.config[col].dataIndex;
7728 * Sets the dataIndex for a column.
7729 * @param {Number} col The column index
7730 * @param {Number} dataIndex The new dataIndex
7732 setDataIndex : function(col, dataIndex){
7733 this.config[col].dataIndex = dataIndex;
7739 * Returns true if the cell is editable.
7740 * @param {Number} colIndex The column index
7741 * @param {Number} rowIndex The row index - this is nto actually used..?
7744 isCellEditable : function(colIndex, rowIndex){
7745 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7749 * Returns the editor defined for the cell/column.
7750 * return false or null to disable editing.
7751 * @param {Number} colIndex The column index
7752 * @param {Number} rowIndex The row index
7755 getCellEditor : function(colIndex, rowIndex){
7756 return this.config[colIndex].editor;
7760 * Sets if a column is editable.
7761 * @param {Number} col The column index
7762 * @param {Boolean} editable True if the column is editable
7764 setEditable : function(col, editable){
7765 this.config[col].editable = editable;
7770 * Returns true if the column is hidden.
7771 * @param {Number} colIndex The column index
7774 isHidden : function(colIndex){
7775 return this.config[colIndex].hidden;
7780 * Returns true if the column width cannot be changed
7782 isFixed : function(colIndex){
7783 return this.config[colIndex].fixed;
7787 * Returns true if the column can be resized
7790 isResizable : function(colIndex){
7791 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7794 * Sets if a column is hidden.
7795 * @param {Number} colIndex The column index
7796 * @param {Boolean} hidden True if the column is hidden
7798 setHidden : function(colIndex, hidden){
7799 this.config[colIndex].hidden = hidden;
7800 this.totalWidth = null;
7801 this.fireEvent("hiddenchange", this, colIndex, hidden);
7805 * Sets the editor for a column.
7806 * @param {Number} col The column index
7807 * @param {Object} editor The editor object
7809 setEditor : function(col, editor){
7810 this.config[col].editor = editor;
7813 * Add a column (experimental...) - defaults to adding to the end..
7814 * @param {Object} config
7816 addColumn : function(c)
7819 var i = this.config.length;
7822 if(typeof c.dataIndex == "undefined"){
7825 if(typeof c.renderer == "string"){
7826 c.renderer = Roo.util.Format[c.renderer];
7828 if(typeof c.id == "undefined"){
7831 if(c.editor && c.editor.xtype){
7832 c.editor = Roo.factory(c.editor, Roo.grid);
7834 if(c.editor && c.editor.isFormField){
7835 c.editor = new Roo.grid.GridEditor(c.editor);
7837 this.lookup[c.id] = c;
7842 Roo.grid.ColumnModel.defaultRenderer = function(value)
7844 if(typeof value == "object") {
7847 if(typeof value == "string" && value.length < 1){
7851 return String.format("{0}", value);
7854 // Alias for backwards compatibility
7855 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7858 * Ext JS Library 1.1.1
7859 * Copyright(c) 2006-2007, Ext JS, LLC.
7861 * Originally Released Under LGPL - original licence link has changed is not relivant.
7864 * <script type="text/javascript">
7868 * @class Roo.LoadMask
7869 * A simple utility class for generically masking elements while loading data. If the element being masked has
7870 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7871 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7872 * element's UpdateManager load indicator and will be destroyed after the initial load.
7874 * Create a new LoadMask
7875 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7876 * @param {Object} config The config object
7878 Roo.LoadMask = function(el, config){
7879 this.el = Roo.get(el);
7880 Roo.apply(this, config);
7882 this.store.on('beforeload', this.onBeforeLoad, this);
7883 this.store.on('load', this.onLoad, this);
7884 this.store.on('loadexception', this.onLoadException, this);
7885 this.removeMask = false;
7887 var um = this.el.getUpdateManager();
7888 um.showLoadIndicator = false; // disable the default indicator
7889 um.on('beforeupdate', this.onBeforeLoad, this);
7890 um.on('update', this.onLoad, this);
7891 um.on('failure', this.onLoad, this);
7892 this.removeMask = true;
7896 Roo.LoadMask.prototype = {
7898 * @cfg {Boolean} removeMask
7899 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7900 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7904 * The text to display in a centered loading message box (defaults to 'Loading...')
7908 * @cfg {String} msgCls
7909 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7911 msgCls : 'x-mask-loading',
7914 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7920 * Disables the mask to prevent it from being displayed
7922 disable : function(){
7923 this.disabled = true;
7927 * Enables the mask so that it can be displayed
7929 enable : function(){
7930 this.disabled = false;
7933 onLoadException : function()
7937 if (typeof(arguments[3]) != 'undefined') {
7938 Roo.MessageBox.alert("Error loading",arguments[3]);
7942 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7943 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7950 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7955 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7959 onBeforeLoad : function(){
7961 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7966 destroy : function(){
7968 this.store.un('beforeload', this.onBeforeLoad, this);
7969 this.store.un('load', this.onLoad, this);
7970 this.store.un('loadexception', this.onLoadException, this);
7972 var um = this.el.getUpdateManager();
7973 um.un('beforeupdate', this.onBeforeLoad, this);
7974 um.un('update', this.onLoad, this);
7975 um.un('failure', this.onLoad, this);
7986 * @class Roo.bootstrap.Table
7987 * @extends Roo.bootstrap.Component
7988 * Bootstrap Table class
7989 * @cfg {String} cls table class
7990 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7991 * @cfg {String} bgcolor Specifies the background color for a table
7992 * @cfg {Number} border Specifies whether the table cells should have borders or not
7993 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7994 * @cfg {Number} cellspacing Specifies the space between cells
7995 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7996 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7997 * @cfg {String} sortable Specifies that the table should be sortable
7998 * @cfg {String} summary Specifies a summary of the content of a table
7999 * @cfg {Number} width Specifies the width of a table
8000 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
8002 * @cfg {boolean} striped Should the rows be alternative striped
8003 * @cfg {boolean} bordered Add borders to the table
8004 * @cfg {boolean} hover Add hover highlighting
8005 * @cfg {boolean} condensed Format condensed
8006 * @cfg {boolean} responsive Format condensed
8007 * @cfg {Boolean} loadMask (true|false) default false
8008 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8009 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8010 * @cfg {Boolean} rowSelection (true|false) default false
8011 * @cfg {Boolean} cellSelection (true|false) default false
8012 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8013 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8014 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8015 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8019 * Create a new Table
8020 * @param {Object} config The config object
8023 Roo.bootstrap.Table = function(config){
8024 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8029 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8030 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8031 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8032 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8034 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8036 this.sm.grid = this;
8037 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8038 this.sm = this.selModel;
8039 this.sm.xmodule = this.xmodule || false;
8042 if (this.cm && typeof(this.cm.config) == 'undefined') {
8043 this.colModel = new Roo.grid.ColumnModel(this.cm);
8044 this.cm = this.colModel;
8045 this.cm.xmodule = this.xmodule || false;
8048 this.store= Roo.factory(this.store, Roo.data);
8049 this.ds = this.store;
8050 this.ds.xmodule = this.xmodule || false;
8053 if (this.footer && this.store) {
8054 this.footer.dataSource = this.ds;
8055 this.footer = Roo.factory(this.footer);
8062 * Fires when a cell is clicked
8063 * @param {Roo.bootstrap.Table} this
8064 * @param {Roo.Element} el
8065 * @param {Number} rowIndex
8066 * @param {Number} columnIndex
8067 * @param {Roo.EventObject} e
8071 * @event celldblclick
8072 * Fires when a cell is double clicked
8073 * @param {Roo.bootstrap.Table} this
8074 * @param {Roo.Element} el
8075 * @param {Number} rowIndex
8076 * @param {Number} columnIndex
8077 * @param {Roo.EventObject} e
8079 "celldblclick" : true,
8082 * Fires when a row is clicked
8083 * @param {Roo.bootstrap.Table} this
8084 * @param {Roo.Element} el
8085 * @param {Number} rowIndex
8086 * @param {Roo.EventObject} e
8090 * @event rowdblclick
8091 * Fires when a row is double clicked
8092 * @param {Roo.bootstrap.Table} this
8093 * @param {Roo.Element} el
8094 * @param {Number} rowIndex
8095 * @param {Roo.EventObject} e
8097 "rowdblclick" : true,
8100 * Fires when a mouseover occur
8101 * @param {Roo.bootstrap.Table} this
8102 * @param {Roo.Element} el
8103 * @param {Number} rowIndex
8104 * @param {Number} columnIndex
8105 * @param {Roo.EventObject} e
8110 * Fires when a mouseout occur
8111 * @param {Roo.bootstrap.Table} this
8112 * @param {Roo.Element} el
8113 * @param {Number} rowIndex
8114 * @param {Number} columnIndex
8115 * @param {Roo.EventObject} e
8120 * Fires when a row is rendered, so you can change add a style to it.
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8126 * @event rowsrendered
8127 * Fires when all the rows have been rendered
8128 * @param {Roo.bootstrap.Table} this
8130 'rowsrendered' : true,
8132 * @event contextmenu
8133 * The raw contextmenu event for the entire grid.
8134 * @param {Roo.EventObject} e
8136 "contextmenu" : true,
8138 * @event rowcontextmenu
8139 * Fires when a row is right clicked
8140 * @param {Roo.bootstrap.Table} this
8141 * @param {Number} rowIndex
8142 * @param {Roo.EventObject} e
8144 "rowcontextmenu" : true,
8146 * @event cellcontextmenu
8147 * Fires when a cell is right clicked
8148 * @param {Roo.bootstrap.Table} this
8149 * @param {Number} rowIndex
8150 * @param {Number} cellIndex
8151 * @param {Roo.EventObject} e
8153 "cellcontextmenu" : true,
8155 * @event headercontextmenu
8156 * Fires when a header is right clicked
8157 * @param {Roo.bootstrap.Table} this
8158 * @param {Number} columnIndex
8159 * @param {Roo.EventObject} e
8161 "headercontextmenu" : true
8165 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8191 rowSelection : false,
8192 cellSelection : false,
8195 // Roo.Element - the tbody
8197 // Roo.Element - thead element
8200 container: false, // used by gridpanel...
8206 auto_hide_footer : false,
8208 getAutoCreate : function()
8210 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8217 if (this.scrollBody) {
8218 cfg.cls += ' table-body-fixed';
8221 cfg.cls += ' table-striped';
8225 cfg.cls += ' table-hover';
8227 if (this.bordered) {
8228 cfg.cls += ' table-bordered';
8230 if (this.condensed) {
8231 cfg.cls += ' table-condensed';
8233 if (this.responsive) {
8234 cfg.cls += ' table-responsive';
8238 cfg.cls+= ' ' +this.cls;
8241 // this lot should be simplifed...
8254 ].forEach(function(k) {
8262 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8265 if(this.store || this.cm){
8266 if(this.headerShow){
8267 cfg.cn.push(this.renderHeader());
8270 cfg.cn.push(this.renderBody());
8272 if(this.footerShow){
8273 cfg.cn.push(this.renderFooter());
8275 // where does this come from?
8276 //cfg.cls+= ' TableGrid';
8279 return { cn : [ cfg ] };
8282 initEvents : function()
8284 if(!this.store || !this.cm){
8287 if (this.selModel) {
8288 this.selModel.initEvents();
8292 //Roo.log('initEvents with ds!!!!');
8294 this.mainBody = this.el.select('tbody', true).first();
8295 this.mainHead = this.el.select('thead', true).first();
8296 this.mainFoot = this.el.select('tfoot', true).first();
8301 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8302 e.on('click', this.sort, this);
8305 this.mainBody.on("click", this.onClick, this);
8306 this.mainBody.on("dblclick", this.onDblClick, this);
8308 // why is this done????? = it breaks dialogs??
8309 //this.parent().el.setStyle('position', 'relative');
8313 this.footer.parentId = this.id;
8314 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8317 this.el.select('tfoot tr td').first().addClass('hide');
8322 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8325 this.store.on('load', this.onLoad, this);
8326 this.store.on('beforeload', this.onBeforeLoad, this);
8327 this.store.on('update', this.onUpdate, this);
8328 this.store.on('add', this.onAdd, this);
8329 this.store.on("clear", this.clear, this);
8331 this.el.on("contextmenu", this.onContextMenu, this);
8333 this.mainBody.on('scroll', this.onBodyScroll, this);
8335 this.cm.on("headerchange", this.onHeaderChange, this);
8337 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8341 onContextMenu : function(e, t)
8343 this.processEvent("contextmenu", e);
8346 processEvent : function(name, e)
8348 if (name != 'touchstart' ) {
8349 this.fireEvent(name, e);
8352 var t = e.getTarget();
8354 var cell = Roo.get(t);
8360 if(cell.findParent('tfoot', false, true)){
8364 if(cell.findParent('thead', false, true)){
8366 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8367 cell = Roo.get(t).findParent('th', false, true);
8369 Roo.log("failed to find th in thead?");
8370 Roo.log(e.getTarget());
8375 var cellIndex = cell.dom.cellIndex;
8377 var ename = name == 'touchstart' ? 'click' : name;
8378 this.fireEvent("header" + ename, this, cellIndex, e);
8383 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8384 cell = Roo.get(t).findParent('td', false, true);
8386 Roo.log("failed to find th in tbody?");
8387 Roo.log(e.getTarget());
8392 var row = cell.findParent('tr', false, true);
8393 var cellIndex = cell.dom.cellIndex;
8394 var rowIndex = row.dom.rowIndex - 1;
8398 this.fireEvent("row" + name, this, rowIndex, e);
8402 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8408 onMouseover : function(e, el)
8410 var cell = Roo.get(el);
8416 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8417 cell = cell.findParent('td', false, true);
8420 var row = cell.findParent('tr', false, true);
8421 var cellIndex = cell.dom.cellIndex;
8422 var rowIndex = row.dom.rowIndex - 1; // start from 0
8424 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8428 onMouseout : function(e, el)
8430 var cell = Roo.get(el);
8436 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8437 cell = cell.findParent('td', false, true);
8440 var row = cell.findParent('tr', false, true);
8441 var cellIndex = cell.dom.cellIndex;
8442 var rowIndex = row.dom.rowIndex - 1; // start from 0
8444 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8448 onClick : function(e, el)
8450 var cell = Roo.get(el);
8452 if(!cell || (!this.cellSelection && !this.rowSelection)){
8456 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8457 cell = cell.findParent('td', false, true);
8460 if(!cell || typeof(cell) == 'undefined'){
8464 var row = cell.findParent('tr', false, true);
8466 if(!row || typeof(row) == 'undefined'){
8470 var cellIndex = cell.dom.cellIndex;
8471 var rowIndex = this.getRowIndex(row);
8473 // why??? - should these not be based on SelectionModel?
8474 //if(this.cellSelection){
8475 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8478 //if(this.rowSelection){
8479 this.fireEvent('rowclick', this, row, rowIndex, e);
8484 onDblClick : function(e,el)
8486 var cell = Roo.get(el);
8488 if(!cell || (!this.cellSelection && !this.rowSelection)){
8492 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8493 cell = cell.findParent('td', false, true);
8496 if(!cell || typeof(cell) == 'undefined'){
8500 var row = cell.findParent('tr', false, true);
8502 if(!row || typeof(row) == 'undefined'){
8506 var cellIndex = cell.dom.cellIndex;
8507 var rowIndex = this.getRowIndex(row);
8509 if(this.cellSelection){
8510 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8513 if(this.rowSelection){
8514 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8518 sort : function(e,el)
8520 var col = Roo.get(el);
8522 if(!col.hasClass('sortable')){
8526 var sort = col.attr('sort');
8529 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8533 this.store.sortInfo = {field : sort, direction : dir};
8536 Roo.log("calling footer first");
8537 this.footer.onClick('first');
8540 this.store.load({ params : { start : 0 } });
8544 renderHeader : function()
8552 this.totalWidth = 0;
8554 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8556 var config = cm.config[i];
8560 cls : 'x-hcol-' + i,
8563 html: cm.getColumnHeader(i)
8566 var tooltip = cm.getColumnTooltip(i);
8568 c.tooltip = tooltip;
8574 if(typeof(config.sortable) != 'undefined' && config.sortable){
8576 c.html = '<i class="fa"></i>' + c.html;
8579 // could use BS4 hidden-..-down
8581 if(typeof(config.lgHeader) != 'undefined'){
8582 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8585 if(typeof(config.mdHeader) != 'undefined'){
8586 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8589 if(typeof(config.smHeader) != 'undefined'){
8590 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8593 if(typeof(config.xsHeader) != 'undefined'){
8594 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8601 if(typeof(config.tooltip) != 'undefined'){
8602 c.tooltip = config.tooltip;
8605 if(typeof(config.colspan) != 'undefined'){
8606 c.colspan = config.colspan;
8609 if(typeof(config.hidden) != 'undefined' && config.hidden){
8610 c.style += ' display:none;';
8613 if(typeof(config.dataIndex) != 'undefined'){
8614 c.sort = config.dataIndex;
8619 if(typeof(config.align) != 'undefined' && config.align.length){
8620 c.style += ' text-align:' + config.align + ';';
8623 if(typeof(config.width) != 'undefined'){
8624 c.style += ' width:' + config.width + 'px;';
8625 this.totalWidth += config.width;
8627 this.totalWidth += 100; // assume minimum of 100 per column?
8630 if(typeof(config.cls) != 'undefined'){
8631 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8634 ['xs','sm','md','lg'].map(function(size){
8636 if(typeof(config[size]) == 'undefined'){
8640 if (!config[size]) { // 0 = hidden
8641 // BS 4 '0' is treated as hide that column and below.
8642 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8646 c.cls += ' col-' + size + '-' + config[size] + (
8647 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8659 renderBody : function()
8669 colspan : this.cm.getColumnCount()
8679 renderFooter : function()
8689 colspan : this.cm.getColumnCount()
8703 // Roo.log('ds onload');
8708 var ds = this.store;
8710 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8711 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8712 if (_this.store.sortInfo) {
8714 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8715 e.select('i', true).addClass(['fa-arrow-up']);
8718 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8719 e.select('i', true).addClass(['fa-arrow-down']);
8724 var tbody = this.mainBody;
8726 if(ds.getCount() > 0){
8727 ds.data.each(function(d,rowIndex){
8728 var row = this.renderRow(cm, ds, rowIndex);
8730 tbody.createChild(row);
8734 if(row.cellObjects.length){
8735 Roo.each(row.cellObjects, function(r){
8736 _this.renderCellObject(r);
8743 var tfoot = this.el.select('tfoot', true).first();
8745 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8747 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8749 var total = this.ds.getTotalCount();
8751 if(this.footer.pageSize < total){
8752 this.mainFoot.show();
8756 Roo.each(this.el.select('tbody td', true).elements, function(e){
8757 e.on('mouseover', _this.onMouseover, _this);
8760 Roo.each(this.el.select('tbody td', true).elements, function(e){
8761 e.on('mouseout', _this.onMouseout, _this);
8763 this.fireEvent('rowsrendered', this);
8769 onUpdate : function(ds,record)
8771 this.refreshRow(record);
8775 onRemove : function(ds, record, index, isUpdate){
8776 if(isUpdate !== true){
8777 this.fireEvent("beforerowremoved", this, index, record);
8779 var bt = this.mainBody.dom;
8781 var rows = this.el.select('tbody > tr', true).elements;
8783 if(typeof(rows[index]) != 'undefined'){
8784 bt.removeChild(rows[index].dom);
8787 // if(bt.rows[index]){
8788 // bt.removeChild(bt.rows[index]);
8791 if(isUpdate !== true){
8792 //this.stripeRows(index);
8793 //this.syncRowHeights(index, index);
8795 this.fireEvent("rowremoved", this, index, record);
8799 onAdd : function(ds, records, rowIndex)
8801 //Roo.log('on Add called');
8802 // - note this does not handle multiple adding very well..
8803 var bt = this.mainBody.dom;
8804 for (var i =0 ; i < records.length;i++) {
8805 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8806 //Roo.log(records[i]);
8807 //Roo.log(this.store.getAt(rowIndex+i));
8808 this.insertRow(this.store, rowIndex + i, false);
8815 refreshRow : function(record){
8816 var ds = this.store, index;
8817 if(typeof record == 'number'){
8819 record = ds.getAt(index);
8821 index = ds.indexOf(record);
8823 return; // should not happen - but seems to
8826 this.insertRow(ds, index, true);
8828 this.onRemove(ds, record, index+1, true);
8830 //this.syncRowHeights(index, index);
8832 this.fireEvent("rowupdated", this, index, record);
8835 insertRow : function(dm, rowIndex, isUpdate){
8838 this.fireEvent("beforerowsinserted", this, rowIndex);
8840 //var s = this.getScrollState();
8841 var row = this.renderRow(this.cm, this.store, rowIndex);
8842 // insert before rowIndex..
8843 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8847 if(row.cellObjects.length){
8848 Roo.each(row.cellObjects, function(r){
8849 _this.renderCellObject(r);
8854 this.fireEvent("rowsinserted", this, rowIndex);
8855 //this.syncRowHeights(firstRow, lastRow);
8856 //this.stripeRows(firstRow);
8863 getRowDom : function(rowIndex)
8865 var rows = this.el.select('tbody > tr', true).elements;
8867 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8870 // returns the object tree for a tr..
8873 renderRow : function(cm, ds, rowIndex)
8875 var d = ds.getAt(rowIndex);
8879 cls : 'x-row-' + rowIndex,
8883 var cellObjects = [];
8885 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8886 var config = cm.config[i];
8888 var renderer = cm.getRenderer(i);
8892 if(typeof(renderer) !== 'undefined'){
8893 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8895 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8896 // and are rendered into the cells after the row is rendered - using the id for the element.
8898 if(typeof(value) === 'object'){
8908 rowIndex : rowIndex,
8913 this.fireEvent('rowclass', this, rowcfg);
8917 // this might end up displaying HTML?
8918 // this is too messy... - better to only do it on columsn you know are going to be too long
8919 //tooltip : (typeof(value) === 'object') ? '' : value,
8920 cls : rowcfg.rowClass + ' x-col-' + i,
8922 html: (typeof(value) === 'object') ? '' : value
8929 if(typeof(config.colspan) != 'undefined'){
8930 td.colspan = config.colspan;
8933 if(typeof(config.hidden) != 'undefined' && config.hidden){
8934 td.style += ' display:none;';
8937 if(typeof(config.align) != 'undefined' && config.align.length){
8938 td.style += ' text-align:' + config.align + ';';
8940 if(typeof(config.valign) != 'undefined' && config.valign.length){
8941 td.style += ' vertical-align:' + config.valign + ';';
8944 if(typeof(config.width) != 'undefined'){
8945 td.style += ' width:' + config.width + 'px;';
8948 if(typeof(config.cursor) != 'undefined'){
8949 td.style += ' cursor:' + config.cursor + ';';
8952 if(typeof(config.cls) != 'undefined'){
8953 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8956 ['xs','sm','md','lg'].map(function(size){
8958 if(typeof(config[size]) == 'undefined'){
8964 if (!config[size]) { // 0 = hidden
8965 // BS 4 '0' is treated as hide that column and below.
8966 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8970 td.cls += ' col-' + size + '-' + config[size] + (
8971 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8981 row.cellObjects = cellObjects;
8989 onBeforeLoad : function()
8998 this.el.select('tbody', true).first().dom.innerHTML = '';
9001 * Show or hide a row.
9002 * @param {Number} rowIndex to show or hide
9003 * @param {Boolean} state hide
9005 setRowVisibility : function(rowIndex, state)
9007 var bt = this.mainBody.dom;
9009 var rows = this.el.select('tbody > tr', true).elements;
9011 if(typeof(rows[rowIndex]) == 'undefined'){
9014 rows[rowIndex].dom.style.display = state ? '' : 'none';
9018 getSelectionModel : function(){
9020 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9022 return this.selModel;
9025 * Render the Roo.bootstrap object from renderder
9027 renderCellObject : function(r)
9031 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9033 var t = r.cfg.render(r.container);
9036 Roo.each(r.cfg.cn, function(c){
9038 container: t.getChildContainer(),
9041 _this.renderCellObject(child);
9046 getRowIndex : function(row)
9050 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9061 * Returns the grid's underlying element = used by panel.Grid
9062 * @return {Element} The element
9064 getGridEl : function(){
9068 * Forces a resize - used by panel.Grid
9069 * @return {Element} The element
9071 autoSize : function()
9073 //var ctr = Roo.get(this.container.dom.parentElement);
9074 var ctr = Roo.get(this.el.dom);
9076 var thd = this.getGridEl().select('thead',true).first();
9077 var tbd = this.getGridEl().select('tbody', true).first();
9078 var tfd = this.getGridEl().select('tfoot', true).first();
9080 var cw = ctr.getWidth();
9081 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9085 tbd.setWidth(ctr.getWidth());
9086 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9087 // this needs fixing for various usage - currently only hydra job advers I think..
9089 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9091 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9094 cw = Math.max(cw, this.totalWidth);
9095 this.getGridEl().select('tbody tr',true).setWidth(cw);
9097 // resize 'expandable coloumn?
9099 return; // we doe not have a view in this design..
9102 onBodyScroll: function()
9104 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9106 this.mainHead.setStyle({
9107 'position' : 'relative',
9108 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9114 var scrollHeight = this.mainBody.dom.scrollHeight;
9116 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9118 var height = this.mainBody.getHeight();
9120 if(scrollHeight - height == scrollTop) {
9122 var total = this.ds.getTotalCount();
9124 if(this.footer.cursor + this.footer.pageSize < total){
9126 this.footer.ds.load({
9128 start : this.footer.cursor + this.footer.pageSize,
9129 limit : this.footer.pageSize
9139 onHeaderChange : function()
9141 var header = this.renderHeader();
9142 var table = this.el.select('table', true).first();
9144 this.mainHead.remove();
9145 this.mainHead = table.createChild(header, this.mainBody, false);
9147 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9148 e.on('click', this.sort, this);
9154 onHiddenChange : function(colModel, colIndex, hidden)
9156 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9157 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9159 this.CSS.updateRule(thSelector, "display", "");
9160 this.CSS.updateRule(tdSelector, "display", "");
9163 this.CSS.updateRule(thSelector, "display", "none");
9164 this.CSS.updateRule(tdSelector, "display", "none");
9167 this.onHeaderChange();
9171 setColumnWidth: function(col_index, width)
9173 // width = "md-2 xs-2..."
9174 if(!this.colModel.config[col_index]) {
9178 var w = width.split(" ");
9180 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9182 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9185 for(var j = 0; j < w.length; j++) {
9191 var size_cls = w[j].split("-");
9193 if(!Number.isInteger(size_cls[1] * 1)) {
9197 if(!this.colModel.config[col_index][size_cls[0]]) {
9201 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9205 h_row[0].classList.replace(
9206 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9207 "col-"+size_cls[0]+"-"+size_cls[1]
9210 for(var i = 0; i < rows.length; i++) {
9212 var size_cls = w[j].split("-");
9214 if(!Number.isInteger(size_cls[1] * 1)) {
9218 if(!this.colModel.config[col_index][size_cls[0]]) {
9222 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9226 rows[i].classList.replace(
9227 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9228 "col-"+size_cls[0]+"-"+size_cls[1]
9232 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9247 * @class Roo.bootstrap.TableCell
9248 * @extends Roo.bootstrap.Component
9249 * Bootstrap TableCell class
9250 * @cfg {String} html cell contain text
9251 * @cfg {String} cls cell class
9252 * @cfg {String} tag cell tag (td|th) default td
9253 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9254 * @cfg {String} align Aligns the content in a cell
9255 * @cfg {String} axis Categorizes cells
9256 * @cfg {String} bgcolor Specifies the background color of a cell
9257 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9258 * @cfg {Number} colspan Specifies the number of columns a cell should span
9259 * @cfg {String} headers Specifies one or more header cells a cell is related to
9260 * @cfg {Number} height Sets the height of a cell
9261 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9262 * @cfg {Number} rowspan Sets the number of rows a cell should span
9263 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9264 * @cfg {String} valign Vertical aligns the content in a cell
9265 * @cfg {Number} width Specifies the width of a cell
9268 * Create a new TableCell
9269 * @param {Object} config The config object
9272 Roo.bootstrap.TableCell = function(config){
9273 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9276 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9296 getAutoCreate : function(){
9297 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9317 cfg.align=this.align
9323 cfg.bgcolor=this.bgcolor
9326 cfg.charoff=this.charoff
9329 cfg.colspan=this.colspan
9332 cfg.headers=this.headers
9335 cfg.height=this.height
9338 cfg.nowrap=this.nowrap
9341 cfg.rowspan=this.rowspan
9344 cfg.scope=this.scope
9347 cfg.valign=this.valign
9350 cfg.width=this.width
9369 * @class Roo.bootstrap.TableRow
9370 * @extends Roo.bootstrap.Component
9371 * Bootstrap TableRow class
9372 * @cfg {String} cls row class
9373 * @cfg {String} align Aligns the content in a table row
9374 * @cfg {String} bgcolor Specifies a background color for a table row
9375 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9376 * @cfg {String} valign Vertical aligns the content in a table row
9379 * Create a new TableRow
9380 * @param {Object} config The config object
9383 Roo.bootstrap.TableRow = function(config){
9384 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9387 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9395 getAutoCreate : function(){
9396 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9406 cfg.align = this.align;
9409 cfg.bgcolor = this.bgcolor;
9412 cfg.charoff = this.charoff;
9415 cfg.valign = this.valign;
9433 * @class Roo.bootstrap.TableBody
9434 * @extends Roo.bootstrap.Component
9435 * Bootstrap TableBody class
9436 * @cfg {String} cls element class
9437 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9438 * @cfg {String} align Aligns the content inside the element
9439 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9440 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9443 * Create a new TableBody
9444 * @param {Object} config The config object
9447 Roo.bootstrap.TableBody = function(config){
9448 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9451 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9459 getAutoCreate : function(){
9460 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9474 cfg.align = this.align;
9477 cfg.charoff = this.charoff;
9480 cfg.valign = this.valign;
9487 // initEvents : function()
9494 // this.store = Roo.factory(this.store, Roo.data);
9495 // this.store.on('load', this.onLoad, this);
9497 // this.store.load();
9501 // onLoad: function ()
9503 // this.fireEvent('load', this);
9513 * Ext JS Library 1.1.1
9514 * Copyright(c) 2006-2007, Ext JS, LLC.
9516 * Originally Released Under LGPL - original licence link has changed is not relivant.
9519 * <script type="text/javascript">
9522 // as we use this in bootstrap.
9523 Roo.namespace('Roo.form');
9525 * @class Roo.form.Action
9526 * Internal Class used to handle form actions
9528 * @param {Roo.form.BasicForm} el The form element or its id
9529 * @param {Object} config Configuration options
9534 // define the action interface
9535 Roo.form.Action = function(form, options){
9537 this.options = options || {};
9540 * Client Validation Failed
9543 Roo.form.Action.CLIENT_INVALID = 'client';
9545 * Server Validation Failed
9548 Roo.form.Action.SERVER_INVALID = 'server';
9550 * Connect to Server Failed
9553 Roo.form.Action.CONNECT_FAILURE = 'connect';
9555 * Reading Data from Server Failed
9558 Roo.form.Action.LOAD_FAILURE = 'load';
9560 Roo.form.Action.prototype = {
9562 failureType : undefined,
9563 response : undefined,
9567 run : function(options){
9572 success : function(response){
9577 handleResponse : function(response){
9581 // default connection failure
9582 failure : function(response){
9584 this.response = response;
9585 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9586 this.form.afterAction(this, false);
9589 processResponse : function(response){
9590 this.response = response;
9591 if(!response.responseText){
9594 this.result = this.handleResponse(response);
9598 // utility functions used internally
9599 getUrl : function(appendParams){
9600 var url = this.options.url || this.form.url || this.form.el.dom.action;
9602 var p = this.getParams();
9604 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9610 getMethod : function(){
9611 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9614 getParams : function(){
9615 var bp = this.form.baseParams;
9616 var p = this.options.params;
9618 if(typeof p == "object"){
9619 p = Roo.urlEncode(Roo.applyIf(p, bp));
9620 }else if(typeof p == 'string' && bp){
9621 p += '&' + Roo.urlEncode(bp);
9624 p = Roo.urlEncode(bp);
9629 createCallback : function(){
9631 success: this.success,
9632 failure: this.failure,
9634 timeout: (this.form.timeout*1000),
9635 upload: this.form.fileUpload ? this.success : undefined
9640 Roo.form.Action.Submit = function(form, options){
9641 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9644 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9647 haveProgress : false,
9648 uploadComplete : false,
9650 // uploadProgress indicator.
9651 uploadProgress : function()
9653 if (!this.form.progressUrl) {
9657 if (!this.haveProgress) {
9658 Roo.MessageBox.progress("Uploading", "Uploading");
9660 if (this.uploadComplete) {
9661 Roo.MessageBox.hide();
9665 this.haveProgress = true;
9667 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9669 var c = new Roo.data.Connection();
9671 url : this.form.progressUrl,
9676 success : function(req){
9677 //console.log(data);
9681 rdata = Roo.decode(req.responseText)
9683 Roo.log("Invalid data from server..");
9687 if (!rdata || !rdata.success) {
9689 Roo.MessageBox.alert(Roo.encode(rdata));
9692 var data = rdata.data;
9694 if (this.uploadComplete) {
9695 Roo.MessageBox.hide();
9700 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9701 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9704 this.uploadProgress.defer(2000,this);
9707 failure: function(data) {
9708 Roo.log('progress url failed ');
9719 // run get Values on the form, so it syncs any secondary forms.
9720 this.form.getValues();
9722 var o = this.options;
9723 var method = this.getMethod();
9724 var isPost = method == 'POST';
9725 if(o.clientValidation === false || this.form.isValid()){
9727 if (this.form.progressUrl) {
9728 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9729 (new Date() * 1) + '' + Math.random());
9734 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9735 form:this.form.el.dom,
9736 url:this.getUrl(!isPost),
9738 params:isPost ? this.getParams() : null,
9739 isUpload: this.form.fileUpload,
9740 formData : this.form.formData
9743 this.uploadProgress();
9745 }else if (o.clientValidation !== false){ // client validation failed
9746 this.failureType = Roo.form.Action.CLIENT_INVALID;
9747 this.form.afterAction(this, false);
9751 success : function(response)
9753 this.uploadComplete= true;
9754 if (this.haveProgress) {
9755 Roo.MessageBox.hide();
9759 var result = this.processResponse(response);
9760 if(result === true || result.success){
9761 this.form.afterAction(this, true);
9765 this.form.markInvalid(result.errors);
9766 this.failureType = Roo.form.Action.SERVER_INVALID;
9768 this.form.afterAction(this, false);
9770 failure : function(response)
9772 this.uploadComplete= true;
9773 if (this.haveProgress) {
9774 Roo.MessageBox.hide();
9777 this.response = response;
9778 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9779 this.form.afterAction(this, false);
9782 handleResponse : function(response){
9783 if(this.form.errorReader){
9784 var rs = this.form.errorReader.read(response);
9787 for(var i = 0, len = rs.records.length; i < len; i++) {
9788 var r = rs.records[i];
9792 if(errors.length < 1){
9796 success : rs.success,
9802 ret = Roo.decode(response.responseText);
9806 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9816 Roo.form.Action.Load = function(form, options){
9817 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9818 this.reader = this.form.reader;
9821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9826 Roo.Ajax.request(Roo.apply(
9827 this.createCallback(), {
9828 method:this.getMethod(),
9829 url:this.getUrl(false),
9830 params:this.getParams()
9834 success : function(response){
9836 var result = this.processResponse(response);
9837 if(result === true || !result.success || !result.data){
9838 this.failureType = Roo.form.Action.LOAD_FAILURE;
9839 this.form.afterAction(this, false);
9842 this.form.clearInvalid();
9843 this.form.setValues(result.data);
9844 this.form.afterAction(this, true);
9847 handleResponse : function(response){
9848 if(this.form.reader){
9849 var rs = this.form.reader.read(response);
9850 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9852 success : rs.success,
9856 return Roo.decode(response.responseText);
9860 Roo.form.Action.ACTION_TYPES = {
9861 'load' : Roo.form.Action.Load,
9862 'submit' : Roo.form.Action.Submit
9871 * @class Roo.bootstrap.Form
9872 * @extends Roo.bootstrap.Component
9873 * Bootstrap Form class
9874 * @cfg {String} method GET | POST (default POST)
9875 * @cfg {String} labelAlign top | left (default top)
9876 * @cfg {String} align left | right - for navbars
9877 * @cfg {Boolean} loadMask load mask when submit (default true)
9882 * @param {Object} config The config object
9886 Roo.bootstrap.Form = function(config){
9888 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9890 Roo.bootstrap.Form.popover.apply();
9894 * @event clientvalidation
9895 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9896 * @param {Form} this
9897 * @param {Boolean} valid true if the form has passed client-side validation
9899 clientvalidation: true,
9901 * @event beforeaction
9902 * Fires before any action is performed. Return false to cancel the action.
9903 * @param {Form} this
9904 * @param {Action} action The action to be performed
9908 * @event actionfailed
9909 * Fires when an action fails.
9910 * @param {Form} this
9911 * @param {Action} action The action that failed
9913 actionfailed : true,
9915 * @event actioncomplete
9916 * Fires when an action is completed.
9917 * @param {Form} this
9918 * @param {Action} action The action that completed
9920 actioncomplete : true
9924 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9927 * @cfg {String} method
9928 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9933 * The URL to use for form actions if one isn't supplied in the action options.
9936 * @cfg {Boolean} fileUpload
9937 * Set to true if this form is a file upload.
9941 * @cfg {Object} baseParams
9942 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9946 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9950 * @cfg {Sting} align (left|right) for navbar forms
9955 activeAction : null,
9958 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9959 * element by passing it or its id or mask the form itself by passing in true.
9962 waitMsgTarget : false,
9967 * @cfg {Boolean} errorMask (true|false) default false
9972 * @cfg {Number} maskOffset Default 100
9977 * @cfg {Boolean} maskBody
9981 getAutoCreate : function(){
9985 method : this.method || 'POST',
9986 id : this.id || Roo.id(),
9989 if (this.parent().xtype.match(/^Nav/)) {
9990 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9994 if (this.labelAlign == 'left' ) {
9995 cfg.cls += ' form-horizontal';
10001 initEvents : function()
10003 this.el.on('submit', this.onSubmit, this);
10004 // this was added as random key presses on the form where triggering form submit.
10005 this.el.on('keypress', function(e) {
10006 if (e.getCharCode() != 13) {
10009 // we might need to allow it for textareas.. and some other items.
10010 // check e.getTarget().
10012 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10016 Roo.log("keypress blocked");
10018 e.preventDefault();
10024 onSubmit : function(e){
10029 * Returns true if client-side validation on the form is successful.
10032 isValid : function(){
10033 var items = this.getItems();
10035 var target = false;
10037 items.each(function(f){
10043 Roo.log('invalid field: ' + f.name);
10047 if(!target && f.el.isVisible(true)){
10053 if(this.errorMask && !valid){
10054 Roo.bootstrap.Form.popover.mask(this, target);
10061 * Returns true if any fields in this form have changed since their original load.
10064 isDirty : function(){
10066 var items = this.getItems();
10067 items.each(function(f){
10077 * Performs a predefined action (submit or load) or custom actions you define on this form.
10078 * @param {String} actionName The name of the action type
10079 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10080 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10081 * accept other config options):
10083 Property Type Description
10084 ---------------- --------------- ----------------------------------------------------------------------------------
10085 url String The url for the action (defaults to the form's url)
10086 method String The form method to use (defaults to the form's method, or POST if not defined)
10087 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10088 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10089 validate the form on the client (defaults to false)
10091 * @return {BasicForm} this
10093 doAction : function(action, options){
10094 if(typeof action == 'string'){
10095 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10097 if(this.fireEvent('beforeaction', this, action) !== false){
10098 this.beforeAction(action);
10099 action.run.defer(100, action);
10105 beforeAction : function(action){
10106 var o = action.options;
10111 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10113 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10116 // not really supported yet.. ??
10118 //if(this.waitMsgTarget === true){
10119 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10120 //}else if(this.waitMsgTarget){
10121 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10122 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10124 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10130 afterAction : function(action, success){
10131 this.activeAction = null;
10132 var o = action.options;
10137 Roo.get(document.body).unmask();
10143 //if(this.waitMsgTarget === true){
10144 // this.el.unmask();
10145 //}else if(this.waitMsgTarget){
10146 // this.waitMsgTarget.unmask();
10148 // Roo.MessageBox.updateProgress(1);
10149 // Roo.MessageBox.hide();
10156 Roo.callback(o.success, o.scope, [this, action]);
10157 this.fireEvent('actioncomplete', this, action);
10161 // failure condition..
10162 // we have a scenario where updates need confirming.
10163 // eg. if a locking scenario exists..
10164 // we look for { errors : { needs_confirm : true }} in the response.
10166 (typeof(action.result) != 'undefined') &&
10167 (typeof(action.result.errors) != 'undefined') &&
10168 (typeof(action.result.errors.needs_confirm) != 'undefined')
10171 Roo.log("not supported yet");
10174 Roo.MessageBox.confirm(
10175 "Change requires confirmation",
10176 action.result.errorMsg,
10181 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10191 Roo.callback(o.failure, o.scope, [this, action]);
10192 // show an error message if no failed handler is set..
10193 if (!this.hasListener('actionfailed')) {
10194 Roo.log("need to add dialog support");
10196 Roo.MessageBox.alert("Error",
10197 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10198 action.result.errorMsg :
10199 "Saving Failed, please check your entries or try again"
10204 this.fireEvent('actionfailed', this, action);
10209 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10210 * @param {String} id The value to search for
10213 findField : function(id){
10214 var items = this.getItems();
10215 var field = items.get(id);
10217 items.each(function(f){
10218 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10225 return field || null;
10228 * Mark fields in this form invalid in bulk.
10229 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10230 * @return {BasicForm} this
10232 markInvalid : function(errors){
10233 if(errors instanceof Array){
10234 for(var i = 0, len = errors.length; i < len; i++){
10235 var fieldError = errors[i];
10236 var f = this.findField(fieldError.id);
10238 f.markInvalid(fieldError.msg);
10244 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10245 field.markInvalid(errors[id]);
10249 //Roo.each(this.childForms || [], function (f) {
10250 // f.markInvalid(errors);
10257 * Set values for fields in this form in bulk.
10258 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10259 * @return {BasicForm} this
10261 setValues : function(values){
10262 if(values instanceof Array){ // array of objects
10263 for(var i = 0, len = values.length; i < len; i++){
10265 var f = this.findField(v.id);
10267 f.setValue(v.value);
10268 if(this.trackResetOnLoad){
10269 f.originalValue = f.getValue();
10273 }else{ // object hash
10276 if(typeof values[id] != 'function' && (field = this.findField(id))){
10278 if (field.setFromData &&
10279 field.valueField &&
10280 field.displayField &&
10281 // combos' with local stores can
10282 // be queried via setValue()
10283 // to set their value..
10284 (field.store && !field.store.isLocal)
10288 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10289 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10290 field.setFromData(sd);
10292 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10294 field.setFromData(values);
10297 field.setValue(values[id]);
10301 if(this.trackResetOnLoad){
10302 field.originalValue = field.getValue();
10308 //Roo.each(this.childForms || [], function (f) {
10309 // f.setValues(values);
10316 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10317 * they are returned as an array.
10318 * @param {Boolean} asString
10321 getValues : function(asString){
10322 //if (this.childForms) {
10323 // copy values from the child forms
10324 // Roo.each(this.childForms, function (f) {
10325 // this.setValues(f.getValues());
10331 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10332 if(asString === true){
10335 return Roo.urlDecode(fs);
10339 * Returns the fields in this form as an object with key/value pairs.
10340 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10343 getFieldValues : function(with_hidden)
10345 var items = this.getItems();
10347 items.each(function(f){
10349 if (!f.getName()) {
10353 var v = f.getValue();
10355 if (f.inputType =='radio') {
10356 if (typeof(ret[f.getName()]) == 'undefined') {
10357 ret[f.getName()] = ''; // empty..
10360 if (!f.el.dom.checked) {
10364 v = f.el.dom.value;
10368 if(f.xtype == 'MoneyField'){
10369 ret[f.currencyName] = f.getCurrency();
10372 // not sure if this supported any more..
10373 if ((typeof(v) == 'object') && f.getRawValue) {
10374 v = f.getRawValue() ; // dates..
10376 // combo boxes where name != hiddenName...
10377 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10378 ret[f.name] = f.getRawValue();
10380 ret[f.getName()] = v;
10387 * Clears all invalid messages in this form.
10388 * @return {BasicForm} this
10390 clearInvalid : function(){
10391 var items = this.getItems();
10393 items.each(function(f){
10401 * Resets this form.
10402 * @return {BasicForm} this
10404 reset : function(){
10405 var items = this.getItems();
10406 items.each(function(f){
10410 Roo.each(this.childForms || [], function (f) {
10418 getItems : function()
10420 var r=new Roo.util.MixedCollection(false, function(o){
10421 return o.id || (o.id = Roo.id());
10423 var iter = function(el) {
10430 Roo.each(el.items,function(e) {
10439 hideFields : function(items)
10441 Roo.each(items, function(i){
10443 var f = this.findField(i);
10454 showFields : function(items)
10456 Roo.each(items, function(i){
10458 var f = this.findField(i);
10471 Roo.apply(Roo.bootstrap.Form, {
10487 intervalID : false,
10493 if(this.isApplied){
10498 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10499 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10500 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10501 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10504 this.maskEl.top.enableDisplayMode("block");
10505 this.maskEl.left.enableDisplayMode("block");
10506 this.maskEl.bottom.enableDisplayMode("block");
10507 this.maskEl.right.enableDisplayMode("block");
10509 this.toolTip = new Roo.bootstrap.Tooltip({
10510 cls : 'roo-form-error-popover',
10512 'left' : ['r-l', [-2,0], 'right'],
10513 'right' : ['l-r', [2,0], 'left'],
10514 'bottom' : ['tl-bl', [0,2], 'top'],
10515 'top' : [ 'bl-tl', [0,-2], 'bottom']
10519 this.toolTip.render(Roo.get(document.body));
10521 this.toolTip.el.enableDisplayMode("block");
10523 Roo.get(document.body).on('click', function(){
10527 Roo.get(document.body).on('touchstart', function(){
10531 this.isApplied = true
10534 mask : function(form, target)
10538 this.target = target;
10540 if(!this.form.errorMask || !target.el){
10544 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10546 Roo.log(scrollable);
10548 var ot = this.target.el.calcOffsetsTo(scrollable);
10550 var scrollTo = ot[1] - this.form.maskOffset;
10552 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10554 scrollable.scrollTo('top', scrollTo);
10556 var box = this.target.el.getBox();
10558 var zIndex = Roo.bootstrap.Modal.zIndex++;
10561 this.maskEl.top.setStyle('position', 'absolute');
10562 this.maskEl.top.setStyle('z-index', zIndex);
10563 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10564 this.maskEl.top.setLeft(0);
10565 this.maskEl.top.setTop(0);
10566 this.maskEl.top.show();
10568 this.maskEl.left.setStyle('position', 'absolute');
10569 this.maskEl.left.setStyle('z-index', zIndex);
10570 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10571 this.maskEl.left.setLeft(0);
10572 this.maskEl.left.setTop(box.y - this.padding);
10573 this.maskEl.left.show();
10575 this.maskEl.bottom.setStyle('position', 'absolute');
10576 this.maskEl.bottom.setStyle('z-index', zIndex);
10577 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10578 this.maskEl.bottom.setLeft(0);
10579 this.maskEl.bottom.setTop(box.bottom + this.padding);
10580 this.maskEl.bottom.show();
10582 this.maskEl.right.setStyle('position', 'absolute');
10583 this.maskEl.right.setStyle('z-index', zIndex);
10584 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10585 this.maskEl.right.setLeft(box.right + this.padding);
10586 this.maskEl.right.setTop(box.y - this.padding);
10587 this.maskEl.right.show();
10589 this.toolTip.bindEl = this.target.el;
10591 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10593 var tip = this.target.blankText;
10595 if(this.target.getValue() !== '' ) {
10597 if (this.target.invalidText.length) {
10598 tip = this.target.invalidText;
10599 } else if (this.target.regexText.length){
10600 tip = this.target.regexText;
10604 this.toolTip.show(tip);
10606 this.intervalID = window.setInterval(function() {
10607 Roo.bootstrap.Form.popover.unmask();
10610 window.onwheel = function(){ return false;};
10612 (function(){ this.isMasked = true; }).defer(500, this);
10616 unmask : function()
10618 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10622 this.maskEl.top.setStyle('position', 'absolute');
10623 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10624 this.maskEl.top.hide();
10626 this.maskEl.left.setStyle('position', 'absolute');
10627 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10628 this.maskEl.left.hide();
10630 this.maskEl.bottom.setStyle('position', 'absolute');
10631 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10632 this.maskEl.bottom.hide();
10634 this.maskEl.right.setStyle('position', 'absolute');
10635 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10636 this.maskEl.right.hide();
10638 this.toolTip.hide();
10640 this.toolTip.el.hide();
10642 window.onwheel = function(){ return true;};
10644 if(this.intervalID){
10645 window.clearInterval(this.intervalID);
10646 this.intervalID = false;
10649 this.isMasked = false;
10659 * Ext JS Library 1.1.1
10660 * Copyright(c) 2006-2007, Ext JS, LLC.
10662 * Originally Released Under LGPL - original licence link has changed is not relivant.
10665 * <script type="text/javascript">
10668 * @class Roo.form.VTypes
10669 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10672 Roo.form.VTypes = function(){
10673 // closure these in so they are only created once.
10674 var alpha = /^[a-zA-Z_]+$/;
10675 var alphanum = /^[a-zA-Z0-9_]+$/;
10676 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10677 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10679 // All these messages and functions are configurable
10682 * The function used to validate email addresses
10683 * @param {String} value The email address
10685 'email' : function(v){
10686 return email.test(v);
10689 * The error text to display when the email validation function returns false
10692 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10694 * The keystroke filter mask to be applied on email input
10697 'emailMask' : /[a-z0-9_\.\-@]/i,
10700 * The function used to validate URLs
10701 * @param {String} value The URL
10703 'url' : function(v){
10704 return url.test(v);
10707 * The error text to display when the url validation function returns false
10710 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10713 * The function used to validate alpha values
10714 * @param {String} value The value
10716 'alpha' : function(v){
10717 return alpha.test(v);
10720 * The error text to display when the alpha validation function returns false
10723 'alphaText' : 'This field should only contain letters and _',
10725 * The keystroke filter mask to be applied on alpha input
10728 'alphaMask' : /[a-z_]/i,
10731 * The function used to validate alphanumeric values
10732 * @param {String} value The value
10734 'alphanum' : function(v){
10735 return alphanum.test(v);
10738 * The error text to display when the alphanumeric validation function returns false
10741 'alphanumText' : 'This field should only contain letters, numbers and _',
10743 * The keystroke filter mask to be applied on alphanumeric input
10746 'alphanumMask' : /[a-z0-9_]/i
10756 * @class Roo.bootstrap.Input
10757 * @extends Roo.bootstrap.Component
10758 * Bootstrap Input class
10759 * @cfg {Boolean} disabled is it disabled
10760 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10761 * @cfg {String} name name of the input
10762 * @cfg {string} fieldLabel - the label associated
10763 * @cfg {string} placeholder - placeholder to put in text.
10764 * @cfg {string} before - input group add on before
10765 * @cfg {string} after - input group add on after
10766 * @cfg {string} size - (lg|sm) or leave empty..
10767 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10768 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10769 * @cfg {Number} md colspan out of 12 for computer-sized screens
10770 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10771 * @cfg {string} value default value of the input
10772 * @cfg {Number} labelWidth set the width of label
10773 * @cfg {Number} labellg set the width of label (1-12)
10774 * @cfg {Number} labelmd set the width of label (1-12)
10775 * @cfg {Number} labelsm set the width of label (1-12)
10776 * @cfg {Number} labelxs set the width of label (1-12)
10777 * @cfg {String} labelAlign (top|left)
10778 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10779 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10780 * @cfg {String} indicatorpos (left|right) default left
10781 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10782 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10783 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10785 * @cfg {String} align (left|center|right) Default left
10786 * @cfg {Boolean} forceFeedback (true|false) Default false
10789 * Create a new Input
10790 * @param {Object} config The config object
10793 Roo.bootstrap.Input = function(config){
10795 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10800 * Fires when this field receives input focus.
10801 * @param {Roo.form.Field} this
10806 * Fires when this field loses input focus.
10807 * @param {Roo.form.Field} this
10811 * @event specialkey
10812 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10813 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10814 * @param {Roo.form.Field} this
10815 * @param {Roo.EventObject} e The event object
10820 * Fires just before the field blurs if the field value has changed.
10821 * @param {Roo.form.Field} this
10822 * @param {Mixed} newValue The new value
10823 * @param {Mixed} oldValue The original value
10828 * Fires after the field has been marked as invalid.
10829 * @param {Roo.form.Field} this
10830 * @param {String} msg The validation message
10835 * Fires after the field has been validated with no errors.
10836 * @param {Roo.form.Field} this
10841 * Fires after the key up
10842 * @param {Roo.form.Field} this
10843 * @param {Roo.EventObject} e The event Object
10848 * Fires after the user pastes into input
10849 * @param {Roo.form.Field} this
10850 * @param {Roo.EventObject} e The event Object
10856 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10858 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10859 automatic validation (defaults to "keyup").
10861 validationEvent : "keyup",
10863 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10865 validateOnBlur : true,
10867 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10869 validationDelay : 250,
10871 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10873 focusClass : "x-form-focus", // not needed???
10877 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10879 invalidClass : "has-warning",
10882 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10884 validClass : "has-success",
10887 * @cfg {Boolean} hasFeedback (true|false) default true
10889 hasFeedback : true,
10892 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10894 invalidFeedbackClass : "glyphicon-warning-sign",
10897 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10899 validFeedbackClass : "glyphicon-ok",
10902 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10904 selectOnFocus : false,
10907 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10911 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10916 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10918 disableKeyFilter : false,
10921 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10925 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10929 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10931 blankText : "Please complete this mandatory field",
10934 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10938 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10940 maxLength : Number.MAX_VALUE,
10942 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10944 minLengthText : "The minimum length for this field is {0}",
10946 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10948 maxLengthText : "The maximum length for this field is {0}",
10952 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10953 * If available, this function will be called only after the basic validators all return true, and will be passed the
10954 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10958 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10959 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10960 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10964 * @cfg {String} regexText -- Depricated - use Invalid Text
10969 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10975 autocomplete: false,
10979 inputType : 'text',
10982 placeholder: false,
10987 preventMark: false,
10988 isFormField : true,
10991 labelAlign : false,
10994 formatedValue : false,
10995 forceFeedback : false,
10997 indicatorpos : 'left',
11007 parentLabelAlign : function()
11010 while (parent.parent()) {
11011 parent = parent.parent();
11012 if (typeof(parent.labelAlign) !='undefined') {
11013 return parent.labelAlign;
11020 getAutoCreate : function()
11022 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11028 if(this.inputType != 'hidden'){
11029 cfg.cls = 'form-group' //input-group
11035 type : this.inputType,
11036 value : this.value,
11037 cls : 'form-control',
11038 placeholder : this.placeholder || '',
11039 autocomplete : this.autocomplete || 'new-password'
11041 if (this.inputType == 'file') {
11042 input.style = 'overflow:hidden'; // why not in CSS?
11045 if(this.capture.length){
11046 input.capture = this.capture;
11049 if(this.accept.length){
11050 input.accept = this.accept + "/*";
11054 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11057 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11058 input.maxLength = this.maxLength;
11061 if (this.disabled) {
11062 input.disabled=true;
11065 if (this.readOnly) {
11066 input.readonly=true;
11070 input.name = this.name;
11074 input.cls += ' input-' + this.size;
11078 ['xs','sm','md','lg'].map(function(size){
11079 if (settings[size]) {
11080 cfg.cls += ' col-' + size + '-' + settings[size];
11084 var inputblock = input;
11088 cls: 'glyphicon form-control-feedback'
11091 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11094 cls : 'has-feedback',
11102 if (this.before || this.after) {
11105 cls : 'input-group',
11109 if (this.before && typeof(this.before) == 'string') {
11111 inputblock.cn.push({
11113 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11117 if (this.before && typeof(this.before) == 'object') {
11118 this.before = Roo.factory(this.before);
11120 inputblock.cn.push({
11122 cls : 'roo-input-before input-group-prepend input-group-' +
11123 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11127 inputblock.cn.push(input);
11129 if (this.after && typeof(this.after) == 'string') {
11130 inputblock.cn.push({
11132 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11136 if (this.after && typeof(this.after) == 'object') {
11137 this.after = Roo.factory(this.after);
11139 inputblock.cn.push({
11141 cls : 'roo-input-after input-group-append input-group-' +
11142 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11146 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11147 inputblock.cls += ' has-feedback';
11148 inputblock.cn.push(feedback);
11153 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11154 tooltip : 'This field is required'
11156 if (this.allowBlank ) {
11157 indicator.style = this.allowBlank ? ' display:none' : '';
11159 if (align ==='left' && this.fieldLabel.length) {
11161 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11168 cls : 'control-label col-form-label',
11169 html : this.fieldLabel
11180 var labelCfg = cfg.cn[1];
11181 var contentCfg = cfg.cn[2];
11183 if(this.indicatorpos == 'right'){
11188 cls : 'control-label col-form-label',
11192 html : this.fieldLabel
11206 labelCfg = cfg.cn[0];
11207 contentCfg = cfg.cn[1];
11211 if(this.labelWidth > 12){
11212 labelCfg.style = "width: " + this.labelWidth + 'px';
11215 if(this.labelWidth < 13 && this.labelmd == 0){
11216 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11219 if(this.labellg > 0){
11220 labelCfg.cls += ' col-lg-' + this.labellg;
11221 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11224 if(this.labelmd > 0){
11225 labelCfg.cls += ' col-md-' + this.labelmd;
11226 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11229 if(this.labelsm > 0){
11230 labelCfg.cls += ' col-sm-' + this.labelsm;
11231 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11234 if(this.labelxs > 0){
11235 labelCfg.cls += ' col-xs-' + this.labelxs;
11236 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11240 } else if ( this.fieldLabel.length) {
11247 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11248 tooltip : 'This field is required',
11249 style : this.allowBlank ? ' display:none' : ''
11253 //cls : 'input-group-addon',
11254 html : this.fieldLabel
11262 if(this.indicatorpos == 'right'){
11267 //cls : 'input-group-addon',
11268 html : this.fieldLabel
11273 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11274 tooltip : 'This field is required',
11275 style : this.allowBlank ? ' display:none' : ''
11295 if (this.parentType === 'Navbar' && this.parent().bar) {
11296 cfg.cls += ' navbar-form';
11299 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11300 // on BS4 we do this only if not form
11301 cfg.cls += ' navbar-form';
11309 * return the real input element.
11311 inputEl: function ()
11313 return this.el.select('input.form-control',true).first();
11316 tooltipEl : function()
11318 return this.inputEl();
11321 indicatorEl : function()
11323 if (Roo.bootstrap.version == 4) {
11324 return false; // not enabled in v4 yet.
11327 var indicator = this.el.select('i.roo-required-indicator',true).first();
11337 setDisabled : function(v)
11339 var i = this.inputEl().dom;
11341 i.removeAttribute('disabled');
11345 i.setAttribute('disabled','true');
11347 initEvents : function()
11350 this.inputEl().on("keydown" , this.fireKey, this);
11351 this.inputEl().on("focus", this.onFocus, this);
11352 this.inputEl().on("blur", this.onBlur, this);
11354 this.inputEl().relayEvent('keyup', this);
11355 this.inputEl().relayEvent('paste', this);
11357 this.indicator = this.indicatorEl();
11359 if(this.indicator){
11360 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11363 // reference to original value for reset
11364 this.originalValue = this.getValue();
11365 //Roo.form.TextField.superclass.initEvents.call(this);
11366 if(this.validationEvent == 'keyup'){
11367 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11368 this.inputEl().on('keyup', this.filterValidation, this);
11370 else if(this.validationEvent !== false){
11371 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11374 if(this.selectOnFocus){
11375 this.on("focus", this.preFocus, this);
11378 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11379 this.inputEl().on("keypress", this.filterKeys, this);
11381 this.inputEl().relayEvent('keypress', this);
11384 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11385 this.el.on("click", this.autoSize, this);
11388 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11389 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11392 if (typeof(this.before) == 'object') {
11393 this.before.render(this.el.select('.roo-input-before',true).first());
11395 if (typeof(this.after) == 'object') {
11396 this.after.render(this.el.select('.roo-input-after',true).first());
11399 this.inputEl().on('change', this.onChange, this);
11402 filterValidation : function(e){
11403 if(!e.isNavKeyPress()){
11404 this.validationTask.delay(this.validationDelay);
11408 * Validates the field value
11409 * @return {Boolean} True if the value is valid, else false
11411 validate : function(){
11412 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11413 if(this.disabled || this.validateValue(this.getRawValue())){
11418 this.markInvalid();
11424 * Validates a value according to the field's validation rules and marks the field as invalid
11425 * if the validation fails
11426 * @param {Mixed} value The value to validate
11427 * @return {Boolean} True if the value is valid, else false
11429 validateValue : function(value)
11431 if(this.getVisibilityEl().hasClass('hidden')){
11435 if(value.length < 1) { // if it's blank
11436 if(this.allowBlank){
11442 if(value.length < this.minLength){
11445 if(value.length > this.maxLength){
11449 var vt = Roo.form.VTypes;
11450 if(!vt[this.vtype](value, this)){
11454 if(typeof this.validator == "function"){
11455 var msg = this.validator(value);
11459 if (typeof(msg) == 'string') {
11460 this.invalidText = msg;
11464 if(this.regex && !this.regex.test(value)){
11472 fireKey : function(e){
11473 //Roo.log('field ' + e.getKey());
11474 if(e.isNavKeyPress()){
11475 this.fireEvent("specialkey", this, e);
11478 focus : function (selectText){
11480 this.inputEl().focus();
11481 if(selectText === true){
11482 this.inputEl().dom.select();
11488 onFocus : function(){
11489 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11490 // this.el.addClass(this.focusClass);
11492 if(!this.hasFocus){
11493 this.hasFocus = true;
11494 this.startValue = this.getValue();
11495 this.fireEvent("focus", this);
11499 beforeBlur : Roo.emptyFn,
11503 onBlur : function(){
11505 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11506 //this.el.removeClass(this.focusClass);
11508 this.hasFocus = false;
11509 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11512 var v = this.getValue();
11513 if(String(v) !== String(this.startValue)){
11514 this.fireEvent('change', this, v, this.startValue);
11516 this.fireEvent("blur", this);
11519 onChange : function(e)
11521 var v = this.getValue();
11522 if(String(v) !== String(this.startValue)){
11523 this.fireEvent('change', this, v, this.startValue);
11529 * Resets the current field value to the originally loaded value and clears any validation messages
11531 reset : function(){
11532 this.setValue(this.originalValue);
11536 * Returns the name of the field
11537 * @return {Mixed} name The name field
11539 getName: function(){
11543 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11544 * @return {Mixed} value The field value
11546 getValue : function(){
11548 var v = this.inputEl().getValue();
11553 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11554 * @return {Mixed} value The field value
11556 getRawValue : function(){
11557 var v = this.inputEl().getValue();
11563 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11564 * @param {Mixed} value The value to set
11566 setRawValue : function(v){
11567 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11570 selectText : function(start, end){
11571 var v = this.getRawValue();
11573 start = start === undefined ? 0 : start;
11574 end = end === undefined ? v.length : end;
11575 var d = this.inputEl().dom;
11576 if(d.setSelectionRange){
11577 d.setSelectionRange(start, end);
11578 }else if(d.createTextRange){
11579 var range = d.createTextRange();
11580 range.moveStart("character", start);
11581 range.moveEnd("character", v.length-end);
11588 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11589 * @param {Mixed} value The value to set
11591 setValue : function(v){
11594 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11600 processValue : function(value){
11601 if(this.stripCharsRe){
11602 var newValue = value.replace(this.stripCharsRe, '');
11603 if(newValue !== value){
11604 this.setRawValue(newValue);
11611 preFocus : function(){
11613 if(this.selectOnFocus){
11614 this.inputEl().dom.select();
11617 filterKeys : function(e){
11618 var k = e.getKey();
11619 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11622 var c = e.getCharCode(), cc = String.fromCharCode(c);
11623 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11626 if(!this.maskRe.test(cc)){
11631 * Clear any invalid styles/messages for this field
11633 clearInvalid : function(){
11635 if(!this.el || this.preventMark){ // not rendered
11640 this.el.removeClass([this.invalidClass, 'is-invalid']);
11642 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11644 var feedback = this.el.select('.form-control-feedback', true).first();
11647 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11652 if(this.indicator){
11653 this.indicator.removeClass('visible');
11654 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11657 this.fireEvent('valid', this);
11661 * Mark this field as valid
11663 markValid : function()
11665 if(!this.el || this.preventMark){ // not rendered...
11669 this.el.removeClass([this.invalidClass, this.validClass]);
11670 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11672 var feedback = this.el.select('.form-control-feedback', true).first();
11675 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11678 if(this.indicator){
11679 this.indicator.removeClass('visible');
11680 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11688 if(this.allowBlank && !this.getRawValue().length){
11691 if (Roo.bootstrap.version == 3) {
11692 this.el.addClass(this.validClass);
11694 this.inputEl().addClass('is-valid');
11697 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11699 var feedback = this.el.select('.form-control-feedback', true).first();
11702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11703 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11708 this.fireEvent('valid', this);
11712 * Mark this field as invalid
11713 * @param {String} msg The validation message
11715 markInvalid : function(msg)
11717 if(!this.el || this.preventMark){ // not rendered
11721 this.el.removeClass([this.invalidClass, this.validClass]);
11722 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11724 var feedback = this.el.select('.form-control-feedback', true).first();
11727 this.el.select('.form-control-feedback', true).first().removeClass(
11728 [this.invalidFeedbackClass, this.validFeedbackClass]);
11735 if(this.allowBlank && !this.getRawValue().length){
11739 if(this.indicator){
11740 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11741 this.indicator.addClass('visible');
11743 if (Roo.bootstrap.version == 3) {
11744 this.el.addClass(this.invalidClass);
11746 this.inputEl().addClass('is-invalid');
11751 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11753 var feedback = this.el.select('.form-control-feedback', true).first();
11756 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11758 if(this.getValue().length || this.forceFeedback){
11759 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11766 this.fireEvent('invalid', this, msg);
11769 SafariOnKeyDown : function(event)
11771 // this is a workaround for a password hang bug on chrome/ webkit.
11772 if (this.inputEl().dom.type != 'password') {
11776 var isSelectAll = false;
11778 if(this.inputEl().dom.selectionEnd > 0){
11779 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11781 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11782 event.preventDefault();
11787 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11789 event.preventDefault();
11790 // this is very hacky as keydown always get's upper case.
11792 var cc = String.fromCharCode(event.getCharCode());
11793 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11797 adjustWidth : function(tag, w){
11798 tag = tag.toLowerCase();
11799 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11800 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11801 if(tag == 'input'){
11804 if(tag == 'textarea'){
11807 }else if(Roo.isOpera){
11808 if(tag == 'input'){
11811 if(tag == 'textarea'){
11819 setFieldLabel : function(v)
11821 if(!this.rendered){
11825 if(this.indicatorEl()){
11826 var ar = this.el.select('label > span',true);
11828 if (ar.elements.length) {
11829 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11830 this.fieldLabel = v;
11834 var br = this.el.select('label',true);
11836 if(br.elements.length) {
11837 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11838 this.fieldLabel = v;
11842 Roo.log('Cannot Found any of label > span || label in input');
11846 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11847 this.fieldLabel = v;
11862 * @class Roo.bootstrap.TextArea
11863 * @extends Roo.bootstrap.Input
11864 * Bootstrap TextArea class
11865 * @cfg {Number} cols Specifies the visible width of a text area
11866 * @cfg {Number} rows Specifies the visible number of lines in a text area
11867 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11868 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11869 * @cfg {string} html text
11872 * Create a new TextArea
11873 * @param {Object} config The config object
11876 Roo.bootstrap.TextArea = function(config){
11877 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11881 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11891 getAutoCreate : function(){
11893 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11899 if(this.inputType != 'hidden'){
11900 cfg.cls = 'form-group' //input-group
11908 value : this.value || '',
11909 html: this.html || '',
11910 cls : 'form-control',
11911 placeholder : this.placeholder || ''
11915 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11916 input.maxLength = this.maxLength;
11920 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11924 input.cols = this.cols;
11927 if (this.readOnly) {
11928 input.readonly = true;
11932 input.name = this.name;
11936 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11940 ['xs','sm','md','lg'].map(function(size){
11941 if (settings[size]) {
11942 cfg.cls += ' col-' + size + '-' + settings[size];
11946 var inputblock = input;
11948 if(this.hasFeedback && !this.allowBlank){
11952 cls: 'glyphicon form-control-feedback'
11956 cls : 'has-feedback',
11965 if (this.before || this.after) {
11968 cls : 'input-group',
11972 inputblock.cn.push({
11974 cls : 'input-group-addon',
11979 inputblock.cn.push(input);
11981 if(this.hasFeedback && !this.allowBlank){
11982 inputblock.cls += ' has-feedback';
11983 inputblock.cn.push(feedback);
11987 inputblock.cn.push({
11989 cls : 'input-group-addon',
11996 if (align ==='left' && this.fieldLabel.length) {
12001 cls : 'control-label',
12002 html : this.fieldLabel
12013 if(this.labelWidth > 12){
12014 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12017 if(this.labelWidth < 13 && this.labelmd == 0){
12018 this.labelmd = this.labelWidth;
12021 if(this.labellg > 0){
12022 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12023 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12026 if(this.labelmd > 0){
12027 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12028 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12031 if(this.labelsm > 0){
12032 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12033 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12036 if(this.labelxs > 0){
12037 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12038 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12041 } else if ( this.fieldLabel.length) {
12046 //cls : 'input-group-addon',
12047 html : this.fieldLabel
12065 if (this.disabled) {
12066 input.disabled=true;
12073 * return the real textarea element.
12075 inputEl: function ()
12077 return this.el.select('textarea.form-control',true).first();
12081 * Clear any invalid styles/messages for this field
12083 clearInvalid : function()
12086 if(!this.el || this.preventMark){ // not rendered
12090 var label = this.el.select('label', true).first();
12091 var icon = this.el.select('i.fa-star', true).first();
12096 this.el.removeClass( this.validClass);
12097 this.inputEl().removeClass('is-invalid');
12099 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12101 var feedback = this.el.select('.form-control-feedback', true).first();
12104 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12109 this.fireEvent('valid', this);
12113 * Mark this field as valid
12115 markValid : function()
12117 if(!this.el || this.preventMark){ // not rendered
12121 this.el.removeClass([this.invalidClass, this.validClass]);
12122 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12124 var feedback = this.el.select('.form-control-feedback', true).first();
12127 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12130 if(this.disabled || this.allowBlank){
12134 var label = this.el.select('label', true).first();
12135 var icon = this.el.select('i.fa-star', true).first();
12140 if (Roo.bootstrap.version == 3) {
12141 this.el.addClass(this.validClass);
12143 this.inputEl().addClass('is-valid');
12147 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12149 var feedback = this.el.select('.form-control-feedback', true).first();
12152 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12153 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12158 this.fireEvent('valid', this);
12162 * Mark this field as invalid
12163 * @param {String} msg The validation message
12165 markInvalid : function(msg)
12167 if(!this.el || this.preventMark){ // not rendered
12171 this.el.removeClass([this.invalidClass, this.validClass]);
12172 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12174 var feedback = this.el.select('.form-control-feedback', true).first();
12177 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12180 if(this.disabled || this.allowBlank){
12184 var label = this.el.select('label', true).first();
12185 var icon = this.el.select('i.fa-star', true).first();
12187 if(!this.getValue().length && label && !icon){
12188 this.el.createChild({
12190 cls : 'text-danger fa fa-lg fa-star',
12191 tooltip : 'This field is required',
12192 style : 'margin-right:5px;'
12196 if (Roo.bootstrap.version == 3) {
12197 this.el.addClass(this.invalidClass);
12199 this.inputEl().addClass('is-invalid');
12202 // fixme ... this may be depricated need to test..
12203 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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]);
12210 if(this.getValue().length || this.forceFeedback){
12211 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12218 this.fireEvent('invalid', this, msg);
12226 * trigger field - base class for combo..
12231 * @class Roo.bootstrap.TriggerField
12232 * @extends Roo.bootstrap.Input
12233 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12234 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12235 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12236 * for which you can provide a custom implementation. For example:
12238 var trigger = new Roo.bootstrap.TriggerField();
12239 trigger.onTriggerClick = myTriggerFn;
12240 trigger.applyTo('my-field');
12243 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12244 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12245 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12246 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12247 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12250 * Create a new TriggerField.
12251 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12252 * to the base TextField)
12254 Roo.bootstrap.TriggerField = function(config){
12255 this.mimicing = false;
12256 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12259 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12261 * @cfg {String} triggerClass A CSS class to apply to the trigger
12264 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12269 * @cfg {Boolean} removable (true|false) special filter default false
12273 /** @cfg {Boolean} grow @hide */
12274 /** @cfg {Number} growMin @hide */
12275 /** @cfg {Number} growMax @hide */
12281 autoSize: Roo.emptyFn,
12285 deferHeight : true,
12288 actionMode : 'wrap',
12293 getAutoCreate : function(){
12295 var align = this.labelAlign || this.parentLabelAlign();
12300 cls: 'form-group' //input-group
12307 type : this.inputType,
12308 cls : 'form-control',
12309 autocomplete: 'new-password',
12310 placeholder : this.placeholder || ''
12314 input.name = this.name;
12317 input.cls += ' input-' + this.size;
12320 if (this.disabled) {
12321 input.disabled=true;
12324 var inputblock = input;
12326 if(this.hasFeedback && !this.allowBlank){
12330 cls: 'glyphicon form-control-feedback'
12333 if(this.removable && !this.editable ){
12335 cls : 'has-feedback',
12341 cls : 'roo-combo-removable-btn close'
12348 cls : 'has-feedback',
12357 if(this.removable && !this.editable ){
12359 cls : 'roo-removable',
12365 cls : 'roo-combo-removable-btn close'
12372 if (this.before || this.after) {
12375 cls : 'input-group',
12379 inputblock.cn.push({
12381 cls : 'input-group-addon input-group-prepend input-group-text',
12386 inputblock.cn.push(input);
12388 if(this.hasFeedback && !this.allowBlank){
12389 inputblock.cls += ' has-feedback';
12390 inputblock.cn.push(feedback);
12394 inputblock.cn.push({
12396 cls : 'input-group-addon input-group-append input-group-text',
12405 var ibwrap = inputblock;
12410 cls: 'roo-select2-choices',
12414 cls: 'roo-select2-search-field',
12426 cls: 'roo-select2-container input-group',
12431 cls: 'form-hidden-field'
12437 if(!this.multiple && this.showToggleBtn){
12443 if (this.caret != false) {
12446 cls: 'fa fa-' + this.caret
12453 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12455 Roo.bootstrap.version == 3 ? caret : '',
12458 cls: 'combobox-clear',
12472 combobox.cls += ' roo-select2-container-multi';
12476 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12477 tooltip : 'This field is required'
12479 if (Roo.bootstrap.version == 4) {
12482 style : 'display:none'
12487 if (align ==='left' && this.fieldLabel.length) {
12489 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12496 cls : 'control-label',
12497 html : this.fieldLabel
12509 var labelCfg = cfg.cn[1];
12510 var contentCfg = cfg.cn[2];
12512 if(this.indicatorpos == 'right'){
12517 cls : 'control-label',
12521 html : this.fieldLabel
12535 labelCfg = cfg.cn[0];
12536 contentCfg = cfg.cn[1];
12539 if(this.labelWidth > 12){
12540 labelCfg.style = "width: " + this.labelWidth + 'px';
12543 if(this.labelWidth < 13 && this.labelmd == 0){
12544 this.labelmd = this.labelWidth;
12547 if(this.labellg > 0){
12548 labelCfg.cls += ' col-lg-' + this.labellg;
12549 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12552 if(this.labelmd > 0){
12553 labelCfg.cls += ' col-md-' + this.labelmd;
12554 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12557 if(this.labelsm > 0){
12558 labelCfg.cls += ' col-sm-' + this.labelsm;
12559 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12562 if(this.labelxs > 0){
12563 labelCfg.cls += ' col-xs-' + this.labelxs;
12564 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12567 } else if ( this.fieldLabel.length) {
12568 // Roo.log(" label");
12573 //cls : 'input-group-addon',
12574 html : this.fieldLabel
12582 if(this.indicatorpos == 'right'){
12590 html : this.fieldLabel
12604 // Roo.log(" no label && no align");
12611 ['xs','sm','md','lg'].map(function(size){
12612 if (settings[size]) {
12613 cfg.cls += ' col-' + size + '-' + settings[size];
12624 onResize : function(w, h){
12625 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12626 // if(typeof w == 'number'){
12627 // var x = w - this.trigger.getWidth();
12628 // this.inputEl().setWidth(this.adjustWidth('input', x));
12629 // this.trigger.setStyle('left', x+'px');
12634 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12637 getResizeEl : function(){
12638 return this.inputEl();
12642 getPositionEl : function(){
12643 return this.inputEl();
12647 alignErrorIcon : function(){
12648 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12652 initEvents : function(){
12656 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12657 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12658 if(!this.multiple && this.showToggleBtn){
12659 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12660 if(this.hideTrigger){
12661 this.trigger.setDisplayed(false);
12663 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12667 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12670 if(this.removable && !this.editable && !this.tickable){
12671 var close = this.closeTriggerEl();
12674 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12675 close.on('click', this.removeBtnClick, this, close);
12679 //this.trigger.addClassOnOver('x-form-trigger-over');
12680 //this.trigger.addClassOnClick('x-form-trigger-click');
12683 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12687 closeTriggerEl : function()
12689 var close = this.el.select('.roo-combo-removable-btn', true).first();
12690 return close ? close : false;
12693 removeBtnClick : function(e, h, el)
12695 e.preventDefault();
12697 if(this.fireEvent("remove", this) !== false){
12699 this.fireEvent("afterremove", this)
12703 createList : function()
12705 this.list = Roo.get(document.body).createChild({
12706 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12707 cls: 'typeahead typeahead-long dropdown-menu shadow',
12708 style: 'display:none'
12711 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12716 initTrigger : function(){
12721 onDestroy : function(){
12723 this.trigger.removeAllListeners();
12724 // this.trigger.remove();
12727 // this.wrap.remove();
12729 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12733 onFocus : function(){
12734 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12736 if(!this.mimicing){
12737 this.wrap.addClass('x-trigger-wrap-focus');
12738 this.mimicing = true;
12739 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12740 if(this.monitorTab){
12741 this.el.on("keydown", this.checkTab, this);
12748 checkTab : function(e){
12749 if(e.getKey() == e.TAB){
12750 this.triggerBlur();
12755 onBlur : function(){
12760 mimicBlur : function(e, t){
12762 if(!this.wrap.contains(t) && this.validateBlur()){
12763 this.triggerBlur();
12769 triggerBlur : function(){
12770 this.mimicing = false;
12771 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12772 if(this.monitorTab){
12773 this.el.un("keydown", this.checkTab, this);
12775 //this.wrap.removeClass('x-trigger-wrap-focus');
12776 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12780 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12781 validateBlur : function(e, t){
12786 onDisable : function(){
12787 this.inputEl().dom.disabled = true;
12788 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12790 // this.wrap.addClass('x-item-disabled');
12795 onEnable : function(){
12796 this.inputEl().dom.disabled = false;
12797 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12799 // this.el.removeClass('x-item-disabled');
12804 onShow : function(){
12805 var ae = this.getActionEl();
12808 ae.dom.style.display = '';
12809 ae.dom.style.visibility = 'visible';
12815 onHide : function(){
12816 var ae = this.getActionEl();
12817 ae.dom.style.display = 'none';
12821 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12822 * by an implementing function.
12824 * @param {EventObject} e
12826 onTriggerClick : Roo.emptyFn
12834 * @class Roo.bootstrap.CardUploader
12835 * @extends Roo.bootstrap.Button
12836 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12837 * @cfg {Number} errorTimeout default 3000
12838 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12839 * @cfg {Array} html The button text.
12843 * Create a new CardUploader
12844 * @param {Object} config The config object
12847 Roo.bootstrap.CardUploader = function(config){
12851 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12854 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12862 * When a image is clicked on - and needs to display a slideshow or similar..
12863 * @param {Roo.bootstrap.Card} this
12864 * @param {Object} The image information data
12870 * When a the download link is clicked
12871 * @param {Roo.bootstrap.Card} this
12872 * @param {Object} The image information data contains
12879 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12882 errorTimeout : 3000,
12886 fileCollection : false,
12889 getAutoCreate : function()
12893 cls :'form-group' ,
12898 //cls : 'input-group-addon',
12899 html : this.fieldLabel
12907 value : this.value,
12908 cls : 'd-none form-control'
12913 multiple : 'multiple',
12915 cls : 'd-none roo-card-upload-selector'
12919 cls : 'roo-card-uploader-button-container w-100 mb-2'
12922 cls : 'card-columns roo-card-uploader-container'
12932 getChildContainer : function() /// what children are added to.
12934 return this.containerEl;
12937 getButtonContainer : function() /// what children are added to.
12939 return this.el.select(".roo-card-uploader-button-container").first();
12942 initEvents : function()
12945 Roo.bootstrap.Input.prototype.initEvents.call(this);
12949 xns: Roo.bootstrap,
12952 container_method : 'getButtonContainer' ,
12953 html : this.html, // fix changable?
12956 'click' : function(btn, e) {
12965 this.urlAPI = (window.createObjectURL && window) ||
12966 (window.URL && URL.revokeObjectURL && URL) ||
12967 (window.webkitURL && webkitURL);
12972 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12974 this.selectorEl.on('change', this.onFileSelected, this);
12977 this.images.forEach(function(img) {
12980 this.images = false;
12982 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12988 onClick : function(e)
12990 e.preventDefault();
12992 this.selectorEl.dom.click();
12996 onFileSelected : function(e)
12998 e.preventDefault();
13000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13004 Roo.each(this.selectorEl.dom.files, function(file){
13005 this.addFile(file);
13014 addFile : function(file)
13017 if(typeof(file) === 'string'){
13018 throw "Add file by name?"; // should not happen
13022 if(!file || !this.urlAPI){
13032 var url = _this.urlAPI.createObjectURL( file);
13035 id : Roo.bootstrap.CardUploader.ID--,
13036 is_uploaded : false,
13040 mimetype : file.type,
13048 * addCard - add an Attachment to the uploader
13049 * @param data - the data about the image to upload
13053 title : "Title of file",
13054 is_uploaded : false,
13055 src : "http://.....",
13056 srcfile : { the File upload object },
13057 mimetype : file.type,
13060 .. any other data...
13066 addCard : function (data)
13068 // hidden input element?
13069 // if the file is not an image...
13070 //then we need to use something other that and header_image
13075 xns : Roo.bootstrap,
13076 xtype : 'CardFooter',
13079 xns : Roo.bootstrap,
13085 xns : Roo.bootstrap,
13087 html : String.format("<small>{0}</small>", data.title),
13088 cls : 'col-10 text-left',
13093 click : function() {
13095 t.fireEvent( "download", t, data );
13101 xns : Roo.bootstrap,
13103 style: 'max-height: 28px; ',
13109 click : function() {
13110 t.removeCard(data.id)
13122 var cn = this.addxtype(
13125 xns : Roo.bootstrap,
13128 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13129 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13130 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13135 initEvents : function() {
13136 Roo.bootstrap.Card.prototype.initEvents.call(this);
13138 this.imgEl = this.el.select('.card-img-top').first();
13140 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13141 this.imgEl.set({ 'pointer' : 'cursor' });
13144 this.getCardFooter().addClass('p-1');
13151 // dont' really need ot update items.
13152 // this.items.push(cn);
13153 this.fileCollection.add(cn);
13155 if (!data.srcfile) {
13156 this.updateInput();
13161 var reader = new FileReader();
13162 reader.addEventListener("load", function() {
13163 data.srcdata = reader.result;
13166 reader.readAsDataURL(data.srcfile);
13171 removeCard : function(id)
13174 var card = this.fileCollection.get(id);
13175 card.data.is_deleted = 1;
13176 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13177 //this.fileCollection.remove(card);
13178 //this.items = this.items.filter(function(e) { return e != card });
13179 // dont' really need ot update items.
13180 card.el.dom.parentNode.removeChild(card.el.dom);
13181 this.updateInput();
13187 this.fileCollection.each(function(card) {
13188 if (card.el.dom && card.el.dom.parentNode) {
13189 card.el.dom.parentNode.removeChild(card.el.dom);
13192 this.fileCollection.clear();
13193 this.updateInput();
13196 updateInput : function()
13199 this.fileCollection.each(function(e) {
13203 this.inputEl().dom.value = JSON.stringify(data);
13213 Roo.bootstrap.CardUploader.ID = -1;/*
13215 * Ext JS Library 1.1.1
13216 * Copyright(c) 2006-2007, Ext JS, LLC.
13218 * Originally Released Under LGPL - original licence link has changed is not relivant.
13221 * <script type="text/javascript">
13226 * @class Roo.data.SortTypes
13228 * Defines the default sorting (casting?) comparison functions used when sorting data.
13230 Roo.data.SortTypes = {
13232 * Default sort that does nothing
13233 * @param {Mixed} s The value being converted
13234 * @return {Mixed} The comparison value
13236 none : function(s){
13241 * The regular expression used to strip tags
13245 stripTagsRE : /<\/?[^>]+>/gi,
13248 * Strips all HTML tags to sort on text only
13249 * @param {Mixed} s The value being converted
13250 * @return {String} The comparison value
13252 asText : function(s){
13253 return String(s).replace(this.stripTagsRE, "");
13257 * Strips all HTML tags to sort on text only - Case insensitive
13258 * @param {Mixed} s The value being converted
13259 * @return {String} The comparison value
13261 asUCText : function(s){
13262 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13266 * Case insensitive string
13267 * @param {Mixed} s The value being converted
13268 * @return {String} The comparison value
13270 asUCString : function(s) {
13271 return String(s).toUpperCase();
13276 * @param {Mixed} s The value being converted
13277 * @return {Number} The comparison value
13279 asDate : function(s) {
13283 if(s instanceof Date){
13284 return s.getTime();
13286 return Date.parse(String(s));
13291 * @param {Mixed} s The value being converted
13292 * @return {Float} The comparison value
13294 asFloat : function(s) {
13295 var val = parseFloat(String(s).replace(/,/g, ""));
13304 * @param {Mixed} s The value being converted
13305 * @return {Number} The comparison value
13307 asInt : function(s) {
13308 var val = parseInt(String(s).replace(/,/g, ""));
13316 * Ext JS Library 1.1.1
13317 * Copyright(c) 2006-2007, Ext JS, LLC.
13319 * Originally Released Under LGPL - original licence link has changed is not relivant.
13322 * <script type="text/javascript">
13326 * @class Roo.data.Record
13327 * Instances of this class encapsulate both record <em>definition</em> information, and record
13328 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13329 * to access Records cached in an {@link Roo.data.Store} object.<br>
13331 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13332 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13335 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13337 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13338 * {@link #create}. The parameters are the same.
13339 * @param {Array} data An associative Array of data values keyed by the field name.
13340 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13341 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13342 * not specified an integer id is generated.
13344 Roo.data.Record = function(data, id){
13345 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13350 * Generate a constructor for a specific record layout.
13351 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13352 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13353 * Each field definition object may contain the following properties: <ul>
13354 * <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,
13355 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13356 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13357 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13358 * is being used, then this is a string containing the javascript expression to reference the data relative to
13359 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13360 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13361 * this may be omitted.</p></li>
13362 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13363 * <ul><li>auto (Default, implies no conversion)</li>
13368 * <li>date</li></ul></p></li>
13369 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13370 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13371 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13372 * by the Reader into an object that will be stored in the Record. It is passed the
13373 * following parameters:<ul>
13374 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13376 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13378 * <br>usage:<br><pre><code>
13379 var TopicRecord = Roo.data.Record.create(
13380 {name: 'title', mapping: 'topic_title'},
13381 {name: 'author', mapping: 'username'},
13382 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13383 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13384 {name: 'lastPoster', mapping: 'user2'},
13385 {name: 'excerpt', mapping: 'post_text'}
13388 var myNewRecord = new TopicRecord({
13389 title: 'Do my job please',
13392 lastPost: new Date(),
13393 lastPoster: 'Animal',
13394 excerpt: 'No way dude!'
13396 myStore.add(myNewRecord);
13401 Roo.data.Record.create = function(o){
13402 var f = function(){
13403 f.superclass.constructor.apply(this, arguments);
13405 Roo.extend(f, Roo.data.Record);
13406 var p = f.prototype;
13407 p.fields = new Roo.util.MixedCollection(false, function(field){
13410 for(var i = 0, len = o.length; i < len; i++){
13411 p.fields.add(new Roo.data.Field(o[i]));
13413 f.getField = function(name){
13414 return p.fields.get(name);
13419 Roo.data.Record.AUTO_ID = 1000;
13420 Roo.data.Record.EDIT = 'edit';
13421 Roo.data.Record.REJECT = 'reject';
13422 Roo.data.Record.COMMIT = 'commit';
13424 Roo.data.Record.prototype = {
13426 * Readonly flag - true if this record has been modified.
13435 join : function(store){
13436 this.store = store;
13440 * Set the named field to the specified value.
13441 * @param {String} name The name of the field to set.
13442 * @param {Object} value The value to set the field to.
13444 set : function(name, value){
13445 if(this.data[name] == value){
13449 if(!this.modified){
13450 this.modified = {};
13452 if(typeof this.modified[name] == 'undefined'){
13453 this.modified[name] = this.data[name];
13455 this.data[name] = value;
13456 if(!this.editing && this.store){
13457 this.store.afterEdit(this);
13462 * Get the value of the named field.
13463 * @param {String} name The name of the field to get the value of.
13464 * @return {Object} The value of the field.
13466 get : function(name){
13467 return this.data[name];
13471 beginEdit : function(){
13472 this.editing = true;
13473 this.modified = {};
13477 cancelEdit : function(){
13478 this.editing = false;
13479 delete this.modified;
13483 endEdit : function(){
13484 this.editing = false;
13485 if(this.dirty && this.store){
13486 this.store.afterEdit(this);
13491 * Usually called by the {@link Roo.data.Store} which owns the Record.
13492 * Rejects all changes made to the Record since either creation, or the last commit operation.
13493 * Modified fields are reverted to their original values.
13495 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13496 * of reject operations.
13498 reject : function(){
13499 var m = this.modified;
13501 if(typeof m[n] != "function"){
13502 this.data[n] = m[n];
13505 this.dirty = false;
13506 delete this.modified;
13507 this.editing = false;
13509 this.store.afterReject(this);
13514 * Usually called by the {@link Roo.data.Store} which owns the Record.
13515 * Commits all changes made to the Record since either creation, or the last commit operation.
13517 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13518 * of commit operations.
13520 commit : function(){
13521 this.dirty = false;
13522 delete this.modified;
13523 this.editing = false;
13525 this.store.afterCommit(this);
13530 hasError : function(){
13531 return this.error != null;
13535 clearError : function(){
13540 * Creates a copy of this record.
13541 * @param {String} id (optional) A new record id if you don't want to use this record's id
13544 copy : function(newId) {
13545 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13549 * Ext JS Library 1.1.1
13550 * Copyright(c) 2006-2007, Ext JS, LLC.
13552 * Originally Released Under LGPL - original licence link has changed is not relivant.
13555 * <script type="text/javascript">
13561 * @class Roo.data.Store
13562 * @extends Roo.util.Observable
13563 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13564 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13566 * 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
13567 * has no knowledge of the format of the data returned by the Proxy.<br>
13569 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13570 * instances from the data object. These records are cached and made available through accessor functions.
13572 * Creates a new Store.
13573 * @param {Object} config A config object containing the objects needed for the Store to access data,
13574 * and read the data into Records.
13576 Roo.data.Store = function(config){
13577 this.data = new Roo.util.MixedCollection(false);
13578 this.data.getKey = function(o){
13581 this.baseParams = {};
13583 this.paramNames = {
13588 "multisort" : "_multisort"
13591 if(config && config.data){
13592 this.inlineData = config.data;
13593 delete config.data;
13596 Roo.apply(this, config);
13598 if(this.reader){ // reader passed
13599 this.reader = Roo.factory(this.reader, Roo.data);
13600 this.reader.xmodule = this.xmodule || false;
13601 if(!this.recordType){
13602 this.recordType = this.reader.recordType;
13604 if(this.reader.onMetaChange){
13605 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13609 if(this.recordType){
13610 this.fields = this.recordType.prototype.fields;
13612 this.modified = [];
13616 * @event datachanged
13617 * Fires when the data cache has changed, and a widget which is using this Store
13618 * as a Record cache should refresh its view.
13619 * @param {Store} this
13621 datachanged : true,
13623 * @event metachange
13624 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13625 * @param {Store} this
13626 * @param {Object} meta The JSON metadata
13631 * Fires when Records have been added to the Store
13632 * @param {Store} this
13633 * @param {Roo.data.Record[]} records The array of Records added
13634 * @param {Number} index The index at which the record(s) were added
13639 * Fires when a Record has been removed from the Store
13640 * @param {Store} this
13641 * @param {Roo.data.Record} record The Record that was removed
13642 * @param {Number} index The index at which the record was removed
13647 * Fires when a Record has been updated
13648 * @param {Store} this
13649 * @param {Roo.data.Record} record The Record that was updated
13650 * @param {String} operation The update operation being performed. Value may be one of:
13652 Roo.data.Record.EDIT
13653 Roo.data.Record.REJECT
13654 Roo.data.Record.COMMIT
13660 * Fires when the data cache has been cleared.
13661 * @param {Store} this
13665 * @event beforeload
13666 * Fires before a request is made for a new data object. If the beforeload handler returns false
13667 * the load action will be canceled.
13668 * @param {Store} this
13669 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13673 * @event beforeloadadd
13674 * Fires after a new set of Records has been loaded.
13675 * @param {Store} this
13676 * @param {Roo.data.Record[]} records The Records that were loaded
13677 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13679 beforeloadadd : true,
13682 * Fires after a new set of Records has been loaded, before they are added to the store.
13683 * @param {Store} this
13684 * @param {Roo.data.Record[]} records The Records that were loaded
13685 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13686 * @params {Object} return from reader
13690 * @event loadexception
13691 * Fires if an exception occurs in the Proxy during loading.
13692 * Called with the signature of the Proxy's "loadexception" event.
13693 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13696 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13697 * @param {Object} load options
13698 * @param {Object} jsonData from your request (normally this contains the Exception)
13700 loadexception : true
13704 this.proxy = Roo.factory(this.proxy, Roo.data);
13705 this.proxy.xmodule = this.xmodule || false;
13706 this.relayEvents(this.proxy, ["loadexception"]);
13708 this.sortToggle = {};
13709 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13711 Roo.data.Store.superclass.constructor.call(this);
13713 if(this.inlineData){
13714 this.loadData(this.inlineData);
13715 delete this.inlineData;
13719 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13721 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13722 * without a remote query - used by combo/forms at present.
13726 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13729 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13732 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13733 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13736 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13737 * on any HTTP request
13740 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13743 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13747 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13748 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13750 remoteSort : false,
13753 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13754 * loaded or when a record is removed. (defaults to false).
13756 pruneModifiedRecords : false,
13759 lastOptions : null,
13762 * Add Records to the Store and fires the add event.
13763 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13765 add : function(records){
13766 records = [].concat(records);
13767 for(var i = 0, len = records.length; i < len; i++){
13768 records[i].join(this);
13770 var index = this.data.length;
13771 this.data.addAll(records);
13772 this.fireEvent("add", this, records, index);
13776 * Remove a Record from the Store and fires the remove event.
13777 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13779 remove : function(record){
13780 var index = this.data.indexOf(record);
13781 this.data.removeAt(index);
13783 if(this.pruneModifiedRecords){
13784 this.modified.remove(record);
13786 this.fireEvent("remove", this, record, index);
13790 * Remove all Records from the Store and fires the clear event.
13792 removeAll : function(){
13794 if(this.pruneModifiedRecords){
13795 this.modified = [];
13797 this.fireEvent("clear", this);
13801 * Inserts Records to the Store at the given index and fires the add event.
13802 * @param {Number} index The start index at which to insert the passed Records.
13803 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13805 insert : function(index, records){
13806 records = [].concat(records);
13807 for(var i = 0, len = records.length; i < len; i++){
13808 this.data.insert(index, records[i]);
13809 records[i].join(this);
13811 this.fireEvent("add", this, records, index);
13815 * Get the index within the cache of the passed Record.
13816 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13817 * @return {Number} The index of the passed Record. Returns -1 if not found.
13819 indexOf : function(record){
13820 return this.data.indexOf(record);
13824 * Get the index within the cache of the Record with the passed id.
13825 * @param {String} id The id of the Record to find.
13826 * @return {Number} The index of the Record. Returns -1 if not found.
13828 indexOfId : function(id){
13829 return this.data.indexOfKey(id);
13833 * Get the Record with the specified id.
13834 * @param {String} id The id of the Record to find.
13835 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13837 getById : function(id){
13838 return this.data.key(id);
13842 * Get the Record at the specified index.
13843 * @param {Number} index The index of the Record to find.
13844 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13846 getAt : function(index){
13847 return this.data.itemAt(index);
13851 * Returns a range of Records between specified indices.
13852 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13853 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13854 * @return {Roo.data.Record[]} An array of Records
13856 getRange : function(start, end){
13857 return this.data.getRange(start, end);
13861 storeOptions : function(o){
13862 o = Roo.apply({}, o);
13865 this.lastOptions = o;
13869 * Loads the Record cache from the configured Proxy using the configured Reader.
13871 * If using remote paging, then the first load call must specify the <em>start</em>
13872 * and <em>limit</em> properties in the options.params property to establish the initial
13873 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13875 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13876 * and this call will return before the new data has been loaded. Perform any post-processing
13877 * in a callback function, or in a "load" event handler.</strong>
13879 * @param {Object} options An object containing properties which control loading options:<ul>
13880 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13881 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13882 * passed the following arguments:<ul>
13883 * <li>r : Roo.data.Record[]</li>
13884 * <li>options: Options object from the load call</li>
13885 * <li>success: Boolean success indicator</li></ul></li>
13886 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13887 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13890 load : function(options){
13891 options = options || {};
13892 if(this.fireEvent("beforeload", this, options) !== false){
13893 this.storeOptions(options);
13894 var p = Roo.apply(options.params || {}, this.baseParams);
13895 // if meta was not loaded from remote source.. try requesting it.
13896 if (!this.reader.metaFromRemote) {
13897 p._requestMeta = 1;
13899 if(this.sortInfo && this.remoteSort){
13900 var pn = this.paramNames;
13901 p[pn["sort"]] = this.sortInfo.field;
13902 p[pn["dir"]] = this.sortInfo.direction;
13904 if (this.multiSort) {
13905 var pn = this.paramNames;
13906 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13909 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13914 * Reloads the Record cache from the configured Proxy using the configured Reader and
13915 * the options from the last load operation performed.
13916 * @param {Object} options (optional) An object containing properties which may override the options
13917 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13918 * the most recently used options are reused).
13920 reload : function(options){
13921 this.load(Roo.applyIf(options||{}, this.lastOptions));
13925 // Called as a callback by the Reader during a load operation.
13926 loadRecords : function(o, options, success){
13927 if(!o || success === false){
13928 if(success !== false){
13929 this.fireEvent("load", this, [], options, o);
13931 if(options.callback){
13932 options.callback.call(options.scope || this, [], options, false);
13936 // if data returned failure - throw an exception.
13937 if (o.success === false) {
13938 // show a message if no listener is registered.
13939 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13940 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13942 // loadmask wil be hooked into this..
13943 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13946 var r = o.records, t = o.totalRecords || r.length;
13948 this.fireEvent("beforeloadadd", this, r, options, o);
13950 if(!options || options.add !== true){
13951 if(this.pruneModifiedRecords){
13952 this.modified = [];
13954 for(var i = 0, len = r.length; i < len; i++){
13958 this.data = this.snapshot;
13959 delete this.snapshot;
13962 this.data.addAll(r);
13963 this.totalLength = t;
13965 this.fireEvent("datachanged", this);
13967 this.totalLength = Math.max(t, this.data.length+r.length);
13971 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13973 var e = new Roo.data.Record({});
13975 e.set(this.parent.displayField, this.parent.emptyTitle);
13976 e.set(this.parent.valueField, '');
13981 this.fireEvent("load", this, r, options, o);
13982 if(options.callback){
13983 options.callback.call(options.scope || this, r, options, true);
13989 * Loads data from a passed data block. A Reader which understands the format of the data
13990 * must have been configured in the constructor.
13991 * @param {Object} data The data block from which to read the Records. The format of the data expected
13992 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13993 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13995 loadData : function(o, append){
13996 var r = this.reader.readRecords(o);
13997 this.loadRecords(r, {add: append}, true);
14001 * using 'cn' the nested child reader read the child array into it's child stores.
14002 * @param {Object} rec The record with a 'children array
14004 loadDataFromChildren : function(rec)
14006 this.loadData(this.reader.toLoadData(rec));
14011 * Gets the number of cached records.
14013 * <em>If using paging, this may not be the total size of the dataset. If the data object
14014 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14015 * the data set size</em>
14017 getCount : function(){
14018 return this.data.length || 0;
14022 * Gets the total number of records in the dataset as returned by the server.
14024 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14025 * the dataset size</em>
14027 getTotalCount : function(){
14028 return this.totalLength || 0;
14032 * Returns the sort state of the Store as an object with two properties:
14034 field {String} The name of the field by which the Records are sorted
14035 direction {String} The sort order, "ASC" or "DESC"
14038 getSortState : function(){
14039 return this.sortInfo;
14043 applySort : function(){
14044 if(this.sortInfo && !this.remoteSort){
14045 var s = this.sortInfo, f = s.field;
14046 var st = this.fields.get(f).sortType;
14047 var fn = function(r1, r2){
14048 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14049 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14051 this.data.sort(s.direction, fn);
14052 if(this.snapshot && this.snapshot != this.data){
14053 this.snapshot.sort(s.direction, fn);
14059 * Sets the default sort column and order to be used by the next load operation.
14060 * @param {String} fieldName The name of the field to sort by.
14061 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14063 setDefaultSort : function(field, dir){
14064 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14068 * Sort the Records.
14069 * If remote sorting is used, the sort is performed on the server, and the cache is
14070 * reloaded. If local sorting is used, the cache is sorted internally.
14071 * @param {String} fieldName The name of the field to sort by.
14072 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14074 sort : function(fieldName, dir){
14075 var f = this.fields.get(fieldName);
14077 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14079 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14080 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14085 this.sortToggle[f.name] = dir;
14086 this.sortInfo = {field: f.name, direction: dir};
14087 if(!this.remoteSort){
14089 this.fireEvent("datachanged", this);
14091 this.load(this.lastOptions);
14096 * Calls the specified function for each of the Records in the cache.
14097 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14098 * Returning <em>false</em> aborts and exits the iteration.
14099 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14101 each : function(fn, scope){
14102 this.data.each(fn, scope);
14106 * Gets all records modified since the last commit. Modified records are persisted across load operations
14107 * (e.g., during paging).
14108 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14110 getModifiedRecords : function(){
14111 return this.modified;
14115 createFilterFn : function(property, value, anyMatch){
14116 if(!value.exec){ // not a regex
14117 value = String(value);
14118 if(value.length == 0){
14121 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14123 return function(r){
14124 return value.test(r.data[property]);
14129 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14130 * @param {String} property A field on your records
14131 * @param {Number} start The record index to start at (defaults to 0)
14132 * @param {Number} end The last record index to include (defaults to length - 1)
14133 * @return {Number} The sum
14135 sum : function(property, start, end){
14136 var rs = this.data.items, v = 0;
14137 start = start || 0;
14138 end = (end || end === 0) ? end : rs.length-1;
14140 for(var i = start; i <= end; i++){
14141 v += (rs[i].data[property] || 0);
14147 * Filter the records by a specified property.
14148 * @param {String} field A field on your records
14149 * @param {String/RegExp} value Either a string that the field
14150 * should start with or a RegExp to test against the field
14151 * @param {Boolean} anyMatch True to match any part not just the beginning
14153 filter : function(property, value, anyMatch){
14154 var fn = this.createFilterFn(property, value, anyMatch);
14155 return fn ? this.filterBy(fn) : this.clearFilter();
14159 * Filter by a function. The specified function will be called with each
14160 * record in this data source. If the function returns true the record is included,
14161 * otherwise it is filtered.
14162 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14163 * @param {Object} scope (optional) The scope of the function (defaults to this)
14165 filterBy : function(fn, scope){
14166 this.snapshot = this.snapshot || this.data;
14167 this.data = this.queryBy(fn, scope||this);
14168 this.fireEvent("datachanged", this);
14172 * Query the records by a specified property.
14173 * @param {String} field A field on your records
14174 * @param {String/RegExp} value Either a string that the field
14175 * should start with or a RegExp to test against the field
14176 * @param {Boolean} anyMatch True to match any part not just the beginning
14177 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14179 query : function(property, value, anyMatch){
14180 var fn = this.createFilterFn(property, value, anyMatch);
14181 return fn ? this.queryBy(fn) : this.data.clone();
14185 * Query by a function. The specified function will be called with each
14186 * record in this data source. If the function returns true the record is included
14188 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14189 * @param {Object} scope (optional) The scope of the function (defaults to this)
14190 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14192 queryBy : function(fn, scope){
14193 var data = this.snapshot || this.data;
14194 return data.filterBy(fn, scope||this);
14198 * Collects unique values for a particular dataIndex from this store.
14199 * @param {String} dataIndex The property to collect
14200 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14201 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14202 * @return {Array} An array of the unique values
14204 collect : function(dataIndex, allowNull, bypassFilter){
14205 var d = (bypassFilter === true && this.snapshot) ?
14206 this.snapshot.items : this.data.items;
14207 var v, sv, r = [], l = {};
14208 for(var i = 0, len = d.length; i < len; i++){
14209 v = d[i].data[dataIndex];
14211 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14220 * Revert to a view of the Record cache with no filtering applied.
14221 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14223 clearFilter : function(suppressEvent){
14224 if(this.snapshot && this.snapshot != this.data){
14225 this.data = this.snapshot;
14226 delete this.snapshot;
14227 if(suppressEvent !== true){
14228 this.fireEvent("datachanged", this);
14234 afterEdit : function(record){
14235 if(this.modified.indexOf(record) == -1){
14236 this.modified.push(record);
14238 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14242 afterReject : function(record){
14243 this.modified.remove(record);
14244 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14248 afterCommit : function(record){
14249 this.modified.remove(record);
14250 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14254 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14255 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14257 commitChanges : function(){
14258 var m = this.modified.slice(0);
14259 this.modified = [];
14260 for(var i = 0, len = m.length; i < len; i++){
14266 * Cancel outstanding changes on all changed records.
14268 rejectChanges : function(){
14269 var m = this.modified.slice(0);
14270 this.modified = [];
14271 for(var i = 0, len = m.length; i < len; i++){
14276 onMetaChange : function(meta, rtype, o){
14277 this.recordType = rtype;
14278 this.fields = rtype.prototype.fields;
14279 delete this.snapshot;
14280 this.sortInfo = meta.sortInfo || this.sortInfo;
14281 this.modified = [];
14282 this.fireEvent('metachange', this, this.reader.meta);
14285 moveIndex : function(data, type)
14287 var index = this.indexOf(data);
14289 var newIndex = index + type;
14293 this.insert(newIndex, data);
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14308 * @class Roo.data.SimpleStore
14309 * @extends Roo.data.Store
14310 * Small helper class to make creating Stores from Array data easier.
14311 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14312 * @cfg {Array} fields An array of field definition objects, or field name strings.
14313 * @cfg {Object} an existing reader (eg. copied from another store)
14314 * @cfg {Array} data The multi-dimensional array of data
14316 * @param {Object} config
14318 Roo.data.SimpleStore = function(config)
14320 Roo.data.SimpleStore.superclass.constructor.call(this, {
14322 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14325 Roo.data.Record.create(config.fields)
14327 proxy : new Roo.data.MemoryProxy(config.data)
14331 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14333 * Ext JS Library 1.1.1
14334 * Copyright(c) 2006-2007, Ext JS, LLC.
14336 * Originally Released Under LGPL - original licence link has changed is not relivant.
14339 * <script type="text/javascript">
14344 * @extends Roo.data.Store
14345 * @class Roo.data.JsonStore
14346 * Small helper class to make creating Stores for JSON data easier. <br/>
14348 var store = new Roo.data.JsonStore({
14349 url: 'get-images.php',
14351 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14354 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14355 * JsonReader and HttpProxy (unless inline data is provided).</b>
14356 * @cfg {Array} fields An array of field definition objects, or field name strings.
14358 * @param {Object} config
14360 Roo.data.JsonStore = function(c){
14361 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14362 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14363 reader: new Roo.data.JsonReader(c, c.fields)
14366 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14368 * Ext JS Library 1.1.1
14369 * Copyright(c) 2006-2007, Ext JS, LLC.
14371 * Originally Released Under LGPL - original licence link has changed is not relivant.
14374 * <script type="text/javascript">
14378 Roo.data.Field = function(config){
14379 if(typeof config == "string"){
14380 config = {name: config};
14382 Roo.apply(this, config);
14385 this.type = "auto";
14388 var st = Roo.data.SortTypes;
14389 // named sortTypes are supported, here we look them up
14390 if(typeof this.sortType == "string"){
14391 this.sortType = st[this.sortType];
14394 // set default sortType for strings and dates
14395 if(!this.sortType){
14398 this.sortType = st.asUCString;
14401 this.sortType = st.asDate;
14404 this.sortType = st.none;
14409 var stripRe = /[\$,%]/g;
14411 // prebuilt conversion function for this field, instead of
14412 // switching every time we're reading a value
14414 var cv, dateFormat = this.dateFormat;
14419 cv = function(v){ return v; };
14422 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14426 return v !== undefined && v !== null && v !== '' ?
14427 parseInt(String(v).replace(stripRe, ""), 10) : '';
14432 return v !== undefined && v !== null && v !== '' ?
14433 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14438 cv = function(v){ return v === true || v === "true" || v == 1; };
14445 if(v instanceof Date){
14449 if(dateFormat == "timestamp"){
14450 return new Date(v*1000);
14452 return Date.parseDate(v, dateFormat);
14454 var parsed = Date.parse(v);
14455 return parsed ? new Date(parsed) : null;
14464 Roo.data.Field.prototype = {
14472 * Ext JS Library 1.1.1
14473 * Copyright(c) 2006-2007, Ext JS, LLC.
14475 * Originally Released Under LGPL - original licence link has changed is not relivant.
14478 * <script type="text/javascript">
14481 // Base class for reading structured data from a data source. This class is intended to be
14482 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14485 * @class Roo.data.DataReader
14486 * Base class for reading structured data from a data source. This class is intended to be
14487 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14490 Roo.data.DataReader = function(meta, recordType){
14494 this.recordType = recordType instanceof Array ?
14495 Roo.data.Record.create(recordType) : recordType;
14498 Roo.data.DataReader.prototype = {
14501 readerType : 'Data',
14503 * Create an empty record
14504 * @param {Object} data (optional) - overlay some values
14505 * @return {Roo.data.Record} record created.
14507 newRow : function(d) {
14509 this.recordType.prototype.fields.each(function(c) {
14511 case 'int' : da[c.name] = 0; break;
14512 case 'date' : da[c.name] = new Date(); break;
14513 case 'float' : da[c.name] = 0.0; break;
14514 case 'boolean' : da[c.name] = false; break;
14515 default : da[c.name] = ""; break;
14519 return new this.recordType(Roo.apply(da, d));
14525 * Ext JS Library 1.1.1
14526 * Copyright(c) 2006-2007, Ext JS, LLC.
14528 * Originally Released Under LGPL - original licence link has changed is not relivant.
14531 * <script type="text/javascript">
14535 * @class Roo.data.DataProxy
14536 * @extends Roo.data.Observable
14537 * This class is an abstract base class for implementations which provide retrieval of
14538 * unformatted data objects.<br>
14540 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14541 * (of the appropriate type which knows how to parse the data object) to provide a block of
14542 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14544 * Custom implementations must implement the load method as described in
14545 * {@link Roo.data.HttpProxy#load}.
14547 Roo.data.DataProxy = function(){
14550 * @event beforeload
14551 * Fires before a network request is made to retrieve a data object.
14552 * @param {Object} This DataProxy object.
14553 * @param {Object} params The params parameter to the load function.
14558 * Fires before the load method's callback is called.
14559 * @param {Object} This DataProxy object.
14560 * @param {Object} o The data object.
14561 * @param {Object} arg The callback argument object passed to the load function.
14565 * @event loadexception
14566 * Fires if an Exception occurs during data retrieval.
14567 * @param {Object} This DataProxy object.
14568 * @param {Object} o The data object.
14569 * @param {Object} arg The callback argument object passed to the load function.
14570 * @param {Object} e The Exception.
14572 loadexception : true
14574 Roo.data.DataProxy.superclass.constructor.call(this);
14577 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14580 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14584 * Ext JS Library 1.1.1
14585 * Copyright(c) 2006-2007, Ext JS, LLC.
14587 * Originally Released Under LGPL - original licence link has changed is not relivant.
14590 * <script type="text/javascript">
14593 * @class Roo.data.MemoryProxy
14594 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14595 * to the Reader when its load method is called.
14597 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14599 Roo.data.MemoryProxy = function(data){
14603 Roo.data.MemoryProxy.superclass.constructor.call(this);
14607 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14610 * Load data from the requested source (in this case an in-memory
14611 * data object passed to the constructor), read the data object into
14612 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14613 * process that block using the passed callback.
14614 * @param {Object} params This parameter is not used by the MemoryProxy class.
14615 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14616 * object into a block of Roo.data.Records.
14617 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14618 * The function must be passed <ul>
14619 * <li>The Record block object</li>
14620 * <li>The "arg" argument from the load function</li>
14621 * <li>A boolean success indicator</li>
14623 * @param {Object} scope The scope in which to call the callback
14624 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14626 load : function(params, reader, callback, scope, arg){
14627 params = params || {};
14630 result = reader.readRecords(params.data ? params.data :this.data);
14632 this.fireEvent("loadexception", this, arg, null, e);
14633 callback.call(scope, null, arg, false);
14636 callback.call(scope, result, arg, true);
14640 update : function(params, records){
14645 * Ext JS Library 1.1.1
14646 * Copyright(c) 2006-2007, Ext JS, LLC.
14648 * Originally Released Under LGPL - original licence link has changed is not relivant.
14651 * <script type="text/javascript">
14654 * @class Roo.data.HttpProxy
14655 * @extends Roo.data.DataProxy
14656 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14657 * configured to reference a certain URL.<br><br>
14659 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14660 * from which the running page was served.<br><br>
14662 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14664 * Be aware that to enable the browser to parse an XML document, the server must set
14665 * the Content-Type header in the HTTP response to "text/xml".
14667 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14668 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14669 * will be used to make the request.
14671 Roo.data.HttpProxy = function(conn){
14672 Roo.data.HttpProxy.superclass.constructor.call(this);
14673 // is conn a conn config or a real conn?
14675 this.useAjax = !conn || !conn.events;
14679 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14680 // thse are take from connection...
14683 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14686 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14687 * extra parameters to each request made by this object. (defaults to undefined)
14690 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14691 * to each request made by this object. (defaults to undefined)
14694 * @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)
14697 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14700 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14706 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14710 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14711 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14712 * a finer-grained basis than the DataProxy events.
14714 getConnection : function(){
14715 return this.useAjax ? Roo.Ajax : this.conn;
14719 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14720 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14721 * process that block using the passed callback.
14722 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14723 * for the request to the remote server.
14724 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14725 * object into a block of Roo.data.Records.
14726 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14727 * The function must be passed <ul>
14728 * <li>The Record block object</li>
14729 * <li>The "arg" argument from the load function</li>
14730 * <li>A boolean success indicator</li>
14732 * @param {Object} scope The scope in which to call the callback
14733 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14735 load : function(params, reader, callback, scope, arg){
14736 if(this.fireEvent("beforeload", this, params) !== false){
14738 params : params || {},
14740 callback : callback,
14745 callback : this.loadResponse,
14749 Roo.applyIf(o, this.conn);
14750 if(this.activeRequest){
14751 Roo.Ajax.abort(this.activeRequest);
14753 this.activeRequest = Roo.Ajax.request(o);
14755 this.conn.request(o);
14758 callback.call(scope||this, null, arg, false);
14763 loadResponse : function(o, success, response){
14764 delete this.activeRequest;
14766 this.fireEvent("loadexception", this, o, response);
14767 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14772 result = o.reader.read(response);
14774 this.fireEvent("loadexception", this, o, response, e);
14775 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14779 this.fireEvent("load", this, o, o.request.arg);
14780 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14784 update : function(dataSet){
14789 updateResponse : function(dataSet){
14794 * Ext JS Library 1.1.1
14795 * Copyright(c) 2006-2007, Ext JS, LLC.
14797 * Originally Released Under LGPL - original licence link has changed is not relivant.
14800 * <script type="text/javascript">
14804 * @class Roo.data.ScriptTagProxy
14805 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14806 * other than the originating domain of the running page.<br><br>
14808 * <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
14809 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14811 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14812 * source code that is used as the source inside a <script> tag.<br><br>
14814 * In order for the browser to process the returned data, the server must wrap the data object
14815 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14816 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14817 * depending on whether the callback name was passed:
14820 boolean scriptTag = false;
14821 String cb = request.getParameter("callback");
14824 response.setContentType("text/javascript");
14826 response.setContentType("application/x-json");
14828 Writer out = response.getWriter();
14830 out.write(cb + "(");
14832 out.print(dataBlock.toJsonString());
14839 * @param {Object} config A configuration object.
14841 Roo.data.ScriptTagProxy = function(config){
14842 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14843 Roo.apply(this, config);
14844 this.head = document.getElementsByTagName("head")[0];
14847 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14849 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14851 * @cfg {String} url The URL from which to request the data object.
14854 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14858 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14859 * the server the name of the callback function set up by the load call to process the returned data object.
14860 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14861 * javascript output which calls this named function passing the data object as its only parameter.
14863 callbackParam : "callback",
14865 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14866 * name to the request.
14871 * Load data from the configured URL, read the data object into
14872 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14873 * process that block using the passed callback.
14874 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14875 * for the request to the remote server.
14876 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14877 * object into a block of Roo.data.Records.
14878 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14879 * The function must be passed <ul>
14880 * <li>The Record block object</li>
14881 * <li>The "arg" argument from the load function</li>
14882 * <li>A boolean success indicator</li>
14884 * @param {Object} scope The scope in which to call the callback
14885 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14887 load : function(params, reader, callback, scope, arg){
14888 if(this.fireEvent("beforeload", this, params) !== false){
14890 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14892 var url = this.url;
14893 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14895 url += "&_dc=" + (new Date().getTime());
14897 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14900 cb : "stcCallback"+transId,
14901 scriptId : "stcScript"+transId,
14905 callback : callback,
14911 window[trans.cb] = function(o){
14912 conn.handleResponse(o, trans);
14915 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14917 if(this.autoAbort !== false){
14921 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14923 var script = document.createElement("script");
14924 script.setAttribute("src", url);
14925 script.setAttribute("type", "text/javascript");
14926 script.setAttribute("id", trans.scriptId);
14927 this.head.appendChild(script);
14929 this.trans = trans;
14931 callback.call(scope||this, null, arg, false);
14936 isLoading : function(){
14937 return this.trans ? true : false;
14941 * Abort the current server request.
14943 abort : function(){
14944 if(this.isLoading()){
14945 this.destroyTrans(this.trans);
14950 destroyTrans : function(trans, isLoaded){
14951 this.head.removeChild(document.getElementById(trans.scriptId));
14952 clearTimeout(trans.timeoutId);
14954 window[trans.cb] = undefined;
14956 delete window[trans.cb];
14959 // if hasn't been loaded, wait for load to remove it to prevent script error
14960 window[trans.cb] = function(){
14961 window[trans.cb] = undefined;
14963 delete window[trans.cb];
14970 handleResponse : function(o, trans){
14971 this.trans = false;
14972 this.destroyTrans(trans, true);
14975 result = trans.reader.readRecords(o);
14977 this.fireEvent("loadexception", this, o, trans.arg, e);
14978 trans.callback.call(trans.scope||window, null, trans.arg, false);
14981 this.fireEvent("load", this, o, trans.arg);
14982 trans.callback.call(trans.scope||window, result, trans.arg, true);
14986 handleFailure : function(trans){
14987 this.trans = false;
14988 this.destroyTrans(trans, false);
14989 this.fireEvent("loadexception", this, null, trans.arg);
14990 trans.callback.call(trans.scope||window, null, trans.arg, false);
14994 * Ext JS Library 1.1.1
14995 * Copyright(c) 2006-2007, Ext JS, LLC.
14997 * Originally Released Under LGPL - original licence link has changed is not relivant.
15000 * <script type="text/javascript">
15004 * @class Roo.data.JsonReader
15005 * @extends Roo.data.DataReader
15006 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15007 * based on mappings in a provided Roo.data.Record constructor.
15009 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15010 * in the reply previously.
15015 var RecordDef = Roo.data.Record.create([
15016 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15017 {name: 'occupation'} // This field will use "occupation" as the mapping.
15019 var myReader = new Roo.data.JsonReader({
15020 totalProperty: "results", // The property which contains the total dataset size (optional)
15021 root: "rows", // The property which contains an Array of row objects
15022 id: "id" // The property within each row object that provides an ID for the record (optional)
15026 * This would consume a JSON file like this:
15028 { 'results': 2, 'rows': [
15029 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15030 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15033 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15034 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15035 * paged from the remote server.
15036 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15037 * @cfg {String} root name of the property which contains the Array of row objects.
15038 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15039 * @cfg {Array} fields Array of field definition objects
15041 * Create a new JsonReader
15042 * @param {Object} meta Metadata configuration options
15043 * @param {Object} recordType Either an Array of field definition objects,
15044 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15046 Roo.data.JsonReader = function(meta, recordType){
15049 // set some defaults:
15050 Roo.applyIf(meta, {
15051 totalProperty: 'total',
15052 successProperty : 'success',
15057 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15059 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15061 readerType : 'Json',
15064 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15065 * Used by Store query builder to append _requestMeta to params.
15068 metaFromRemote : false,
15070 * This method is only used by a DataProxy which has retrieved data from a remote server.
15071 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15072 * @return {Object} data A data block which is used by an Roo.data.Store object as
15073 * a cache of Roo.data.Records.
15075 read : function(response){
15076 var json = response.responseText;
15078 var o = /* eval:var:o */ eval("("+json+")");
15080 throw {message: "JsonReader.read: Json object not found"};
15086 this.metaFromRemote = true;
15087 this.meta = o.metaData;
15088 this.recordType = Roo.data.Record.create(o.metaData.fields);
15089 this.onMetaChange(this.meta, this.recordType, o);
15091 return this.readRecords(o);
15094 // private function a store will implement
15095 onMetaChange : function(meta, recordType, o){
15102 simpleAccess: function(obj, subsc) {
15109 getJsonAccessor: function(){
15111 return function(expr) {
15113 return(re.test(expr))
15114 ? new Function("obj", "return obj." + expr)
15119 return Roo.emptyFn;
15124 * Create a data block containing Roo.data.Records from an XML document.
15125 * @param {Object} o An object which contains an Array of row objects in the property specified
15126 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15127 * which contains the total size of the dataset.
15128 * @return {Object} data A data block which is used by an Roo.data.Store object as
15129 * a cache of Roo.data.Records.
15131 readRecords : function(o){
15133 * After any data loads, the raw JSON data is available for further custom processing.
15137 var s = this.meta, Record = this.recordType,
15138 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15140 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15142 if(s.totalProperty) {
15143 this.getTotal = this.getJsonAccessor(s.totalProperty);
15145 if(s.successProperty) {
15146 this.getSuccess = this.getJsonAccessor(s.successProperty);
15148 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15150 var g = this.getJsonAccessor(s.id);
15151 this.getId = function(rec) {
15153 return (r === undefined || r === "") ? null : r;
15156 this.getId = function(){return null;};
15159 for(var jj = 0; jj < fl; jj++){
15161 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15162 this.ef[jj] = this.getJsonAccessor(map);
15166 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15167 if(s.totalProperty){
15168 var vt = parseInt(this.getTotal(o), 10);
15173 if(s.successProperty){
15174 var vs = this.getSuccess(o);
15175 if(vs === false || vs === 'false'){
15180 for(var i = 0; i < c; i++){
15183 var id = this.getId(n);
15184 for(var j = 0; j < fl; j++){
15186 var v = this.ef[j](n);
15188 Roo.log('missing convert for ' + f.name);
15192 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15194 var record = new Record(values, id);
15196 records[i] = record;
15202 totalRecords : totalRecords
15205 // used when loading children.. @see loadDataFromChildren
15206 toLoadData: function(rec)
15208 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15209 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15210 return { data : data, total : data.length };
15215 * Ext JS Library 1.1.1
15216 * Copyright(c) 2006-2007, Ext JS, LLC.
15218 * Originally Released Under LGPL - original licence link has changed is not relivant.
15221 * <script type="text/javascript">
15225 * @class Roo.data.ArrayReader
15226 * @extends Roo.data.DataReader
15227 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15228 * Each element of that Array represents a row of data fields. The
15229 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15230 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15234 var RecordDef = Roo.data.Record.create([
15235 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15236 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15238 var myReader = new Roo.data.ArrayReader({
15239 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15243 * This would consume an Array like this:
15245 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15249 * Create a new JsonReader
15250 * @param {Object} meta Metadata configuration options.
15251 * @param {Object|Array} recordType Either an Array of field definition objects
15253 * @cfg {Array} fields Array of field definition objects
15254 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15255 * as specified to {@link Roo.data.Record#create},
15256 * or an {@link Roo.data.Record} object
15259 * created using {@link Roo.data.Record#create}.
15261 Roo.data.ArrayReader = function(meta, recordType)
15263 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15266 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15269 * Create a data block containing Roo.data.Records from an XML document.
15270 * @param {Object} o An Array of row objects which represents the dataset.
15271 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15272 * a cache of Roo.data.Records.
15274 readRecords : function(o)
15276 var sid = this.meta ? this.meta.id : null;
15277 var recordType = this.recordType, fields = recordType.prototype.fields;
15280 for(var i = 0; i < root.length; i++){
15283 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15284 for(var j = 0, jlen = fields.length; j < jlen; j++){
15285 var f = fields.items[j];
15286 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15287 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15289 values[f.name] = v;
15291 var record = new recordType(values, id);
15293 records[records.length] = record;
15297 totalRecords : records.length
15300 // used when loading children.. @see loadDataFromChildren
15301 toLoadData: function(rec)
15303 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15304 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15315 * @class Roo.bootstrap.ComboBox
15316 * @extends Roo.bootstrap.TriggerField
15317 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15318 * @cfg {Boolean} append (true|false) default false
15319 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15320 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15321 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15322 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15323 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15324 * @cfg {Boolean} animate default true
15325 * @cfg {Boolean} emptyResultText only for touch device
15326 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15327 * @cfg {String} emptyTitle default ''
15328 * @cfg {Number} width fixed with? experimental
15330 * Create a new ComboBox.
15331 * @param {Object} config Configuration options
15333 Roo.bootstrap.ComboBox = function(config){
15334 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15338 * Fires when the dropdown list is expanded
15339 * @param {Roo.bootstrap.ComboBox} combo This combo box
15344 * Fires when the dropdown list is collapsed
15345 * @param {Roo.bootstrap.ComboBox} combo This combo box
15349 * @event beforeselect
15350 * Fires before a list item is selected. Return false to cancel the selection.
15351 * @param {Roo.bootstrap.ComboBox} combo This combo box
15352 * @param {Roo.data.Record} record The data record returned from the underlying store
15353 * @param {Number} index The index of the selected item in the dropdown list
15355 'beforeselect' : true,
15358 * Fires when a list item is selected
15359 * @param {Roo.bootstrap.ComboBox} combo This combo box
15360 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15361 * @param {Number} index The index of the selected item in the dropdown list
15365 * @event beforequery
15366 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15367 * The event object passed has these properties:
15368 * @param {Roo.bootstrap.ComboBox} combo This combo box
15369 * @param {String} query The query
15370 * @param {Boolean} forceAll true to force "all" query
15371 * @param {Boolean} cancel true to cancel the query
15372 * @param {Object} e The query event object
15374 'beforequery': true,
15377 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15378 * @param {Roo.bootstrap.ComboBox} combo This combo box
15383 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15384 * @param {Roo.bootstrap.ComboBox} combo This combo box
15385 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15390 * Fires when the remove value from the combobox array
15391 * @param {Roo.bootstrap.ComboBox} combo This combo box
15395 * @event afterremove
15396 * Fires when the remove value from the combobox array
15397 * @param {Roo.bootstrap.ComboBox} combo This combo box
15399 'afterremove' : true,
15401 * @event specialfilter
15402 * Fires when specialfilter
15403 * @param {Roo.bootstrap.ComboBox} combo This combo box
15405 'specialfilter' : true,
15408 * Fires when tick the element
15409 * @param {Roo.bootstrap.ComboBox} combo This combo box
15413 * @event touchviewdisplay
15414 * Fires when touch view require special display (default is using displayField)
15415 * @param {Roo.bootstrap.ComboBox} combo This combo box
15416 * @param {Object} cfg set html .
15418 'touchviewdisplay' : true
15423 this.tickItems = [];
15425 this.selectedIndex = -1;
15426 if(this.mode == 'local'){
15427 if(config.queryDelay === undefined){
15428 this.queryDelay = 10;
15430 if(config.minChars === undefined){
15436 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15439 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15440 * rendering into an Roo.Editor, defaults to false)
15443 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15444 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15447 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15450 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15451 * the dropdown list (defaults to undefined, with no header element)
15455 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15459 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15461 listWidth: undefined,
15463 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15464 * mode = 'remote' or 'text' if mode = 'local')
15466 displayField: undefined,
15469 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15470 * mode = 'remote' or 'value' if mode = 'local').
15471 * Note: use of a valueField requires the user make a selection
15472 * in order for a value to be mapped.
15474 valueField: undefined,
15476 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15481 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15482 * field's data value (defaults to the underlying DOM element's name)
15484 hiddenName: undefined,
15486 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15490 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15492 selectedClass: 'active',
15495 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15499 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15500 * anchor positions (defaults to 'tl-bl')
15502 listAlign: 'tl-bl?',
15504 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15508 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15509 * query specified by the allQuery config option (defaults to 'query')
15511 triggerAction: 'query',
15513 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15514 * (defaults to 4, does not apply if editable = false)
15518 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15519 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15523 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15524 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15528 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15529 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15533 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15534 * when editable = true (defaults to false)
15536 selectOnFocus:false,
15538 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15540 queryParam: 'query',
15542 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15543 * when mode = 'remote' (defaults to 'Loading...')
15545 loadingText: 'Loading...',
15547 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15551 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15555 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15556 * traditional select (defaults to true)
15560 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15564 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15568 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15569 * listWidth has a higher value)
15573 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15574 * allow the user to set arbitrary text into the field (defaults to false)
15576 forceSelection:false,
15578 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15579 * if typeAhead = true (defaults to 250)
15581 typeAheadDelay : 250,
15583 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15584 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15586 valueNotFoundText : undefined,
15588 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15590 blockFocus : false,
15593 * @cfg {Boolean} disableClear Disable showing of clear button.
15595 disableClear : false,
15597 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15599 alwaysQuery : false,
15602 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15607 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15609 invalidClass : "has-warning",
15612 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15614 validClass : "has-success",
15617 * @cfg {Boolean} specialFilter (true|false) special filter default false
15619 specialFilter : false,
15622 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15624 mobileTouchView : true,
15627 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15629 useNativeIOS : false,
15632 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15634 mobile_restrict_height : false,
15636 ios_options : false,
15648 btnPosition : 'right',
15649 triggerList : true,
15650 showToggleBtn : true,
15652 emptyResultText: 'Empty',
15653 triggerText : 'Select',
15657 // element that contains real text value.. (when hidden is used..)
15659 getAutoCreate : function()
15664 * Render classic select for iso
15667 if(Roo.isIOS && this.useNativeIOS){
15668 cfg = this.getAutoCreateNativeIOS();
15676 if(Roo.isTouch && this.mobileTouchView){
15677 cfg = this.getAutoCreateTouchView();
15684 if(!this.tickable){
15685 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15690 * ComboBox with tickable selections
15693 var align = this.labelAlign || this.parentLabelAlign();
15696 cls : 'form-group roo-combobox-tickable' //input-group
15699 var btn_text_select = '';
15700 var btn_text_done = '';
15701 var btn_text_cancel = '';
15703 if (this.btn_text_show) {
15704 btn_text_select = 'Select';
15705 btn_text_done = 'Done';
15706 btn_text_cancel = 'Cancel';
15711 cls : 'tickable-buttons',
15716 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15717 //html : this.triggerText
15718 html: btn_text_select
15724 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15726 html: btn_text_done
15732 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15734 html: btn_text_cancel
15740 buttons.cn.unshift({
15742 cls: 'roo-select2-search-field-input'
15748 Roo.each(buttons.cn, function(c){
15750 c.cls += ' btn-' + _this.size;
15753 if (_this.disabled) {
15760 style : 'display: contents',
15765 cls: 'form-hidden-field'
15769 cls: 'roo-select2-choices',
15773 cls: 'roo-select2-search-field',
15784 cls: 'roo-select2-container input-group roo-select2-container-multi',
15790 // cls: 'typeahead typeahead-long dropdown-menu',
15791 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15796 if(this.hasFeedback && !this.allowBlank){
15800 cls: 'glyphicon form-control-feedback'
15803 combobox.cn.push(feedback);
15810 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15811 tooltip : 'This field is required'
15813 if (Roo.bootstrap.version == 4) {
15816 style : 'display:none'
15819 if (align ==='left' && this.fieldLabel.length) {
15821 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15828 cls : 'control-label col-form-label',
15829 html : this.fieldLabel
15841 var labelCfg = cfg.cn[1];
15842 var contentCfg = cfg.cn[2];
15845 if(this.indicatorpos == 'right'){
15851 cls : 'control-label col-form-label',
15855 html : this.fieldLabel
15871 labelCfg = cfg.cn[0];
15872 contentCfg = cfg.cn[1];
15876 if(this.labelWidth > 12){
15877 labelCfg.style = "width: " + this.labelWidth + 'px';
15879 if(this.width * 1 > 0){
15880 contentCfg.style = "width: " + this.width + 'px';
15882 if(this.labelWidth < 13 && this.labelmd == 0){
15883 this.labelmd = this.labelWidth;
15886 if(this.labellg > 0){
15887 labelCfg.cls += ' col-lg-' + this.labellg;
15888 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15891 if(this.labelmd > 0){
15892 labelCfg.cls += ' col-md-' + this.labelmd;
15893 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15896 if(this.labelsm > 0){
15897 labelCfg.cls += ' col-sm-' + this.labelsm;
15898 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15901 if(this.labelxs > 0){
15902 labelCfg.cls += ' col-xs-' + this.labelxs;
15903 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15907 } else if ( this.fieldLabel.length) {
15908 // Roo.log(" label");
15913 //cls : 'input-group-addon',
15914 html : this.fieldLabel
15919 if(this.indicatorpos == 'right'){
15923 //cls : 'input-group-addon',
15924 html : this.fieldLabel
15934 // Roo.log(" no label && no align");
15941 ['xs','sm','md','lg'].map(function(size){
15942 if (settings[size]) {
15943 cfg.cls += ' col-' + size + '-' + settings[size];
15951 _initEventsCalled : false,
15954 initEvents: function()
15956 if (this._initEventsCalled) { // as we call render... prevent looping...
15959 this._initEventsCalled = true;
15962 throw "can not find store for combo";
15965 this.indicator = this.indicatorEl();
15967 this.store = Roo.factory(this.store, Roo.data);
15968 this.store.parent = this;
15970 // if we are building from html. then this element is so complex, that we can not really
15971 // use the rendered HTML.
15972 // so we have to trash and replace the previous code.
15973 if (Roo.XComponent.build_from_html) {
15974 // remove this element....
15975 var e = this.el.dom, k=0;
15976 while (e ) { e = e.previousSibling; ++k;}
15981 this.rendered = false;
15983 this.render(this.parent().getChildContainer(true), k);
15986 if(Roo.isIOS && this.useNativeIOS){
15987 this.initIOSView();
15995 if(Roo.isTouch && this.mobileTouchView){
15996 this.initTouchView();
16001 this.initTickableEvents();
16005 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16007 if(this.hiddenName){
16009 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16011 this.hiddenField.dom.value =
16012 this.hiddenValue !== undefined ? this.hiddenValue :
16013 this.value !== undefined ? this.value : '';
16015 // prevent input submission
16016 this.el.dom.removeAttribute('name');
16017 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16022 // this.el.dom.setAttribute('autocomplete', 'off');
16025 var cls = 'x-combo-list';
16027 //this.list = new Roo.Layer({
16028 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16034 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16035 _this.list.setWidth(lw);
16038 this.list.on('mouseover', this.onViewOver, this);
16039 this.list.on('mousemove', this.onViewMove, this);
16040 this.list.on('scroll', this.onViewScroll, this);
16043 this.list.swallowEvent('mousewheel');
16044 this.assetHeight = 0;
16047 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16048 this.assetHeight += this.header.getHeight();
16051 this.innerList = this.list.createChild({cls:cls+'-inner'});
16052 this.innerList.on('mouseover', this.onViewOver, this);
16053 this.innerList.on('mousemove', this.onViewMove, this);
16054 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16056 if(this.allowBlank && !this.pageSize && !this.disableClear){
16057 this.footer = this.list.createChild({cls:cls+'-ft'});
16058 this.pageTb = new Roo.Toolbar(this.footer);
16062 this.footer = this.list.createChild({cls:cls+'-ft'});
16063 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16064 {pageSize: this.pageSize});
16068 if (this.pageTb && this.allowBlank && !this.disableClear) {
16070 this.pageTb.add(new Roo.Toolbar.Fill(), {
16071 cls: 'x-btn-icon x-btn-clear',
16073 handler: function()
16076 _this.clearValue();
16077 _this.onSelect(false, -1);
16082 this.assetHeight += this.footer.getHeight();
16087 this.tpl = Roo.bootstrap.version == 4 ?
16088 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16089 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16092 this.view = new Roo.View(this.list, this.tpl, {
16093 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16095 //this.view.wrapEl.setDisplayed(false);
16096 this.view.on('click', this.onViewClick, this);
16099 this.store.on('beforeload', this.onBeforeLoad, this);
16100 this.store.on('load', this.onLoad, this);
16101 this.store.on('loadexception', this.onLoadException, this);
16103 if(this.resizable){
16104 this.resizer = new Roo.Resizable(this.list, {
16105 pinned:true, handles:'se'
16107 this.resizer.on('resize', function(r, w, h){
16108 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16109 this.listWidth = w;
16110 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16111 this.restrictHeight();
16113 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16116 if(!this.editable){
16117 this.editable = true;
16118 this.setEditable(false);
16123 if (typeof(this.events.add.listeners) != 'undefined') {
16125 this.addicon = this.wrap.createChild(
16126 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16128 this.addicon.on('click', function(e) {
16129 this.fireEvent('add', this);
16132 if (typeof(this.events.edit.listeners) != 'undefined') {
16134 this.editicon = this.wrap.createChild(
16135 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16136 if (this.addicon) {
16137 this.editicon.setStyle('margin-left', '40px');
16139 this.editicon.on('click', function(e) {
16141 // we fire even if inothing is selected..
16142 this.fireEvent('edit', this, this.lastData );
16148 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16149 "up" : function(e){
16150 this.inKeyMode = true;
16154 "down" : function(e){
16155 if(!this.isExpanded()){
16156 this.onTriggerClick();
16158 this.inKeyMode = true;
16163 "enter" : function(e){
16164 // this.onViewClick();
16168 if(this.fireEvent("specialkey", this, e)){
16169 this.onViewClick(false);
16175 "esc" : function(e){
16179 "tab" : function(e){
16182 if(this.fireEvent("specialkey", this, e)){
16183 this.onViewClick(false);
16191 doRelay : function(foo, bar, hname){
16192 if(hname == 'down' || this.scope.isExpanded()){
16193 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16202 this.queryDelay = Math.max(this.queryDelay || 10,
16203 this.mode == 'local' ? 10 : 250);
16206 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16208 if(this.typeAhead){
16209 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16211 if(this.editable !== false){
16212 this.inputEl().on("keyup", this.onKeyUp, this);
16214 if(this.forceSelection){
16215 this.inputEl().on('blur', this.doForce, this);
16219 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16220 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16224 initTickableEvents: function()
16228 if(this.hiddenName){
16230 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16232 this.hiddenField.dom.value =
16233 this.hiddenValue !== undefined ? this.hiddenValue :
16234 this.value !== undefined ? this.value : '';
16236 // prevent input submission
16237 this.el.dom.removeAttribute('name');
16238 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16243 // this.list = this.el.select('ul.dropdown-menu',true).first();
16245 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16246 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16247 if(this.triggerList){
16248 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16251 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16252 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16254 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16255 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16257 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16258 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16260 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16261 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16262 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16265 this.cancelBtn.hide();
16270 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16271 _this.list.setWidth(lw);
16274 this.list.on('mouseover', this.onViewOver, this);
16275 this.list.on('mousemove', this.onViewMove, this);
16277 this.list.on('scroll', this.onViewScroll, this);
16280 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16281 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16284 this.view = new Roo.View(this.list, this.tpl, {
16289 selectedClass: this.selectedClass
16292 //this.view.wrapEl.setDisplayed(false);
16293 this.view.on('click', this.onViewClick, this);
16297 this.store.on('beforeload', this.onBeforeLoad, this);
16298 this.store.on('load', this.onLoad, this);
16299 this.store.on('loadexception', this.onLoadException, this);
16302 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16303 "up" : function(e){
16304 this.inKeyMode = true;
16308 "down" : function(e){
16309 this.inKeyMode = true;
16313 "enter" : function(e){
16314 if(this.fireEvent("specialkey", this, e)){
16315 this.onViewClick(false);
16321 "esc" : function(e){
16322 this.onTickableFooterButtonClick(e, false, false);
16325 "tab" : function(e){
16326 this.fireEvent("specialkey", this, e);
16328 this.onTickableFooterButtonClick(e, false, false);
16335 doRelay : function(e, fn, key){
16336 if(this.scope.isExpanded()){
16337 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16346 this.queryDelay = Math.max(this.queryDelay || 10,
16347 this.mode == 'local' ? 10 : 250);
16350 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16352 if(this.typeAhead){
16353 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16356 if(this.editable !== false){
16357 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16360 this.indicator = this.indicatorEl();
16362 if(this.indicator){
16363 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16364 this.indicator.hide();
16369 onDestroy : function(){
16371 this.view.setStore(null);
16372 this.view.el.removeAllListeners();
16373 this.view.el.remove();
16374 this.view.purgeListeners();
16377 this.list.dom.innerHTML = '';
16381 this.store.un('beforeload', this.onBeforeLoad, this);
16382 this.store.un('load', this.onLoad, this);
16383 this.store.un('loadexception', this.onLoadException, this);
16385 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16389 fireKey : function(e){
16390 if(e.isNavKeyPress() && !this.list.isVisible()){
16391 this.fireEvent("specialkey", this, e);
16396 onResize: function(w, h)
16400 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16402 // if(typeof w != 'number'){
16403 // // we do not handle it!?!?
16406 // var tw = this.trigger.getWidth();
16407 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16408 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16410 // this.inputEl().setWidth( this.adjustWidth('input', x));
16412 // //this.trigger.setStyle('left', x+'px');
16414 // if(this.list && this.listWidth === undefined){
16415 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16416 // this.list.setWidth(lw);
16417 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16425 * Allow or prevent the user from directly editing the field text. If false is passed,
16426 * the user will only be able to select from the items defined in the dropdown list. This method
16427 * is the runtime equivalent of setting the 'editable' config option at config time.
16428 * @param {Boolean} value True to allow the user to directly edit the field text
16430 setEditable : function(value){
16431 if(value == this.editable){
16434 this.editable = value;
16436 this.inputEl().dom.setAttribute('readOnly', true);
16437 this.inputEl().on('mousedown', this.onTriggerClick, this);
16438 this.inputEl().addClass('x-combo-noedit');
16440 this.inputEl().dom.removeAttribute('readOnly');
16441 this.inputEl().un('mousedown', this.onTriggerClick, this);
16442 this.inputEl().removeClass('x-combo-noedit');
16448 onBeforeLoad : function(combo,opts){
16449 if(!this.hasFocus){
16453 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16455 this.restrictHeight();
16456 this.selectedIndex = -1;
16460 onLoad : function(){
16462 this.hasQuery = false;
16464 if(!this.hasFocus){
16468 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16469 this.loading.hide();
16472 if(this.store.getCount() > 0){
16475 this.restrictHeight();
16476 if(this.lastQuery == this.allQuery){
16477 if(this.editable && !this.tickable){
16478 this.inputEl().dom.select();
16482 !this.selectByValue(this.value, true) &&
16485 !this.store.lastOptions ||
16486 typeof(this.store.lastOptions.add) == 'undefined' ||
16487 this.store.lastOptions.add != true
16490 this.select(0, true);
16493 if(this.autoFocus){
16496 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16497 this.taTask.delay(this.typeAheadDelay);
16501 this.onEmptyResults();
16507 onLoadException : function()
16509 this.hasQuery = false;
16511 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16512 this.loading.hide();
16515 if(this.tickable && this.editable){
16520 // only causes errors at present
16521 //Roo.log(this.store.reader.jsonData);
16522 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16524 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16530 onTypeAhead : function(){
16531 if(this.store.getCount() > 0){
16532 var r = this.store.getAt(0);
16533 var newValue = r.data[this.displayField];
16534 var len = newValue.length;
16535 var selStart = this.getRawValue().length;
16537 if(selStart != len){
16538 this.setRawValue(newValue);
16539 this.selectText(selStart, newValue.length);
16545 onSelect : function(record, index){
16547 if(this.fireEvent('beforeselect', this, record, index) !== false){
16549 this.setFromData(index > -1 ? record.data : false);
16552 this.fireEvent('select', this, record, index);
16557 * Returns the currently selected field value or empty string if no value is set.
16558 * @return {String} value The selected value
16560 getValue : function()
16562 if(Roo.isIOS && this.useNativeIOS){
16563 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16567 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16570 if(this.valueField){
16571 return typeof this.value != 'undefined' ? this.value : '';
16573 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16577 getRawValue : function()
16579 if(Roo.isIOS && this.useNativeIOS){
16580 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16583 var v = this.inputEl().getValue();
16589 * Clears any text/value currently set in the field
16591 clearValue : function(){
16593 if(this.hiddenField){
16594 this.hiddenField.dom.value = '';
16597 this.setRawValue('');
16598 this.lastSelectionText = '';
16599 this.lastData = false;
16601 var close = this.closeTriggerEl();
16612 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16613 * will be displayed in the field. If the value does not match the data value of an existing item,
16614 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16615 * Otherwise the field will be blank (although the value will still be set).
16616 * @param {String} value The value to match
16618 setValue : function(v)
16620 if(Roo.isIOS && this.useNativeIOS){
16621 this.setIOSValue(v);
16631 if(this.valueField){
16632 var r = this.findRecord(this.valueField, v);
16634 text = r.data[this.displayField];
16635 }else if(this.valueNotFoundText !== undefined){
16636 text = this.valueNotFoundText;
16639 this.lastSelectionText = text;
16640 if(this.hiddenField){
16641 this.hiddenField.dom.value = v;
16643 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16646 var close = this.closeTriggerEl();
16649 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16655 * @property {Object} the last set data for the element
16660 * Sets the value of the field based on a object which is related to the record format for the store.
16661 * @param {Object} value the value to set as. or false on reset?
16663 setFromData : function(o){
16670 var dv = ''; // display value
16671 var vv = ''; // value value..
16673 if (this.displayField) {
16674 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16676 // this is an error condition!!!
16677 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16680 if(this.valueField){
16681 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16684 var close = this.closeTriggerEl();
16687 if(dv.length || vv * 1 > 0){
16689 this.blockFocus=true;
16695 if(this.hiddenField){
16696 this.hiddenField.dom.value = vv;
16698 this.lastSelectionText = dv;
16699 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16703 // no hidden field.. - we store the value in 'value', but still display
16704 // display field!!!!
16705 this.lastSelectionText = dv;
16706 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16713 reset : function(){
16714 // overridden so that last data is reset..
16721 this.setValue(this.originalValue);
16722 //this.clearInvalid();
16723 this.lastData = false;
16725 this.view.clearSelections();
16731 findRecord : function(prop, value){
16733 if(this.store.getCount() > 0){
16734 this.store.each(function(r){
16735 if(r.data[prop] == value){
16745 getName: function()
16747 // returns hidden if it's set..
16748 if (!this.rendered) {return ''};
16749 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16753 onViewMove : function(e, t){
16754 this.inKeyMode = false;
16758 onViewOver : function(e, t){
16759 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16762 var item = this.view.findItemFromChild(t);
16765 var index = this.view.indexOf(item);
16766 this.select(index, false);
16771 onViewClick : function(view, doFocus, el, e)
16773 var index = this.view.getSelectedIndexes()[0];
16775 var r = this.store.getAt(index);
16779 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16786 Roo.each(this.tickItems, function(v,k){
16788 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16790 _this.tickItems.splice(k, 1);
16792 if(typeof(e) == 'undefined' && view == false){
16793 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16805 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16806 this.tickItems.push(r.data);
16809 if(typeof(e) == 'undefined' && view == false){
16810 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16817 this.onSelect(r, index);
16819 if(doFocus !== false && !this.blockFocus){
16820 this.inputEl().focus();
16825 restrictHeight : function(){
16826 //this.innerList.dom.style.height = '';
16827 //var inner = this.innerList.dom;
16828 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16829 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16830 //this.list.beginUpdate();
16831 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16832 this.list.alignTo(this.inputEl(), this.listAlign);
16833 this.list.alignTo(this.inputEl(), this.listAlign);
16834 //this.list.endUpdate();
16838 onEmptyResults : function(){
16840 if(this.tickable && this.editable){
16841 this.hasFocus = false;
16842 this.restrictHeight();
16850 * Returns true if the dropdown list is expanded, else false.
16852 isExpanded : function(){
16853 return this.list.isVisible();
16857 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16858 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16859 * @param {String} value The data value of the item to select
16860 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16861 * selected item if it is not currently in view (defaults to true)
16862 * @return {Boolean} True if the value matched an item in the list, else false
16864 selectByValue : function(v, scrollIntoView){
16865 if(v !== undefined && v !== null){
16866 var r = this.findRecord(this.valueField || this.displayField, v);
16868 this.select(this.store.indexOf(r), scrollIntoView);
16876 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16877 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16878 * @param {Number} index The zero-based index of the list item to select
16879 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16880 * selected item if it is not currently in view (defaults to true)
16882 select : function(index, scrollIntoView){
16883 this.selectedIndex = index;
16884 this.view.select(index);
16885 if(scrollIntoView !== false){
16886 var el = this.view.getNode(index);
16888 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16891 this.list.scrollChildIntoView(el, false);
16897 selectNext : function(){
16898 var ct = this.store.getCount();
16900 if(this.selectedIndex == -1){
16902 }else if(this.selectedIndex < ct-1){
16903 this.select(this.selectedIndex+1);
16909 selectPrev : function(){
16910 var ct = this.store.getCount();
16912 if(this.selectedIndex == -1){
16914 }else if(this.selectedIndex != 0){
16915 this.select(this.selectedIndex-1);
16921 onKeyUp : function(e){
16922 if(this.editable !== false && !e.isSpecialKey()){
16923 this.lastKey = e.getKey();
16924 this.dqTask.delay(this.queryDelay);
16929 validateBlur : function(){
16930 return !this.list || !this.list.isVisible();
16934 initQuery : function(){
16936 var v = this.getRawValue();
16938 if(this.tickable && this.editable){
16939 v = this.tickableInputEl().getValue();
16946 doForce : function(){
16947 if(this.inputEl().dom.value.length > 0){
16948 this.inputEl().dom.value =
16949 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16955 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16956 * query allowing the query action to be canceled if needed.
16957 * @param {String} query The SQL query to execute
16958 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16959 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16960 * saved in the current store (defaults to false)
16962 doQuery : function(q, forceAll){
16964 if(q === undefined || q === null){
16969 forceAll: forceAll,
16973 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16978 forceAll = qe.forceAll;
16979 if(forceAll === true || (q.length >= this.minChars)){
16981 this.hasQuery = true;
16983 if(this.lastQuery != q || this.alwaysQuery){
16984 this.lastQuery = q;
16985 if(this.mode == 'local'){
16986 this.selectedIndex = -1;
16988 this.store.clearFilter();
16991 if(this.specialFilter){
16992 this.fireEvent('specialfilter', this);
16997 this.store.filter(this.displayField, q);
17000 this.store.fireEvent("datachanged", this.store);
17007 this.store.baseParams[this.queryParam] = q;
17009 var options = {params : this.getParams(q)};
17012 options.add = true;
17013 options.params.start = this.page * this.pageSize;
17016 this.store.load(options);
17019 * this code will make the page width larger, at the beginning, the list not align correctly,
17020 * we should expand the list on onLoad
17021 * so command out it
17026 this.selectedIndex = -1;
17031 this.loadNext = false;
17035 getParams : function(q){
17037 //p[this.queryParam] = q;
17041 p.limit = this.pageSize;
17047 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17049 collapse : function(){
17050 if(!this.isExpanded()){
17056 this.hasFocus = false;
17060 this.cancelBtn.hide();
17061 this.trigger.show();
17064 this.tickableInputEl().dom.value = '';
17065 this.tickableInputEl().blur();
17070 Roo.get(document).un('mousedown', this.collapseIf, this);
17071 Roo.get(document).un('mousewheel', this.collapseIf, this);
17072 if (!this.editable) {
17073 Roo.get(document).un('keydown', this.listKeyPress, this);
17075 this.fireEvent('collapse', this);
17081 collapseIf : function(e){
17082 var in_combo = e.within(this.el);
17083 var in_list = e.within(this.list);
17084 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17086 if (in_combo || in_list || is_list) {
17087 //e.stopPropagation();
17092 this.onTickableFooterButtonClick(e, false, false);
17100 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17102 expand : function(){
17104 if(this.isExpanded() || !this.hasFocus){
17108 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17109 this.list.setWidth(lw);
17115 this.restrictHeight();
17119 this.tickItems = Roo.apply([], this.item);
17122 this.cancelBtn.show();
17123 this.trigger.hide();
17126 this.tickableInputEl().focus();
17131 Roo.get(document).on('mousedown', this.collapseIf, this);
17132 Roo.get(document).on('mousewheel', this.collapseIf, this);
17133 if (!this.editable) {
17134 Roo.get(document).on('keydown', this.listKeyPress, this);
17137 this.fireEvent('expand', this);
17141 // Implements the default empty TriggerField.onTriggerClick function
17142 onTriggerClick : function(e)
17144 Roo.log('trigger click');
17146 if(this.disabled || !this.triggerList){
17151 this.loadNext = false;
17153 if(this.isExpanded()){
17155 if (!this.blockFocus) {
17156 this.inputEl().focus();
17160 this.hasFocus = true;
17161 if(this.triggerAction == 'all') {
17162 this.doQuery(this.allQuery, true);
17164 this.doQuery(this.getRawValue());
17166 if (!this.blockFocus) {
17167 this.inputEl().focus();
17172 onTickableTriggerClick : function(e)
17179 this.loadNext = false;
17180 this.hasFocus = true;
17182 if(this.triggerAction == 'all') {
17183 this.doQuery(this.allQuery, true);
17185 this.doQuery(this.getRawValue());
17189 onSearchFieldClick : function(e)
17191 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17192 this.onTickableFooterButtonClick(e, false, false);
17196 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17201 this.loadNext = false;
17202 this.hasFocus = true;
17204 if(this.triggerAction == 'all') {
17205 this.doQuery(this.allQuery, true);
17207 this.doQuery(this.getRawValue());
17211 listKeyPress : function(e)
17213 //Roo.log('listkeypress');
17214 // scroll to first matching element based on key pres..
17215 if (e.isSpecialKey()) {
17218 var k = String.fromCharCode(e.getKey()).toUpperCase();
17221 var csel = this.view.getSelectedNodes();
17222 var cselitem = false;
17224 var ix = this.view.indexOf(csel[0]);
17225 cselitem = this.store.getAt(ix);
17226 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17232 this.store.each(function(v) {
17234 // start at existing selection.
17235 if (cselitem.id == v.id) {
17241 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17242 match = this.store.indexOf(v);
17248 if (match === false) {
17249 return true; // no more action?
17252 this.view.select(match);
17253 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17254 sn.scrollIntoView(sn.dom.parentNode, false);
17257 onViewScroll : function(e, t){
17259 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){
17263 this.hasQuery = true;
17265 this.loading = this.list.select('.loading', true).first();
17267 if(this.loading === null){
17268 this.list.createChild({
17270 cls: 'loading roo-select2-more-results roo-select2-active',
17271 html: 'Loading more results...'
17274 this.loading = this.list.select('.loading', true).first();
17276 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17278 this.loading.hide();
17281 this.loading.show();
17286 this.loadNext = true;
17288 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17293 addItem : function(o)
17295 var dv = ''; // display value
17297 if (this.displayField) {
17298 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17300 // this is an error condition!!!
17301 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17308 var choice = this.choices.createChild({
17310 cls: 'roo-select2-search-choice',
17319 cls: 'roo-select2-search-choice-close fa fa-times',
17324 }, this.searchField);
17326 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17328 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17336 this.inputEl().dom.value = '';
17341 onRemoveItem : function(e, _self, o)
17343 e.preventDefault();
17345 this.lastItem = Roo.apply([], this.item);
17347 var index = this.item.indexOf(o.data) * 1;
17350 Roo.log('not this item?!');
17354 this.item.splice(index, 1);
17359 this.fireEvent('remove', this, e);
17365 syncValue : function()
17367 if(!this.item.length){
17374 Roo.each(this.item, function(i){
17375 if(_this.valueField){
17376 value.push(i[_this.valueField]);
17383 this.value = value.join(',');
17385 if(this.hiddenField){
17386 this.hiddenField.dom.value = this.value;
17389 this.store.fireEvent("datachanged", this.store);
17394 clearItem : function()
17396 if(!this.multiple){
17402 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17410 if(this.tickable && !Roo.isTouch){
17411 this.view.refresh();
17415 inputEl: function ()
17417 if(Roo.isIOS && this.useNativeIOS){
17418 return this.el.select('select.roo-ios-select', true).first();
17421 if(Roo.isTouch && this.mobileTouchView){
17422 return this.el.select('input.form-control',true).first();
17426 return this.searchField;
17429 return this.el.select('input.form-control',true).first();
17432 onTickableFooterButtonClick : function(e, btn, el)
17434 e.preventDefault();
17436 this.lastItem = Roo.apply([], this.item);
17438 if(btn && btn.name == 'cancel'){
17439 this.tickItems = Roo.apply([], this.item);
17448 Roo.each(this.tickItems, function(o){
17456 validate : function()
17458 if(this.getVisibilityEl().hasClass('hidden')){
17462 var v = this.getRawValue();
17465 v = this.getValue();
17468 if(this.disabled || this.allowBlank || v.length){
17473 this.markInvalid();
17477 tickableInputEl : function()
17479 if(!this.tickable || !this.editable){
17480 return this.inputEl();
17483 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17487 getAutoCreateTouchView : function()
17492 cls: 'form-group' //input-group
17498 type : this.inputType,
17499 cls : 'form-control x-combo-noedit',
17500 autocomplete: 'new-password',
17501 placeholder : this.placeholder || '',
17506 input.name = this.name;
17510 input.cls += ' input-' + this.size;
17513 if (this.disabled) {
17514 input.disabled = true;
17518 cls : 'roo-combobox-wrap',
17525 inputblock.cls += ' input-group';
17527 inputblock.cn.unshift({
17529 cls : 'input-group-addon input-group-prepend input-group-text',
17534 if(this.removable && !this.multiple){
17535 inputblock.cls += ' roo-removable';
17537 inputblock.cn.push({
17540 cls : 'roo-combo-removable-btn close'
17544 if(this.hasFeedback && !this.allowBlank){
17546 inputblock.cls += ' has-feedback';
17548 inputblock.cn.push({
17550 cls: 'glyphicon form-control-feedback'
17557 inputblock.cls += (this.before) ? '' : ' input-group';
17559 inputblock.cn.push({
17561 cls : 'input-group-addon input-group-append input-group-text',
17567 var ibwrap = inputblock;
17572 cls: 'roo-select2-choices',
17576 cls: 'roo-select2-search-field',
17589 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17594 cls: 'form-hidden-field'
17600 if(!this.multiple && this.showToggleBtn){
17606 if (this.caret != false) {
17609 cls: 'fa fa-' + this.caret
17616 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17618 Roo.bootstrap.version == 3 ? caret : '',
17621 cls: 'combobox-clear',
17635 combobox.cls += ' roo-select2-container-multi';
17638 var required = this.allowBlank ? {
17640 style: 'display: none'
17643 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17644 tooltip : 'This field is required'
17647 var align = this.labelAlign || this.parentLabelAlign();
17649 if (align ==='left' && this.fieldLabel.length) {
17655 cls : 'control-label col-form-label',
17656 html : this.fieldLabel
17660 cls : 'roo-combobox-wrap ',
17667 var labelCfg = cfg.cn[1];
17668 var contentCfg = cfg.cn[2];
17671 if(this.indicatorpos == 'right'){
17676 cls : 'control-label col-form-label',
17680 html : this.fieldLabel
17686 cls : "roo-combobox-wrap ",
17694 labelCfg = cfg.cn[0];
17695 contentCfg = cfg.cn[1];
17700 if(this.labelWidth > 12){
17701 labelCfg.style = "width: " + this.labelWidth + 'px';
17704 if(this.labelWidth < 13 && this.labelmd == 0){
17705 this.labelmd = this.labelWidth;
17708 if(this.labellg > 0){
17709 labelCfg.cls += ' col-lg-' + this.labellg;
17710 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17713 if(this.labelmd > 0){
17714 labelCfg.cls += ' col-md-' + this.labelmd;
17715 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17718 if(this.labelsm > 0){
17719 labelCfg.cls += ' col-sm-' + this.labelsm;
17720 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17723 if(this.labelxs > 0){
17724 labelCfg.cls += ' col-xs-' + this.labelxs;
17725 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17729 } else if ( this.fieldLabel.length) {
17734 cls : 'control-label',
17735 html : this.fieldLabel
17746 if(this.indicatorpos == 'right'){
17750 cls : 'control-label',
17751 html : this.fieldLabel,
17769 var settings = this;
17771 ['xs','sm','md','lg'].map(function(size){
17772 if (settings[size]) {
17773 cfg.cls += ' col-' + size + '-' + settings[size];
17780 initTouchView : function()
17782 this.renderTouchView();
17784 this.touchViewEl.on('scroll', function(){
17785 this.el.dom.scrollTop = 0;
17788 this.originalValue = this.getValue();
17790 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17792 this.inputEl().on("click", this.showTouchView, this);
17793 if (this.triggerEl) {
17794 this.triggerEl.on("click", this.showTouchView, this);
17798 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17799 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17801 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17803 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17804 this.store.on('load', this.onTouchViewLoad, this);
17805 this.store.on('loadexception', this.onTouchViewLoadException, this);
17807 if(this.hiddenName){
17809 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17811 this.hiddenField.dom.value =
17812 this.hiddenValue !== undefined ? this.hiddenValue :
17813 this.value !== undefined ? this.value : '';
17815 this.el.dom.removeAttribute('name');
17816 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17820 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17821 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17824 if(this.removable && !this.multiple){
17825 var close = this.closeTriggerEl();
17827 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17828 close.on('click', this.removeBtnClick, this, close);
17832 * fix the bug in Safari iOS8
17834 this.inputEl().on("focus", function(e){
17835 document.activeElement.blur();
17838 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17845 renderTouchView : function()
17847 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17848 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17850 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17851 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17853 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17854 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17855 this.touchViewBodyEl.setStyle('overflow', 'auto');
17857 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17858 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17860 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17861 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17865 showTouchView : function()
17871 this.touchViewHeaderEl.hide();
17873 if(this.modalTitle.length){
17874 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17875 this.touchViewHeaderEl.show();
17878 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17879 this.touchViewEl.show();
17881 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17883 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17884 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17886 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17888 if(this.modalTitle.length){
17889 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17892 this.touchViewBodyEl.setHeight(bodyHeight);
17896 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17898 this.touchViewEl.addClass(['in','show']);
17901 if(this._touchViewMask){
17902 Roo.get(document.body).addClass("x-body-masked");
17903 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17904 this._touchViewMask.setStyle('z-index', 10000);
17905 this._touchViewMask.addClass('show');
17908 this.doTouchViewQuery();
17912 hideTouchView : function()
17914 this.touchViewEl.removeClass(['in','show']);
17918 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17920 this.touchViewEl.setStyle('display', 'none');
17923 if(this._touchViewMask){
17924 this._touchViewMask.removeClass('show');
17925 Roo.get(document.body).removeClass("x-body-masked");
17929 setTouchViewValue : function()
17936 Roo.each(this.tickItems, function(o){
17941 this.hideTouchView();
17944 doTouchViewQuery : function()
17953 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17957 if(!this.alwaysQuery || this.mode == 'local'){
17958 this.onTouchViewLoad();
17965 onTouchViewBeforeLoad : function(combo,opts)
17971 onTouchViewLoad : function()
17973 if(this.store.getCount() < 1){
17974 this.onTouchViewEmptyResults();
17978 this.clearTouchView();
17980 var rawValue = this.getRawValue();
17982 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17984 this.tickItems = [];
17986 this.store.data.each(function(d, rowIndex){
17987 var row = this.touchViewListGroup.createChild(template);
17989 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17990 row.addClass(d.data.cls);
17993 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17996 html : d.data[this.displayField]
17999 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18000 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18003 row.removeClass('selected');
18004 if(!this.multiple && this.valueField &&
18005 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18008 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18009 row.addClass('selected');
18012 if(this.multiple && this.valueField &&
18013 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18017 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18018 this.tickItems.push(d.data);
18021 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18025 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18027 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18029 if(this.modalTitle.length){
18030 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18033 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18035 if(this.mobile_restrict_height && listHeight < bodyHeight){
18036 this.touchViewBodyEl.setHeight(listHeight);
18041 if(firstChecked && listHeight > bodyHeight){
18042 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18047 onTouchViewLoadException : function()
18049 this.hideTouchView();
18052 onTouchViewEmptyResults : function()
18054 this.clearTouchView();
18056 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18058 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18062 clearTouchView : function()
18064 this.touchViewListGroup.dom.innerHTML = '';
18067 onTouchViewClick : function(e, el, o)
18069 e.preventDefault();
18072 var rowIndex = o.rowIndex;
18074 var r = this.store.getAt(rowIndex);
18076 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18078 if(!this.multiple){
18079 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18080 c.dom.removeAttribute('checked');
18083 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18085 this.setFromData(r.data);
18087 var close = this.closeTriggerEl();
18093 this.hideTouchView();
18095 this.fireEvent('select', this, r, rowIndex);
18100 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18101 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18102 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18106 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18107 this.addItem(r.data);
18108 this.tickItems.push(r.data);
18112 getAutoCreateNativeIOS : function()
18115 cls: 'form-group' //input-group,
18120 cls : 'roo-ios-select'
18124 combobox.name = this.name;
18127 if (this.disabled) {
18128 combobox.disabled = true;
18131 var settings = this;
18133 ['xs','sm','md','lg'].map(function(size){
18134 if (settings[size]) {
18135 cfg.cls += ' col-' + size + '-' + settings[size];
18145 initIOSView : function()
18147 this.store.on('load', this.onIOSViewLoad, this);
18152 onIOSViewLoad : function()
18154 if(this.store.getCount() < 1){
18158 this.clearIOSView();
18160 if(this.allowBlank) {
18162 var default_text = '-- SELECT --';
18164 if(this.placeholder.length){
18165 default_text = this.placeholder;
18168 if(this.emptyTitle.length){
18169 default_text += ' - ' + this.emptyTitle + ' -';
18172 var opt = this.inputEl().createChild({
18175 html : default_text
18179 o[this.valueField] = 0;
18180 o[this.displayField] = default_text;
18182 this.ios_options.push({
18189 this.store.data.each(function(d, rowIndex){
18193 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18194 html = d.data[this.displayField];
18199 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18200 value = d.data[this.valueField];
18209 if(this.value == d.data[this.valueField]){
18210 option['selected'] = true;
18213 var opt = this.inputEl().createChild(option);
18215 this.ios_options.push({
18222 this.inputEl().on('change', function(){
18223 this.fireEvent('select', this);
18228 clearIOSView: function()
18230 this.inputEl().dom.innerHTML = '';
18232 this.ios_options = [];
18235 setIOSValue: function(v)
18239 if(!this.ios_options){
18243 Roo.each(this.ios_options, function(opts){
18245 opts.el.dom.removeAttribute('selected');
18247 if(opts.data[this.valueField] != v){
18251 opts.el.dom.setAttribute('selected', true);
18257 * @cfg {Boolean} grow
18261 * @cfg {Number} growMin
18265 * @cfg {Number} growMax
18274 Roo.apply(Roo.bootstrap.ComboBox, {
18278 cls: 'modal-header',
18300 cls: 'list-group-item',
18304 cls: 'roo-combobox-list-group-item-value'
18308 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18322 listItemCheckbox : {
18324 cls: 'list-group-item',
18328 cls: 'roo-combobox-list-group-item-value'
18332 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18348 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18353 cls: 'modal-footer',
18361 cls: 'col-xs-6 text-left',
18364 cls: 'btn btn-danger roo-touch-view-cancel',
18370 cls: 'col-xs-6 text-right',
18373 cls: 'btn btn-success roo-touch-view-ok',
18384 Roo.apply(Roo.bootstrap.ComboBox, {
18386 touchViewTemplate : {
18388 cls: 'modal fade roo-combobox-touch-view',
18392 cls: 'modal-dialog',
18393 style : 'position:fixed', // we have to fix position....
18397 cls: 'modal-content',
18399 Roo.bootstrap.ComboBox.header,
18400 Roo.bootstrap.ComboBox.body,
18401 Roo.bootstrap.ComboBox.footer
18410 * Ext JS Library 1.1.1
18411 * Copyright(c) 2006-2007, Ext JS, LLC.
18413 * Originally Released Under LGPL - original licence link has changed is not relivant.
18416 * <script type="text/javascript">
18421 * @extends Roo.util.Observable
18422 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18423 * This class also supports single and multi selection modes. <br>
18424 * Create a data model bound view:
18426 var store = new Roo.data.Store(...);
18428 var view = new Roo.View({
18430 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18432 singleSelect: true,
18433 selectedClass: "ydataview-selected",
18437 // listen for node click?
18438 view.on("click", function(vw, index, node, e){
18439 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18443 dataModel.load("foobar.xml");
18445 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18447 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18448 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18450 * Note: old style constructor is still suported (container, template, config)
18453 * Create a new View
18454 * @param {Object} config The config object
18457 Roo.View = function(config, depreciated_tpl, depreciated_config){
18459 this.parent = false;
18461 if (typeof(depreciated_tpl) == 'undefined') {
18462 // new way.. - universal constructor.
18463 Roo.apply(this, config);
18464 this.el = Roo.get(this.el);
18467 this.el = Roo.get(config);
18468 this.tpl = depreciated_tpl;
18469 Roo.apply(this, depreciated_config);
18471 this.wrapEl = this.el.wrap().wrap();
18472 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18475 if(typeof(this.tpl) == "string"){
18476 this.tpl = new Roo.Template(this.tpl);
18478 // support xtype ctors..
18479 this.tpl = new Roo.factory(this.tpl, Roo);
18483 this.tpl.compile();
18488 * @event beforeclick
18489 * Fires before a click is processed. Returns false to cancel the default action.
18490 * @param {Roo.View} this
18491 * @param {Number} index The index of the target node
18492 * @param {HTMLElement} node The target node
18493 * @param {Roo.EventObject} e The raw event object
18495 "beforeclick" : true,
18498 * Fires when a template node is clicked.
18499 * @param {Roo.View} this
18500 * @param {Number} index The index of the target node
18501 * @param {HTMLElement} node The target node
18502 * @param {Roo.EventObject} e The raw event object
18507 * Fires when a template node is double clicked.
18508 * @param {Roo.View} this
18509 * @param {Number} index The index of the target node
18510 * @param {HTMLElement} node The target node
18511 * @param {Roo.EventObject} e The raw event object
18515 * @event contextmenu
18516 * Fires when a template node is right clicked.
18517 * @param {Roo.View} this
18518 * @param {Number} index The index of the target node
18519 * @param {HTMLElement} node The target node
18520 * @param {Roo.EventObject} e The raw event object
18522 "contextmenu" : true,
18524 * @event selectionchange
18525 * Fires when the selected nodes change.
18526 * @param {Roo.View} this
18527 * @param {Array} selections Array of the selected nodes
18529 "selectionchange" : true,
18532 * @event beforeselect
18533 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18534 * @param {Roo.View} this
18535 * @param {HTMLElement} node The node to be selected
18536 * @param {Array} selections Array of currently selected nodes
18538 "beforeselect" : true,
18540 * @event preparedata
18541 * Fires on every row to render, to allow you to change the data.
18542 * @param {Roo.View} this
18543 * @param {Object} data to be rendered (change this)
18545 "preparedata" : true
18553 "click": this.onClick,
18554 "dblclick": this.onDblClick,
18555 "contextmenu": this.onContextMenu,
18559 this.selections = [];
18561 this.cmp = new Roo.CompositeElementLite([]);
18563 this.store = Roo.factory(this.store, Roo.data);
18564 this.setStore(this.store, true);
18567 if ( this.footer && this.footer.xtype) {
18569 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18571 this.footer.dataSource = this.store;
18572 this.footer.container = fctr;
18573 this.footer = Roo.factory(this.footer, Roo);
18574 fctr.insertFirst(this.el);
18576 // this is a bit insane - as the paging toolbar seems to detach the el..
18577 // dom.parentNode.parentNode.parentNode
18578 // they get detached?
18582 Roo.View.superclass.constructor.call(this);
18587 Roo.extend(Roo.View, Roo.util.Observable, {
18590 * @cfg {Roo.data.Store} store Data store to load data from.
18595 * @cfg {String|Roo.Element} el The container element.
18600 * @cfg {String|Roo.Template} tpl The template used by this View
18604 * @cfg {String} dataName the named area of the template to use as the data area
18605 * Works with domtemplates roo-name="name"
18609 * @cfg {String} selectedClass The css class to add to selected nodes
18611 selectedClass : "x-view-selected",
18613 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18618 * @cfg {String} text to display on mask (default Loading)
18622 * @cfg {Boolean} multiSelect Allow multiple selection
18624 multiSelect : false,
18626 * @cfg {Boolean} singleSelect Allow single selection
18628 singleSelect: false,
18631 * @cfg {Boolean} toggleSelect - selecting
18633 toggleSelect : false,
18636 * @cfg {Boolean} tickable - selecting
18641 * Returns the element this view is bound to.
18642 * @return {Roo.Element}
18644 getEl : function(){
18645 return this.wrapEl;
18651 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18653 refresh : function(){
18654 //Roo.log('refresh');
18657 // if we are using something like 'domtemplate', then
18658 // the what gets used is:
18659 // t.applySubtemplate(NAME, data, wrapping data..)
18660 // the outer template then get' applied with
18661 // the store 'extra data'
18662 // and the body get's added to the
18663 // roo-name="data" node?
18664 // <span class='roo-tpl-{name}'></span> ?????
18668 this.clearSelections();
18669 this.el.update("");
18671 var records = this.store.getRange();
18672 if(records.length < 1) {
18674 // is this valid?? = should it render a template??
18676 this.el.update(this.emptyText);
18680 if (this.dataName) {
18681 this.el.update(t.apply(this.store.meta)); //????
18682 el = this.el.child('.roo-tpl-' + this.dataName);
18685 for(var i = 0, len = records.length; i < len; i++){
18686 var data = this.prepareData(records[i].data, i, records[i]);
18687 this.fireEvent("preparedata", this, data, i, records[i]);
18689 var d = Roo.apply({}, data);
18692 Roo.apply(d, {'roo-id' : Roo.id()});
18696 Roo.each(this.parent.item, function(item){
18697 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18700 Roo.apply(d, {'roo-data-checked' : 'checked'});
18704 html[html.length] = Roo.util.Format.trim(
18706 t.applySubtemplate(this.dataName, d, this.store.meta) :
18713 el.update(html.join(""));
18714 this.nodes = el.dom.childNodes;
18715 this.updateIndexes(0);
18720 * Function to override to reformat the data that is sent to
18721 * the template for each node.
18722 * DEPRICATED - use the preparedata event handler.
18723 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18724 * a JSON object for an UpdateManager bound view).
18726 prepareData : function(data, index, record)
18728 this.fireEvent("preparedata", this, data, index, record);
18732 onUpdate : function(ds, record){
18733 // Roo.log('on update');
18734 this.clearSelections();
18735 var index = this.store.indexOf(record);
18736 var n = this.nodes[index];
18737 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18738 n.parentNode.removeChild(n);
18739 this.updateIndexes(index, index);
18745 onAdd : function(ds, records, index)
18747 //Roo.log(['on Add', ds, records, index] );
18748 this.clearSelections();
18749 if(this.nodes.length == 0){
18753 var n = this.nodes[index];
18754 for(var i = 0, len = records.length; i < len; i++){
18755 var d = this.prepareData(records[i].data, i, records[i]);
18757 this.tpl.insertBefore(n, d);
18760 this.tpl.append(this.el, d);
18763 this.updateIndexes(index);
18766 onRemove : function(ds, record, index){
18767 // Roo.log('onRemove');
18768 this.clearSelections();
18769 var el = this.dataName ?
18770 this.el.child('.roo-tpl-' + this.dataName) :
18773 el.dom.removeChild(this.nodes[index]);
18774 this.updateIndexes(index);
18778 * Refresh an individual node.
18779 * @param {Number} index
18781 refreshNode : function(index){
18782 this.onUpdate(this.store, this.store.getAt(index));
18785 updateIndexes : function(startIndex, endIndex){
18786 var ns = this.nodes;
18787 startIndex = startIndex || 0;
18788 endIndex = endIndex || ns.length - 1;
18789 for(var i = startIndex; i <= endIndex; i++){
18790 ns[i].nodeIndex = i;
18795 * Changes the data store this view uses and refresh the view.
18796 * @param {Store} store
18798 setStore : function(store, initial){
18799 if(!initial && this.store){
18800 this.store.un("datachanged", this.refresh);
18801 this.store.un("add", this.onAdd);
18802 this.store.un("remove", this.onRemove);
18803 this.store.un("update", this.onUpdate);
18804 this.store.un("clear", this.refresh);
18805 this.store.un("beforeload", this.onBeforeLoad);
18806 this.store.un("load", this.onLoad);
18807 this.store.un("loadexception", this.onLoad);
18811 store.on("datachanged", this.refresh, this);
18812 store.on("add", this.onAdd, this);
18813 store.on("remove", this.onRemove, this);
18814 store.on("update", this.onUpdate, this);
18815 store.on("clear", this.refresh, this);
18816 store.on("beforeload", this.onBeforeLoad, this);
18817 store.on("load", this.onLoad, this);
18818 store.on("loadexception", this.onLoad, this);
18826 * onbeforeLoad - masks the loading area.
18829 onBeforeLoad : function(store,opts)
18831 //Roo.log('onBeforeLoad');
18833 this.el.update("");
18835 this.el.mask(this.mask ? this.mask : "Loading" );
18837 onLoad : function ()
18844 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18845 * @param {HTMLElement} node
18846 * @return {HTMLElement} The template node
18848 findItemFromChild : function(node){
18849 var el = this.dataName ?
18850 this.el.child('.roo-tpl-' + this.dataName,true) :
18853 if(!node || node.parentNode == el){
18856 var p = node.parentNode;
18857 while(p && p != el){
18858 if(p.parentNode == el){
18867 onClick : function(e){
18868 var item = this.findItemFromChild(e.getTarget());
18870 var index = this.indexOf(item);
18871 if(this.onItemClick(item, index, e) !== false){
18872 this.fireEvent("click", this, index, item, e);
18875 this.clearSelections();
18880 onContextMenu : function(e){
18881 var item = this.findItemFromChild(e.getTarget());
18883 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18888 onDblClick : function(e){
18889 var item = this.findItemFromChild(e.getTarget());
18891 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18895 onItemClick : function(item, index, e)
18897 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18900 if (this.toggleSelect) {
18901 var m = this.isSelected(item) ? 'unselect' : 'select';
18904 _t[m](item, true, false);
18907 if(this.multiSelect || this.singleSelect){
18908 if(this.multiSelect && e.shiftKey && this.lastSelection){
18909 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18911 this.select(item, this.multiSelect && e.ctrlKey);
18912 this.lastSelection = item;
18915 if(!this.tickable){
18916 e.preventDefault();
18924 * Get the number of selected nodes.
18927 getSelectionCount : function(){
18928 return this.selections.length;
18932 * Get the currently selected nodes.
18933 * @return {Array} An array of HTMLElements
18935 getSelectedNodes : function(){
18936 return this.selections;
18940 * Get the indexes of the selected nodes.
18943 getSelectedIndexes : function(){
18944 var indexes = [], s = this.selections;
18945 for(var i = 0, len = s.length; i < len; i++){
18946 indexes.push(s[i].nodeIndex);
18952 * Clear all selections
18953 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18955 clearSelections : function(suppressEvent){
18956 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18957 this.cmp.elements = this.selections;
18958 this.cmp.removeClass(this.selectedClass);
18959 this.selections = [];
18960 if(!suppressEvent){
18961 this.fireEvent("selectionchange", this, this.selections);
18967 * Returns true if the passed node is selected
18968 * @param {HTMLElement/Number} node The node or node index
18969 * @return {Boolean}
18971 isSelected : function(node){
18972 var s = this.selections;
18976 node = this.getNode(node);
18977 return s.indexOf(node) !== -1;
18982 * @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
18983 * @param {Boolean} keepExisting (optional) true to keep existing selections
18984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18986 select : function(nodeInfo, keepExisting, suppressEvent){
18987 if(nodeInfo instanceof Array){
18989 this.clearSelections(true);
18991 for(var i = 0, len = nodeInfo.length; i < len; i++){
18992 this.select(nodeInfo[i], true, true);
18996 var node = this.getNode(nodeInfo);
18997 if(!node || this.isSelected(node)){
18998 return; // already selected.
19001 this.clearSelections(true);
19004 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19005 Roo.fly(node).addClass(this.selectedClass);
19006 this.selections.push(node);
19007 if(!suppressEvent){
19008 this.fireEvent("selectionchange", this, this.selections);
19016 * @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
19017 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19018 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19020 unselect : function(nodeInfo, keepExisting, suppressEvent)
19022 if(nodeInfo instanceof Array){
19023 Roo.each(this.selections, function(s) {
19024 this.unselect(s, nodeInfo);
19028 var node = this.getNode(nodeInfo);
19029 if(!node || !this.isSelected(node)){
19030 //Roo.log("not selected");
19031 return; // not selected.
19035 Roo.each(this.selections, function(s) {
19037 Roo.fly(node).removeClass(this.selectedClass);
19044 this.selections= ns;
19045 this.fireEvent("selectionchange", this, this.selections);
19049 * Gets a template node.
19050 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19051 * @return {HTMLElement} The node or null if it wasn't found
19053 getNode : function(nodeInfo){
19054 if(typeof nodeInfo == "string"){
19055 return document.getElementById(nodeInfo);
19056 }else if(typeof nodeInfo == "number"){
19057 return this.nodes[nodeInfo];
19063 * Gets a range template nodes.
19064 * @param {Number} startIndex
19065 * @param {Number} endIndex
19066 * @return {Array} An array of nodes
19068 getNodes : function(start, end){
19069 var ns = this.nodes;
19070 start = start || 0;
19071 end = typeof end == "undefined" ? ns.length - 1 : end;
19074 for(var i = start; i <= end; i++){
19078 for(var i = start; i >= end; i--){
19086 * Finds the index of the passed node
19087 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19088 * @return {Number} The index of the node or -1
19090 indexOf : function(node){
19091 node = this.getNode(node);
19092 if(typeof node.nodeIndex == "number"){
19093 return node.nodeIndex;
19095 var ns = this.nodes;
19096 for(var i = 0, len = ns.length; i < len; i++){
19107 * based on jquery fullcalendar
19111 Roo.bootstrap = Roo.bootstrap || {};
19113 * @class Roo.bootstrap.Calendar
19114 * @extends Roo.bootstrap.Component
19115 * Bootstrap Calendar class
19116 * @cfg {Boolean} loadMask (true|false) default false
19117 * @cfg {Object} header generate the user specific header of the calendar, default false
19120 * Create a new Container
19121 * @param {Object} config The config object
19126 Roo.bootstrap.Calendar = function(config){
19127 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19131 * Fires when a date is selected
19132 * @param {DatePicker} this
19133 * @param {Date} date The selected date
19137 * @event monthchange
19138 * Fires when the displayed month changes
19139 * @param {DatePicker} this
19140 * @param {Date} date The selected month
19142 'monthchange': true,
19144 * @event evententer
19145 * Fires when mouse over an event
19146 * @param {Calendar} this
19147 * @param {event} Event
19149 'evententer': true,
19151 * @event eventleave
19152 * Fires when the mouse leaves an
19153 * @param {Calendar} this
19156 'eventleave': true,
19158 * @event eventclick
19159 * Fires when the mouse click an
19160 * @param {Calendar} this
19169 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19172 * @cfg {Number} startDay
19173 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19181 getAutoCreate : function(){
19184 var fc_button = function(name, corner, style, content ) {
19185 return Roo.apply({},{
19187 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19189 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19192 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19203 style : 'width:100%',
19210 cls : 'fc-header-left',
19212 fc_button('prev', 'left', 'arrow', '‹' ),
19213 fc_button('next', 'right', 'arrow', '›' ),
19214 { tag: 'span', cls: 'fc-header-space' },
19215 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19223 cls : 'fc-header-center',
19227 cls: 'fc-header-title',
19230 html : 'month / year'
19238 cls : 'fc-header-right',
19240 /* fc_button('month', 'left', '', 'month' ),
19241 fc_button('week', '', '', 'week' ),
19242 fc_button('day', 'right', '', 'day' )
19254 header = this.header;
19257 var cal_heads = function() {
19259 // fixme - handle this.
19261 for (var i =0; i < Date.dayNames.length; i++) {
19262 var d = Date.dayNames[i];
19265 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19266 html : d.substring(0,3)
19270 ret[0].cls += ' fc-first';
19271 ret[6].cls += ' fc-last';
19274 var cal_cell = function(n) {
19277 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19282 cls: 'fc-day-number',
19286 cls: 'fc-day-content',
19290 style: 'position: relative;' // height: 17px;
19302 var cal_rows = function() {
19305 for (var r = 0; r < 6; r++) {
19312 for (var i =0; i < Date.dayNames.length; i++) {
19313 var d = Date.dayNames[i];
19314 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19317 row.cn[0].cls+=' fc-first';
19318 row.cn[0].cn[0].style = 'min-height:90px';
19319 row.cn[6].cls+=' fc-last';
19323 ret[0].cls += ' fc-first';
19324 ret[4].cls += ' fc-prev-last';
19325 ret[5].cls += ' fc-last';
19332 cls: 'fc-border-separate',
19333 style : 'width:100%',
19341 cls : 'fc-first fc-last',
19359 cls : 'fc-content',
19360 style : "position: relative;",
19363 cls : 'fc-view fc-view-month fc-grid',
19364 style : 'position: relative',
19365 unselectable : 'on',
19368 cls : 'fc-event-container',
19369 style : 'position:absolute;z-index:8;top:0;left:0;'
19387 initEvents : function()
19390 throw "can not find store for calendar";
19396 style: "text-align:center",
19400 style: "background-color:white;width:50%;margin:250 auto",
19404 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19415 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19417 var size = this.el.select('.fc-content', true).first().getSize();
19418 this.maskEl.setSize(size.width, size.height);
19419 this.maskEl.enableDisplayMode("block");
19420 if(!this.loadMask){
19421 this.maskEl.hide();
19424 this.store = Roo.factory(this.store, Roo.data);
19425 this.store.on('load', this.onLoad, this);
19426 this.store.on('beforeload', this.onBeforeLoad, this);
19430 this.cells = this.el.select('.fc-day',true);
19431 //Roo.log(this.cells);
19432 this.textNodes = this.el.query('.fc-day-number');
19433 this.cells.addClassOnOver('fc-state-hover');
19435 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19436 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19437 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19438 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19440 this.on('monthchange', this.onMonthChange, this);
19442 this.update(new Date().clearTime());
19445 resize : function() {
19446 var sz = this.el.getSize();
19448 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19449 this.el.select('.fc-day-content div',true).setHeight(34);
19454 showPrevMonth : function(e){
19455 this.update(this.activeDate.add("mo", -1));
19457 showToday : function(e){
19458 this.update(new Date().clearTime());
19461 showNextMonth : function(e){
19462 this.update(this.activeDate.add("mo", 1));
19466 showPrevYear : function(){
19467 this.update(this.activeDate.add("y", -1));
19471 showNextYear : function(){
19472 this.update(this.activeDate.add("y", 1));
19477 update : function(date)
19479 var vd = this.activeDate;
19480 this.activeDate = date;
19481 // if(vd && this.el){
19482 // var t = date.getTime();
19483 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19484 // Roo.log('using add remove');
19486 // this.fireEvent('monthchange', this, date);
19488 // this.cells.removeClass("fc-state-highlight");
19489 // this.cells.each(function(c){
19490 // if(c.dateValue == t){
19491 // c.addClass("fc-state-highlight");
19492 // setTimeout(function(){
19493 // try{c.dom.firstChild.focus();}catch(e){}
19503 var days = date.getDaysInMonth();
19505 var firstOfMonth = date.getFirstDateOfMonth();
19506 var startingPos = firstOfMonth.getDay()-this.startDay;
19508 if(startingPos < this.startDay){
19512 var pm = date.add(Date.MONTH, -1);
19513 var prevStart = pm.getDaysInMonth()-startingPos;
19515 this.cells = this.el.select('.fc-day',true);
19516 this.textNodes = this.el.query('.fc-day-number');
19517 this.cells.addClassOnOver('fc-state-hover');
19519 var cells = this.cells.elements;
19520 var textEls = this.textNodes;
19522 Roo.each(cells, function(cell){
19523 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19526 days += startingPos;
19528 // convert everything to numbers so it's fast
19529 var day = 86400000;
19530 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19533 //Roo.log(prevStart);
19535 var today = new Date().clearTime().getTime();
19536 var sel = date.clearTime().getTime();
19537 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19538 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19539 var ddMatch = this.disabledDatesRE;
19540 var ddText = this.disabledDatesText;
19541 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19542 var ddaysText = this.disabledDaysText;
19543 var format = this.format;
19545 var setCellClass = function(cal, cell){
19549 //Roo.log('set Cell Class');
19551 var t = d.getTime();
19555 cell.dateValue = t;
19557 cell.className += " fc-today";
19558 cell.className += " fc-state-highlight";
19559 cell.title = cal.todayText;
19562 // disable highlight in other month..
19563 //cell.className += " fc-state-highlight";
19568 cell.className = " fc-state-disabled";
19569 cell.title = cal.minText;
19573 cell.className = " fc-state-disabled";
19574 cell.title = cal.maxText;
19578 if(ddays.indexOf(d.getDay()) != -1){
19579 cell.title = ddaysText;
19580 cell.className = " fc-state-disabled";
19583 if(ddMatch && format){
19584 var fvalue = d.dateFormat(format);
19585 if(ddMatch.test(fvalue)){
19586 cell.title = ddText.replace("%0", fvalue);
19587 cell.className = " fc-state-disabled";
19591 if (!cell.initialClassName) {
19592 cell.initialClassName = cell.dom.className;
19595 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19600 for(; i < startingPos; i++) {
19601 textEls[i].innerHTML = (++prevStart);
19602 d.setDate(d.getDate()+1);
19604 cells[i].className = "fc-past fc-other-month";
19605 setCellClass(this, cells[i]);
19610 for(; i < days; i++){
19611 intDay = i - startingPos + 1;
19612 textEls[i].innerHTML = (intDay);
19613 d.setDate(d.getDate()+1);
19615 cells[i].className = ''; // "x-date-active";
19616 setCellClass(this, cells[i]);
19620 for(; i < 42; i++) {
19621 textEls[i].innerHTML = (++extraDays);
19622 d.setDate(d.getDate()+1);
19624 cells[i].className = "fc-future fc-other-month";
19625 setCellClass(this, cells[i]);
19628 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19630 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19632 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19633 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19635 if(totalRows != 6){
19636 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19637 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19640 this.fireEvent('monthchange', this, date);
19644 if(!this.internalRender){
19645 var main = this.el.dom.firstChild;
19646 var w = main.offsetWidth;
19647 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19648 Roo.fly(main).setWidth(w);
19649 this.internalRender = true;
19650 // opera does not respect the auto grow header center column
19651 // then, after it gets a width opera refuses to recalculate
19652 // without a second pass
19653 if(Roo.isOpera && !this.secondPass){
19654 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19655 this.secondPass = true;
19656 this.update.defer(10, this, [date]);
19663 findCell : function(dt) {
19664 dt = dt.clearTime().getTime();
19666 this.cells.each(function(c){
19667 //Roo.log("check " +c.dateValue + '?=' + dt);
19668 if(c.dateValue == dt){
19678 findCells : function(ev) {
19679 var s = ev.start.clone().clearTime().getTime();
19681 var e= ev.end.clone().clearTime().getTime();
19684 this.cells.each(function(c){
19685 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19687 if(c.dateValue > e){
19690 if(c.dateValue < s){
19699 // findBestRow: function(cells)
19703 // for (var i =0 ; i < cells.length;i++) {
19704 // ret = Math.max(cells[i].rows || 0,ret);
19711 addItem : function(ev)
19713 // look for vertical location slot in
19714 var cells = this.findCells(ev);
19716 // ev.row = this.findBestRow(cells);
19718 // work out the location.
19722 for(var i =0; i < cells.length; i++) {
19724 cells[i].row = cells[0].row;
19727 cells[i].row = cells[i].row + 1;
19737 if (crow.start.getY() == cells[i].getY()) {
19739 crow.end = cells[i];
19756 cells[0].events.push(ev);
19758 this.calevents.push(ev);
19761 clearEvents: function() {
19763 if(!this.calevents){
19767 Roo.each(this.cells.elements, function(c){
19773 Roo.each(this.calevents, function(e) {
19774 Roo.each(e.els, function(el) {
19775 el.un('mouseenter' ,this.onEventEnter, this);
19776 el.un('mouseleave' ,this.onEventLeave, this);
19781 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19787 renderEvents: function()
19791 this.cells.each(function(c) {
19800 if(c.row != c.events.length){
19801 r = 4 - (4 - (c.row - c.events.length));
19804 c.events = ev.slice(0, r);
19805 c.more = ev.slice(r);
19807 if(c.more.length && c.more.length == 1){
19808 c.events.push(c.more.pop());
19811 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19815 this.cells.each(function(c) {
19817 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19820 for (var e = 0; e < c.events.length; e++){
19821 var ev = c.events[e];
19822 var rows = ev.rows;
19824 for(var i = 0; i < rows.length; i++) {
19826 // how many rows should it span..
19829 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19830 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19832 unselectable : "on",
19835 cls: 'fc-event-inner',
19839 // cls: 'fc-event-time',
19840 // html : cells.length > 1 ? '' : ev.time
19844 cls: 'fc-event-title',
19845 html : String.format('{0}', ev.title)
19852 cls: 'ui-resizable-handle ui-resizable-e',
19853 html : '  '
19860 cfg.cls += ' fc-event-start';
19862 if ((i+1) == rows.length) {
19863 cfg.cls += ' fc-event-end';
19866 var ctr = _this.el.select('.fc-event-container',true).first();
19867 var cg = ctr.createChild(cfg);
19869 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19870 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19872 var r = (c.more.length) ? 1 : 0;
19873 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19874 cg.setWidth(ebox.right - sbox.x -2);
19876 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19877 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19878 cg.on('click', _this.onEventClick, _this, ev);
19889 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19890 style : 'position: absolute',
19891 unselectable : "on",
19894 cls: 'fc-event-inner',
19898 cls: 'fc-event-title',
19906 cls: 'ui-resizable-handle ui-resizable-e',
19907 html : '  '
19913 var ctr = _this.el.select('.fc-event-container',true).first();
19914 var cg = ctr.createChild(cfg);
19916 var sbox = c.select('.fc-day-content',true).first().getBox();
19917 var ebox = c.select('.fc-day-content',true).first().getBox();
19919 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19920 cg.setWidth(ebox.right - sbox.x -2);
19922 cg.on('click', _this.onMoreEventClick, _this, c.more);
19932 onEventEnter: function (e, el,event,d) {
19933 this.fireEvent('evententer', this, el, event);
19936 onEventLeave: function (e, el,event,d) {
19937 this.fireEvent('eventleave', this, el, event);
19940 onEventClick: function (e, el,event,d) {
19941 this.fireEvent('eventclick', this, el, event);
19944 onMonthChange: function () {
19948 onMoreEventClick: function(e, el, more)
19952 this.calpopover.placement = 'right';
19953 this.calpopover.setTitle('More');
19955 this.calpopover.setContent('');
19957 var ctr = this.calpopover.el.select('.popover-content', true).first();
19959 Roo.each(more, function(m){
19961 cls : 'fc-event-hori fc-event-draggable',
19964 var cg = ctr.createChild(cfg);
19966 cg.on('click', _this.onEventClick, _this, m);
19969 this.calpopover.show(el);
19974 onLoad: function ()
19976 this.calevents = [];
19979 if(this.store.getCount() > 0){
19980 this.store.data.each(function(d){
19983 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19984 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19985 time : d.data.start_time,
19986 title : d.data.title,
19987 description : d.data.description,
19988 venue : d.data.venue
19993 this.renderEvents();
19995 if(this.calevents.length && this.loadMask){
19996 this.maskEl.hide();
20000 onBeforeLoad: function()
20002 this.clearEvents();
20004 this.maskEl.show();
20018 * @class Roo.bootstrap.Popover
20019 * @extends Roo.bootstrap.Component
20020 * Bootstrap Popover class
20021 * @cfg {String} html contents of the popover (or false to use children..)
20022 * @cfg {String} title of popover (or false to hide)
20023 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20024 * @cfg {String} trigger click || hover (or false to trigger manually)
20025 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20026 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20027 * - if false and it has a 'parent' then it will be automatically added to that element
20028 * - if string - Roo.get will be called
20029 * @cfg {Number} delay - delay before showing
20032 * Create a new Popover
20033 * @param {Object} config The config object
20036 Roo.bootstrap.Popover = function(config){
20037 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20043 * After the popover show
20045 * @param {Roo.bootstrap.Popover} this
20050 * After the popover hide
20052 * @param {Roo.bootstrap.Popover} this
20058 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20063 placement : 'right',
20064 trigger : 'hover', // hover
20070 can_build_overlaid : false,
20072 maskEl : false, // the mask element
20075 alignEl : false, // when show is called with an element - this get's stored.
20077 getChildContainer : function()
20079 return this.contentEl;
20082 getPopoverHeader : function()
20084 this.title = true; // flag not to hide it..
20085 this.headerEl.addClass('p-0');
20086 return this.headerEl
20090 getAutoCreate : function(){
20093 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20094 style: 'display:block',
20100 cls : 'popover-inner ',
20104 cls: 'popover-title popover-header',
20105 html : this.title === false ? '' : this.title
20108 cls : 'popover-content popover-body ' + (this.cls || ''),
20109 html : this.html || ''
20120 * @param {string} the title
20122 setTitle: function(str)
20126 this.headerEl.dom.innerHTML = str;
20131 * @param {string} the body content
20133 setContent: function(str)
20136 if (this.contentEl) {
20137 this.contentEl.dom.innerHTML = str;
20141 // as it get's added to the bottom of the page.
20142 onRender : function(ct, position)
20144 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20149 var cfg = Roo.apply({}, this.getAutoCreate());
20153 cfg.cls += ' ' + this.cls;
20156 cfg.style = this.style;
20158 //Roo.log("adding to ");
20159 this.el = Roo.get(document.body).createChild(cfg, position);
20160 // Roo.log(this.el);
20163 this.contentEl = this.el.select('.popover-content',true).first();
20164 this.headerEl = this.el.select('.popover-title',true).first();
20167 if(typeof(this.items) != 'undefined'){
20168 var items = this.items;
20171 for(var i =0;i < items.length;i++) {
20172 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20176 this.items = nitems;
20178 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20179 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20186 resizeMask : function()
20188 this.maskEl.setSize(
20189 Roo.lib.Dom.getViewWidth(true),
20190 Roo.lib.Dom.getViewHeight(true)
20194 initEvents : function()
20198 Roo.bootstrap.Popover.register(this);
20201 this.arrowEl = this.el.select('.arrow',true).first();
20202 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20203 this.el.enableDisplayMode('block');
20207 if (this.over === false && !this.parent()) {
20210 if (this.triggers === false) {
20215 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20216 var triggers = this.trigger ? this.trigger.split(' ') : [];
20217 Roo.each(triggers, function(trigger) {
20219 if (trigger == 'click') {
20220 on_el.on('click', this.toggle, this);
20221 } else if (trigger != 'manual') {
20222 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20223 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20225 on_el.on(eventIn ,this.enter, this);
20226 on_el.on(eventOut, this.leave, this);
20236 toggle : function () {
20237 this.hoverState == 'in' ? this.leave() : this.enter();
20240 enter : function () {
20242 clearTimeout(this.timeout);
20244 this.hoverState = 'in';
20246 if (!this.delay || !this.delay.show) {
20251 this.timeout = setTimeout(function () {
20252 if (_t.hoverState == 'in') {
20255 }, this.delay.show)
20258 leave : function() {
20259 clearTimeout(this.timeout);
20261 this.hoverState = 'out';
20263 if (!this.delay || !this.delay.hide) {
20268 this.timeout = setTimeout(function () {
20269 if (_t.hoverState == 'out') {
20272 }, this.delay.hide)
20276 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20277 * @param {string} (left|right|top|bottom) position
20279 show : function (on_el, placement)
20281 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20282 on_el = on_el || false; // default to false
20285 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20286 on_el = this.parent().el;
20287 } else if (this.over) {
20288 on_el = Roo.get(this.over);
20293 this.alignEl = Roo.get( on_el );
20296 this.render(document.body);
20302 if (this.title === false) {
20303 this.headerEl.hide();
20308 this.el.dom.style.display = 'block';
20311 if (this.alignEl) {
20312 this.updatePosition(this.placement, true);
20315 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20316 var es = this.el.getSize();
20317 var x = Roo.lib.Dom.getViewWidth()/2;
20318 var y = Roo.lib.Dom.getViewHeight()/2;
20319 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20324 //var arrow = this.el.select('.arrow',true).first();
20325 //arrow.set(align[2],
20327 this.el.addClass('in');
20331 this.hoverState = 'in';
20334 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20335 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20336 this.maskEl.dom.style.display = 'block';
20337 this.maskEl.addClass('show');
20339 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20341 this.fireEvent('show', this);
20345 * fire this manually after loading a grid in the table for example
20346 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20347 * @param {Boolean} try and move it if we cant get right position.
20349 updatePosition : function(placement, try_move)
20351 // allow for calling with no parameters
20352 placement = placement ? placement : this.placement;
20353 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20355 this.el.removeClass([
20356 'fade','top','bottom', 'left', 'right','in',
20357 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20359 this.el.addClass(placement + ' bs-popover-' + placement);
20361 if (!this.alignEl ) {
20365 switch (placement) {
20367 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20368 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20369 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20370 //normal display... or moved up/down.
20371 this.el.setXY(offset);
20372 var xy = this.alignEl.getAnchorXY('tr', false);
20374 this.arrowEl.setXY(xy);
20377 // continue through...
20378 return this.updatePosition('left', false);
20382 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20383 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20384 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20385 //normal display... or moved up/down.
20386 this.el.setXY(offset);
20387 var xy = this.alignEl.getAnchorXY('tl', false);
20388 xy[0]-=10;xy[1]+=5; // << fix me
20389 this.arrowEl.setXY(xy);
20393 return this.updatePosition('right', false);
20396 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20397 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20398 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20399 //normal display... or moved up/down.
20400 this.el.setXY(offset);
20401 var xy = this.alignEl.getAnchorXY('t', false);
20402 xy[1]-=10; // << fix me
20403 this.arrowEl.setXY(xy);
20407 return this.updatePosition('bottom', false);
20410 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20411 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20412 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20413 //normal display... or moved up/down.
20414 this.el.setXY(offset);
20415 var xy = this.alignEl.getAnchorXY('b', false);
20416 xy[1]+=2; // << fix me
20417 this.arrowEl.setXY(xy);
20421 return this.updatePosition('top', false);
20432 this.el.setXY([0,0]);
20433 this.el.removeClass('in');
20435 this.hoverState = null;
20436 this.maskEl.hide(); // always..
20437 this.fireEvent('hide', this);
20443 Roo.apply(Roo.bootstrap.Popover, {
20446 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20447 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20448 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20449 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20454 clickHander : false,
20458 onMouseDown : function(e)
20460 if (this.popups.length && !e.getTarget(".roo-popover")) {
20461 /// what is nothing is showing..
20470 register : function(popup)
20472 if (!Roo.bootstrap.Popover.clickHandler) {
20473 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20475 // hide other popups.
20476 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20477 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20478 this.hideAll(); //<< why?
20479 //this.popups.push(popup);
20481 hideAll : function()
20483 this.popups.forEach(function(p) {
20487 onShow : function() {
20488 Roo.bootstrap.Popover.popups.push(this);
20490 onHide : function() {
20491 Roo.bootstrap.Popover.popups.remove(this);
20497 * Card header - holder for the card header elements.
20502 * @class Roo.bootstrap.PopoverNav
20503 * @extends Roo.bootstrap.NavGroup
20504 * Bootstrap Popover header navigation class
20506 * Create a new Popover Header Navigation
20507 * @param {Object} config The config object
20510 Roo.bootstrap.PopoverNav = function(config){
20511 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20514 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20517 container_method : 'getPopoverHeader'
20535 * @class Roo.bootstrap.Progress
20536 * @extends Roo.bootstrap.Component
20537 * Bootstrap Progress class
20538 * @cfg {Boolean} striped striped of the progress bar
20539 * @cfg {Boolean} active animated of the progress bar
20543 * Create a new Progress
20544 * @param {Object} config The config object
20547 Roo.bootstrap.Progress = function(config){
20548 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20551 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20556 getAutoCreate : function(){
20564 cfg.cls += ' progress-striped';
20568 cfg.cls += ' active';
20587 * @class Roo.bootstrap.ProgressBar
20588 * @extends Roo.bootstrap.Component
20589 * Bootstrap ProgressBar class
20590 * @cfg {Number} aria_valuenow aria-value now
20591 * @cfg {Number} aria_valuemin aria-value min
20592 * @cfg {Number} aria_valuemax aria-value max
20593 * @cfg {String} label label for the progress bar
20594 * @cfg {String} panel (success | info | warning | danger )
20595 * @cfg {String} role role of the progress bar
20596 * @cfg {String} sr_only text
20600 * Create a new ProgressBar
20601 * @param {Object} config The config object
20604 Roo.bootstrap.ProgressBar = function(config){
20605 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20608 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20612 aria_valuemax : 100,
20618 getAutoCreate : function()
20623 cls: 'progress-bar',
20624 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20636 cfg.role = this.role;
20639 if(this.aria_valuenow){
20640 cfg['aria-valuenow'] = this.aria_valuenow;
20643 if(this.aria_valuemin){
20644 cfg['aria-valuemin'] = this.aria_valuemin;
20647 if(this.aria_valuemax){
20648 cfg['aria-valuemax'] = this.aria_valuemax;
20651 if(this.label && !this.sr_only){
20652 cfg.html = this.label;
20656 cfg.cls += ' progress-bar-' + this.panel;
20662 update : function(aria_valuenow)
20664 this.aria_valuenow = aria_valuenow;
20666 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20681 * @class Roo.bootstrap.TabGroup
20682 * @extends Roo.bootstrap.Column
20683 * Bootstrap Column class
20684 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20685 * @cfg {Boolean} carousel true to make the group behave like a carousel
20686 * @cfg {Boolean} bullets show bullets for the panels
20687 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20688 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20689 * @cfg {Boolean} showarrow (true|false) show arrow default true
20692 * Create a new TabGroup
20693 * @param {Object} config The config object
20696 Roo.bootstrap.TabGroup = function(config){
20697 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20699 this.navId = Roo.id();
20702 Roo.bootstrap.TabGroup.register(this);
20706 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20709 transition : false,
20714 slideOnTouch : false,
20717 getAutoCreate : function()
20719 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20721 cfg.cls += ' tab-content';
20723 if (this.carousel) {
20724 cfg.cls += ' carousel slide';
20727 cls : 'carousel-inner',
20731 if(this.bullets && !Roo.isTouch){
20734 cls : 'carousel-bullets',
20738 if(this.bullets_cls){
20739 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20746 cfg.cn[0].cn.push(bullets);
20749 if(this.showarrow){
20750 cfg.cn[0].cn.push({
20752 class : 'carousel-arrow',
20756 class : 'carousel-prev',
20760 class : 'fa fa-chevron-left'
20766 class : 'carousel-next',
20770 class : 'fa fa-chevron-right'
20783 initEvents: function()
20785 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20786 // this.el.on("touchstart", this.onTouchStart, this);
20789 if(this.autoslide){
20792 this.slideFn = window.setInterval(function() {
20793 _this.showPanelNext();
20797 if(this.showarrow){
20798 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20799 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20805 // onTouchStart : function(e, el, o)
20807 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20811 // this.showPanelNext();
20815 getChildContainer : function()
20817 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20821 * register a Navigation item
20822 * @param {Roo.bootstrap.NavItem} the navitem to add
20824 register : function(item)
20826 this.tabs.push( item);
20827 item.navId = this.navId; // not really needed..
20832 getActivePanel : function()
20835 Roo.each(this.tabs, function(t) {
20845 getPanelByName : function(n)
20848 Roo.each(this.tabs, function(t) {
20849 if (t.tabId == n) {
20857 indexOfPanel : function(p)
20860 Roo.each(this.tabs, function(t,i) {
20861 if (t.tabId == p.tabId) {
20870 * show a specific panel
20871 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20872 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20874 showPanel : function (pan)
20876 if(this.transition || typeof(pan) == 'undefined'){
20877 Roo.log("waiting for the transitionend");
20881 if (typeof(pan) == 'number') {
20882 pan = this.tabs[pan];
20885 if (typeof(pan) == 'string') {
20886 pan = this.getPanelByName(pan);
20889 var cur = this.getActivePanel();
20892 Roo.log('pan or acitve pan is undefined');
20896 if (pan.tabId == this.getActivePanel().tabId) {
20900 if (false === cur.fireEvent('beforedeactivate')) {
20904 if(this.bullets > 0 && !Roo.isTouch){
20905 this.setActiveBullet(this.indexOfPanel(pan));
20908 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20910 //class="carousel-item carousel-item-next carousel-item-left"
20912 this.transition = true;
20913 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20914 var lr = dir == 'next' ? 'left' : 'right';
20915 pan.el.addClass(dir); // or prev
20916 pan.el.addClass('carousel-item-' + dir); // or prev
20917 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20918 cur.el.addClass(lr); // or right
20919 pan.el.addClass(lr);
20920 cur.el.addClass('carousel-item-' +lr); // or right
20921 pan.el.addClass('carousel-item-' +lr);
20925 cur.el.on('transitionend', function() {
20926 Roo.log("trans end?");
20928 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20929 pan.setActive(true);
20931 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20932 cur.setActive(false);
20934 _this.transition = false;
20936 }, this, { single: true } );
20941 cur.setActive(false);
20942 pan.setActive(true);
20947 showPanelNext : function()
20949 var i = this.indexOfPanel(this.getActivePanel());
20951 if (i >= this.tabs.length - 1 && !this.autoslide) {
20955 if (i >= this.tabs.length - 1 && this.autoslide) {
20959 this.showPanel(this.tabs[i+1]);
20962 showPanelPrev : function()
20964 var i = this.indexOfPanel(this.getActivePanel());
20966 if (i < 1 && !this.autoslide) {
20970 if (i < 1 && this.autoslide) {
20971 i = this.tabs.length;
20974 this.showPanel(this.tabs[i-1]);
20978 addBullet: function()
20980 if(!this.bullets || Roo.isTouch){
20983 var ctr = this.el.select('.carousel-bullets',true).first();
20984 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20985 var bullet = ctr.createChild({
20986 cls : 'bullet bullet-' + i
20987 },ctr.dom.lastChild);
20992 bullet.on('click', (function(e, el, o, ii, t){
20994 e.preventDefault();
20996 this.showPanel(ii);
20998 if(this.autoslide && this.slideFn){
20999 clearInterval(this.slideFn);
21000 this.slideFn = window.setInterval(function() {
21001 _this.showPanelNext();
21005 }).createDelegate(this, [i, bullet], true));
21010 setActiveBullet : function(i)
21016 Roo.each(this.el.select('.bullet', true).elements, function(el){
21017 el.removeClass('selected');
21020 var bullet = this.el.select('.bullet-' + i, true).first();
21026 bullet.addClass('selected');
21037 Roo.apply(Roo.bootstrap.TabGroup, {
21041 * register a Navigation Group
21042 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21044 register : function(navgrp)
21046 this.groups[navgrp.navId] = navgrp;
21050 * fetch a Navigation Group based on the navigation ID
21051 * if one does not exist , it will get created.
21052 * @param {string} the navgroup to add
21053 * @returns {Roo.bootstrap.NavGroup} the navgroup
21055 get: function(navId) {
21056 if (typeof(this.groups[navId]) == 'undefined') {
21057 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21059 return this.groups[navId] ;
21074 * @class Roo.bootstrap.TabPanel
21075 * @extends Roo.bootstrap.Component
21076 * Bootstrap TabPanel class
21077 * @cfg {Boolean} active panel active
21078 * @cfg {String} html panel content
21079 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21080 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21081 * @cfg {String} href click to link..
21082 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21086 * Create a new TabPanel
21087 * @param {Object} config The config object
21090 Roo.bootstrap.TabPanel = function(config){
21091 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21095 * Fires when the active status changes
21096 * @param {Roo.bootstrap.TabPanel} this
21097 * @param {Boolean} state the new state
21102 * @event beforedeactivate
21103 * Fires before a tab is de-activated - can be used to do validation on a form.
21104 * @param {Roo.bootstrap.TabPanel} this
21105 * @return {Boolean} false if there is an error
21108 'beforedeactivate': true
21111 this.tabId = this.tabId || Roo.id();
21115 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21122 touchSlide : false,
21123 getAutoCreate : function(){
21128 // item is needed for carousel - not sure if it has any effect otherwise
21129 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21130 html: this.html || ''
21134 cfg.cls += ' active';
21138 cfg.tabId = this.tabId;
21146 initEvents: function()
21148 var p = this.parent();
21150 this.navId = this.navId || p.navId;
21152 if (typeof(this.navId) != 'undefined') {
21153 // not really needed.. but just in case.. parent should be a NavGroup.
21154 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21158 var i = tg.tabs.length - 1;
21160 if(this.active && tg.bullets > 0 && i < tg.bullets){
21161 tg.setActiveBullet(i);
21165 this.el.on('click', this.onClick, this);
21167 if(Roo.isTouch && this.touchSlide){
21168 this.el.on("touchstart", this.onTouchStart, this);
21169 this.el.on("touchmove", this.onTouchMove, this);
21170 this.el.on("touchend", this.onTouchEnd, this);
21175 onRender : function(ct, position)
21177 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21180 setActive : function(state)
21182 Roo.log("panel - set active " + this.tabId + "=" + state);
21184 this.active = state;
21186 this.el.removeClass('active');
21188 } else if (!this.el.hasClass('active')) {
21189 this.el.addClass('active');
21192 this.fireEvent('changed', this, state);
21195 onClick : function(e)
21197 e.preventDefault();
21199 if(!this.href.length){
21203 window.location.href = this.href;
21212 onTouchStart : function(e)
21214 this.swiping = false;
21216 this.startX = e.browserEvent.touches[0].clientX;
21217 this.startY = e.browserEvent.touches[0].clientY;
21220 onTouchMove : function(e)
21222 this.swiping = true;
21224 this.endX = e.browserEvent.touches[0].clientX;
21225 this.endY = e.browserEvent.touches[0].clientY;
21228 onTouchEnd : function(e)
21235 var tabGroup = this.parent();
21237 if(this.endX > this.startX){ // swiping right
21238 tabGroup.showPanelPrev();
21242 if(this.startX > this.endX){ // swiping left
21243 tabGroup.showPanelNext();
21262 * @class Roo.bootstrap.DateField
21263 * @extends Roo.bootstrap.Input
21264 * Bootstrap DateField class
21265 * @cfg {Number} weekStart default 0
21266 * @cfg {String} viewMode default empty, (months|years)
21267 * @cfg {String} minViewMode default empty, (months|years)
21268 * @cfg {Number} startDate default -Infinity
21269 * @cfg {Number} endDate default Infinity
21270 * @cfg {Boolean} todayHighlight default false
21271 * @cfg {Boolean} todayBtn default false
21272 * @cfg {Boolean} calendarWeeks default false
21273 * @cfg {Object} daysOfWeekDisabled default empty
21274 * @cfg {Boolean} singleMode default false (true | false)
21276 * @cfg {Boolean} keyboardNavigation default true
21277 * @cfg {String} language default en
21280 * Create a new DateField
21281 * @param {Object} config The config object
21284 Roo.bootstrap.DateField = function(config){
21285 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21289 * Fires when this field show.
21290 * @param {Roo.bootstrap.DateField} this
21291 * @param {Mixed} date The date value
21296 * Fires when this field hide.
21297 * @param {Roo.bootstrap.DateField} this
21298 * @param {Mixed} date The date value
21303 * Fires when select a date.
21304 * @param {Roo.bootstrap.DateField} this
21305 * @param {Mixed} date The date value
21309 * @event beforeselect
21310 * Fires when before select a date.
21311 * @param {Roo.bootstrap.DateField} this
21312 * @param {Mixed} date The date value
21314 beforeselect : true
21318 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21321 * @cfg {String} format
21322 * The default date format string which can be overriden for localization support. The format must be
21323 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21327 * @cfg {String} altFormats
21328 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21329 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21331 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21339 todayHighlight : false,
21345 keyboardNavigation: true,
21347 calendarWeeks: false,
21349 startDate: -Infinity,
21353 daysOfWeekDisabled: [],
21357 singleMode : false,
21359 UTCDate: function()
21361 return new Date(Date.UTC.apply(Date, arguments));
21364 UTCToday: function()
21366 var today = new Date();
21367 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21370 getDate: function() {
21371 var d = this.getUTCDate();
21372 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21375 getUTCDate: function() {
21379 setDate: function(d) {
21380 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21383 setUTCDate: function(d) {
21385 this.setValue(this.formatDate(this.date));
21388 onRender: function(ct, position)
21391 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21393 this.language = this.language || 'en';
21394 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21395 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21397 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21398 this.format = this.format || 'm/d/y';
21399 this.isInline = false;
21400 this.isInput = true;
21401 this.component = this.el.select('.add-on', true).first() || false;
21402 this.component = (this.component && this.component.length === 0) ? false : this.component;
21403 this.hasInput = this.component && this.inputEl().length;
21405 if (typeof(this.minViewMode === 'string')) {
21406 switch (this.minViewMode) {
21408 this.minViewMode = 1;
21411 this.minViewMode = 2;
21414 this.minViewMode = 0;
21419 if (typeof(this.viewMode === 'string')) {
21420 switch (this.viewMode) {
21433 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21435 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21437 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21439 this.picker().on('mousedown', this.onMousedown, this);
21440 this.picker().on('click', this.onClick, this);
21442 this.picker().addClass('datepicker-dropdown');
21444 this.startViewMode = this.viewMode;
21446 if(this.singleMode){
21447 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21448 v.setVisibilityMode(Roo.Element.DISPLAY);
21452 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21453 v.setStyle('width', '189px');
21457 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21458 if(!this.calendarWeeks){
21463 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21464 v.attr('colspan', function(i, val){
21465 return parseInt(val) + 1;
21470 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21472 this.setStartDate(this.startDate);
21473 this.setEndDate(this.endDate);
21475 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21482 if(this.isInline) {
21487 picker : function()
21489 return this.pickerEl;
21490 // return this.el.select('.datepicker', true).first();
21493 fillDow: function()
21495 var dowCnt = this.weekStart;
21504 if(this.calendarWeeks){
21512 while (dowCnt < this.weekStart + 7) {
21516 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21520 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21523 fillMonths: function()
21526 var months = this.picker().select('>.datepicker-months td', true).first();
21528 months.dom.innerHTML = '';
21534 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21537 months.createChild(month);
21544 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;
21546 if (this.date < this.startDate) {
21547 this.viewDate = new Date(this.startDate);
21548 } else if (this.date > this.endDate) {
21549 this.viewDate = new Date(this.endDate);
21551 this.viewDate = new Date(this.date);
21559 var d = new Date(this.viewDate),
21560 year = d.getUTCFullYear(),
21561 month = d.getUTCMonth(),
21562 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21563 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21564 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21565 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21566 currentDate = this.date && this.date.valueOf(),
21567 today = this.UTCToday();
21569 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21571 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21573 // this.picker.select('>tfoot th.today').
21574 // .text(dates[this.language].today)
21575 // .toggle(this.todayBtn !== false);
21577 this.updateNavArrows();
21580 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21582 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21584 prevMonth.setUTCDate(day);
21586 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21588 var nextMonth = new Date(prevMonth);
21590 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21592 nextMonth = nextMonth.valueOf();
21594 var fillMonths = false;
21596 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21598 while(prevMonth.valueOf() <= nextMonth) {
21601 if (prevMonth.getUTCDay() === this.weekStart) {
21603 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21611 if(this.calendarWeeks){
21612 // ISO 8601: First week contains first thursday.
21613 // ISO also states week starts on Monday, but we can be more abstract here.
21615 // Start of current week: based on weekstart/current date
21616 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21617 // Thursday of this week
21618 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21619 // First Thursday of year, year from thursday
21620 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21621 // Calendar week: ms between thursdays, div ms per day, div 7 days
21622 calWeek = (th - yth) / 864e5 / 7 + 1;
21624 fillMonths.cn.push({
21632 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21634 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21637 if (this.todayHighlight &&
21638 prevMonth.getUTCFullYear() == today.getFullYear() &&
21639 prevMonth.getUTCMonth() == today.getMonth() &&
21640 prevMonth.getUTCDate() == today.getDate()) {
21641 clsName += ' today';
21644 if (currentDate && prevMonth.valueOf() === currentDate) {
21645 clsName += ' active';
21648 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21649 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21650 clsName += ' disabled';
21653 fillMonths.cn.push({
21655 cls: 'day ' + clsName,
21656 html: prevMonth.getDate()
21659 prevMonth.setDate(prevMonth.getDate()+1);
21662 var currentYear = this.date && this.date.getUTCFullYear();
21663 var currentMonth = this.date && this.date.getUTCMonth();
21665 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21667 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21668 v.removeClass('active');
21670 if(currentYear === year && k === currentMonth){
21671 v.addClass('active');
21674 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21675 v.addClass('disabled');
21681 year = parseInt(year/10, 10) * 10;
21683 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21685 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21688 for (var i = -1; i < 11; i++) {
21689 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21691 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21699 showMode: function(dir)
21702 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21705 Roo.each(this.picker().select('>div',true).elements, function(v){
21706 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21709 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21714 if(this.isInline) {
21718 this.picker().removeClass(['bottom', 'top']);
21720 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21722 * place to the top of element!
21726 this.picker().addClass('top');
21727 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21732 this.picker().addClass('bottom');
21734 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21737 parseDate : function(value)
21739 if(!value || value instanceof Date){
21742 var v = Date.parseDate(value, this.format);
21743 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21744 v = Date.parseDate(value, 'Y-m-d');
21746 if(!v && this.altFormats){
21747 if(!this.altFormatsArray){
21748 this.altFormatsArray = this.altFormats.split("|");
21750 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21751 v = Date.parseDate(value, this.altFormatsArray[i]);
21757 formatDate : function(date, fmt)
21759 return (!date || !(date instanceof Date)) ?
21760 date : date.dateFormat(fmt || this.format);
21763 onFocus : function()
21765 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21769 onBlur : function()
21771 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21773 var d = this.inputEl().getValue();
21780 showPopup : function()
21782 this.picker().show();
21786 this.fireEvent('showpopup', this, this.date);
21789 hidePopup : function()
21791 if(this.isInline) {
21794 this.picker().hide();
21795 this.viewMode = this.startViewMode;
21798 this.fireEvent('hidepopup', this, this.date);
21802 onMousedown: function(e)
21804 e.stopPropagation();
21805 e.preventDefault();
21810 Roo.bootstrap.DateField.superclass.keyup.call(this);
21814 setValue: function(v)
21816 if(this.fireEvent('beforeselect', this, v) !== false){
21817 var d = new Date(this.parseDate(v) ).clearTime();
21819 if(isNaN(d.getTime())){
21820 this.date = this.viewDate = '';
21821 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21825 v = this.formatDate(d);
21827 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21829 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21833 this.fireEvent('select', this, this.date);
21837 getValue: function()
21839 return this.formatDate(this.date);
21842 fireKey: function(e)
21844 if (!this.picker().isVisible()){
21845 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21851 var dateChanged = false,
21853 newDate, newViewDate;
21858 e.preventDefault();
21862 if (!this.keyboardNavigation) {
21865 dir = e.keyCode == 37 ? -1 : 1;
21868 newDate = this.moveYear(this.date, dir);
21869 newViewDate = this.moveYear(this.viewDate, dir);
21870 } else if (e.shiftKey){
21871 newDate = this.moveMonth(this.date, dir);
21872 newViewDate = this.moveMonth(this.viewDate, dir);
21874 newDate = new Date(this.date);
21875 newDate.setUTCDate(this.date.getUTCDate() + dir);
21876 newViewDate = new Date(this.viewDate);
21877 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21879 if (this.dateWithinRange(newDate)){
21880 this.date = newDate;
21881 this.viewDate = newViewDate;
21882 this.setValue(this.formatDate(this.date));
21884 e.preventDefault();
21885 dateChanged = true;
21890 if (!this.keyboardNavigation) {
21893 dir = e.keyCode == 38 ? -1 : 1;
21895 newDate = this.moveYear(this.date, dir);
21896 newViewDate = this.moveYear(this.viewDate, dir);
21897 } else if (e.shiftKey){
21898 newDate = this.moveMonth(this.date, dir);
21899 newViewDate = this.moveMonth(this.viewDate, dir);
21901 newDate = new Date(this.date);
21902 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21903 newViewDate = new Date(this.viewDate);
21904 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21906 if (this.dateWithinRange(newDate)){
21907 this.date = newDate;
21908 this.viewDate = newViewDate;
21909 this.setValue(this.formatDate(this.date));
21911 e.preventDefault();
21912 dateChanged = true;
21916 this.setValue(this.formatDate(this.date));
21918 e.preventDefault();
21921 this.setValue(this.formatDate(this.date));
21935 onClick: function(e)
21937 e.stopPropagation();
21938 e.preventDefault();
21940 var target = e.getTarget();
21942 if(target.nodeName.toLowerCase() === 'i'){
21943 target = Roo.get(target).dom.parentNode;
21946 var nodeName = target.nodeName;
21947 var className = target.className;
21948 var html = target.innerHTML;
21949 //Roo.log(nodeName);
21951 switch(nodeName.toLowerCase()) {
21953 switch(className) {
21959 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21960 switch(this.viewMode){
21962 this.viewDate = this.moveMonth(this.viewDate, dir);
21966 this.viewDate = this.moveYear(this.viewDate, dir);
21972 var date = new Date();
21973 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21975 this.setValue(this.formatDate(this.date));
21982 if (className.indexOf('disabled') < 0) {
21983 if (!this.viewDate) {
21984 this.viewDate = new Date();
21986 this.viewDate.setUTCDate(1);
21987 if (className.indexOf('month') > -1) {
21988 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21990 var year = parseInt(html, 10) || 0;
21991 this.viewDate.setUTCFullYear(year);
21995 if(this.singleMode){
21996 this.setValue(this.formatDate(this.viewDate));
22007 //Roo.log(className);
22008 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22009 var day = parseInt(html, 10) || 1;
22010 var year = (this.viewDate || new Date()).getUTCFullYear(),
22011 month = (this.viewDate || new Date()).getUTCMonth();
22013 if (className.indexOf('old') > -1) {
22020 } else if (className.indexOf('new') > -1) {
22028 //Roo.log([year,month,day]);
22029 this.date = this.UTCDate(year, month, day,0,0,0,0);
22030 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22032 //Roo.log(this.formatDate(this.date));
22033 this.setValue(this.formatDate(this.date));
22040 setStartDate: function(startDate)
22042 this.startDate = startDate || -Infinity;
22043 if (this.startDate !== -Infinity) {
22044 this.startDate = this.parseDate(this.startDate);
22047 this.updateNavArrows();
22050 setEndDate: function(endDate)
22052 this.endDate = endDate || Infinity;
22053 if (this.endDate !== Infinity) {
22054 this.endDate = this.parseDate(this.endDate);
22057 this.updateNavArrows();
22060 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22062 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22063 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22064 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22066 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22067 return parseInt(d, 10);
22070 this.updateNavArrows();
22073 updateNavArrows: function()
22075 if(this.singleMode){
22079 var d = new Date(this.viewDate),
22080 year = d.getUTCFullYear(),
22081 month = d.getUTCMonth();
22083 Roo.each(this.picker().select('.prev', true).elements, function(v){
22085 switch (this.viewMode) {
22088 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22094 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22101 Roo.each(this.picker().select('.next', true).elements, function(v){
22103 switch (this.viewMode) {
22106 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22112 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22120 moveMonth: function(date, dir)
22125 var new_date = new Date(date.valueOf()),
22126 day = new_date.getUTCDate(),
22127 month = new_date.getUTCMonth(),
22128 mag = Math.abs(dir),
22130 dir = dir > 0 ? 1 : -1;
22133 // If going back one month, make sure month is not current month
22134 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22136 return new_date.getUTCMonth() == month;
22138 // If going forward one month, make sure month is as expected
22139 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22141 return new_date.getUTCMonth() != new_month;
22143 new_month = month + dir;
22144 new_date.setUTCMonth(new_month);
22145 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22146 if (new_month < 0 || new_month > 11) {
22147 new_month = (new_month + 12) % 12;
22150 // For magnitudes >1, move one month at a time...
22151 for (var i=0; i<mag; i++) {
22152 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22153 new_date = this.moveMonth(new_date, dir);
22155 // ...then reset the day, keeping it in the new month
22156 new_month = new_date.getUTCMonth();
22157 new_date.setUTCDate(day);
22159 return new_month != new_date.getUTCMonth();
22162 // Common date-resetting loop -- if date is beyond end of month, make it
22165 new_date.setUTCDate(--day);
22166 new_date.setUTCMonth(new_month);
22171 moveYear: function(date, dir)
22173 return this.moveMonth(date, dir*12);
22176 dateWithinRange: function(date)
22178 return date >= this.startDate && date <= this.endDate;
22184 this.picker().remove();
22187 validateValue : function(value)
22189 if(this.getVisibilityEl().hasClass('hidden')){
22193 if(value.length < 1) {
22194 if(this.allowBlank){
22200 if(value.length < this.minLength){
22203 if(value.length > this.maxLength){
22207 var vt = Roo.form.VTypes;
22208 if(!vt[this.vtype](value, this)){
22212 if(typeof this.validator == "function"){
22213 var msg = this.validator(value);
22219 if(this.regex && !this.regex.test(value)){
22223 if(typeof(this.parseDate(value)) == 'undefined'){
22227 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22231 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22241 this.date = this.viewDate = '';
22243 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22248 Roo.apply(Roo.bootstrap.DateField, {
22259 html: '<i class="fa fa-arrow-left"/>'
22269 html: '<i class="fa fa-arrow-right"/>'
22311 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22312 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22313 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22314 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22315 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22328 navFnc: 'FullYear',
22333 navFnc: 'FullYear',
22338 Roo.apply(Roo.bootstrap.DateField, {
22342 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22346 cls: 'datepicker-days',
22350 cls: 'table-condensed',
22352 Roo.bootstrap.DateField.head,
22356 Roo.bootstrap.DateField.footer
22363 cls: 'datepicker-months',
22367 cls: 'table-condensed',
22369 Roo.bootstrap.DateField.head,
22370 Roo.bootstrap.DateField.content,
22371 Roo.bootstrap.DateField.footer
22378 cls: 'datepicker-years',
22382 cls: 'table-condensed',
22384 Roo.bootstrap.DateField.head,
22385 Roo.bootstrap.DateField.content,
22386 Roo.bootstrap.DateField.footer
22405 * @class Roo.bootstrap.TimeField
22406 * @extends Roo.bootstrap.Input
22407 * Bootstrap DateField class
22411 * Create a new TimeField
22412 * @param {Object} config The config object
22415 Roo.bootstrap.TimeField = function(config){
22416 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22420 * Fires when this field show.
22421 * @param {Roo.bootstrap.DateField} thisthis
22422 * @param {Mixed} date The date value
22427 * Fires when this field hide.
22428 * @param {Roo.bootstrap.DateField} this
22429 * @param {Mixed} date The date value
22434 * Fires when select a date.
22435 * @param {Roo.bootstrap.DateField} this
22436 * @param {Mixed} date The date value
22442 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22445 * @cfg {String} format
22446 * The default time format string which can be overriden for localization support. The format must be
22447 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22451 getAutoCreate : function()
22453 this.after = '<i class="fa far fa-clock"></i>';
22454 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22458 onRender: function(ct, position)
22461 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22463 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22465 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22467 this.pop = this.picker().select('>.datepicker-time',true).first();
22468 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22470 this.picker().on('mousedown', this.onMousedown, this);
22471 this.picker().on('click', this.onClick, this);
22473 this.picker().addClass('datepicker-dropdown');
22478 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22479 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22480 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22481 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22482 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22483 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22487 fireKey: function(e){
22488 if (!this.picker().isVisible()){
22489 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22495 e.preventDefault();
22503 this.onTogglePeriod();
22506 this.onIncrementMinutes();
22509 this.onDecrementMinutes();
22518 onClick: function(e) {
22519 e.stopPropagation();
22520 e.preventDefault();
22523 picker : function()
22525 return this.pickerEl;
22528 fillTime: function()
22530 var time = this.pop.select('tbody', true).first();
22532 time.dom.innerHTML = '';
22547 cls: 'hours-up fa fas fa-chevron-up'
22567 cls: 'minutes-up fa fas fa-chevron-up'
22588 cls: 'timepicker-hour',
22603 cls: 'timepicker-minute',
22618 cls: 'btn btn-primary period',
22640 cls: 'hours-down fa fas fa-chevron-down'
22660 cls: 'minutes-down fa fas fa-chevron-down'
22678 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22685 var hours = this.time.getHours();
22686 var minutes = this.time.getMinutes();
22699 hours = hours - 12;
22703 hours = '0' + hours;
22707 minutes = '0' + minutes;
22710 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22711 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22712 this.pop.select('button', true).first().dom.innerHTML = period;
22718 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22720 var cls = ['bottom'];
22722 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22729 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22733 //this.picker().setXY(20000,20000);
22734 this.picker().addClass(cls.join('-'));
22738 Roo.each(cls, function(c){
22743 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22744 //_this.picker().setTop(_this.inputEl().getHeight());
22748 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22750 //_this.picker().setTop(0 - _this.picker().getHeight());
22755 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22759 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22767 onFocus : function()
22769 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22773 onBlur : function()
22775 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22781 this.picker().show();
22786 this.fireEvent('show', this, this.date);
22791 this.picker().hide();
22794 this.fireEvent('hide', this, this.date);
22797 setTime : function()
22800 this.setValue(this.time.format(this.format));
22802 this.fireEvent('select', this, this.date);
22807 onMousedown: function(e){
22808 e.stopPropagation();
22809 e.preventDefault();
22812 onIncrementHours: function()
22814 Roo.log('onIncrementHours');
22815 this.time = this.time.add(Date.HOUR, 1);
22820 onDecrementHours: function()
22822 Roo.log('onDecrementHours');
22823 this.time = this.time.add(Date.HOUR, -1);
22827 onIncrementMinutes: function()
22829 Roo.log('onIncrementMinutes');
22830 this.time = this.time.add(Date.MINUTE, 1);
22834 onDecrementMinutes: function()
22836 Roo.log('onDecrementMinutes');
22837 this.time = this.time.add(Date.MINUTE, -1);
22841 onTogglePeriod: function()
22843 Roo.log('onTogglePeriod');
22844 this.time = this.time.add(Date.HOUR, 12);
22852 Roo.apply(Roo.bootstrap.TimeField, {
22856 cls: 'datepicker dropdown-menu',
22860 cls: 'datepicker-time',
22864 cls: 'table-condensed',
22893 cls: 'btn btn-info ok',
22921 * @class Roo.bootstrap.MonthField
22922 * @extends Roo.bootstrap.Input
22923 * Bootstrap MonthField class
22925 * @cfg {String} language default en
22928 * Create a new MonthField
22929 * @param {Object} config The config object
22932 Roo.bootstrap.MonthField = function(config){
22933 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22938 * Fires when this field show.
22939 * @param {Roo.bootstrap.MonthField} this
22940 * @param {Mixed} date The date value
22945 * Fires when this field hide.
22946 * @param {Roo.bootstrap.MonthField} this
22947 * @param {Mixed} date The date value
22952 * Fires when select a date.
22953 * @param {Roo.bootstrap.MonthField} this
22954 * @param {String} oldvalue The old value
22955 * @param {String} newvalue The new value
22961 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22963 onRender: function(ct, position)
22966 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22968 this.language = this.language || 'en';
22969 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22970 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22972 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22973 this.isInline = false;
22974 this.isInput = true;
22975 this.component = this.el.select('.add-on', true).first() || false;
22976 this.component = (this.component && this.component.length === 0) ? false : this.component;
22977 this.hasInput = this.component && this.inputEL().length;
22979 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22981 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22983 this.picker().on('mousedown', this.onMousedown, this);
22984 this.picker().on('click', this.onClick, this);
22986 this.picker().addClass('datepicker-dropdown');
22988 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22989 v.setStyle('width', '189px');
22996 if(this.isInline) {
23002 setValue: function(v, suppressEvent)
23004 var o = this.getValue();
23006 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23010 if(suppressEvent !== true){
23011 this.fireEvent('select', this, o, v);
23016 getValue: function()
23021 onClick: function(e)
23023 e.stopPropagation();
23024 e.preventDefault();
23026 var target = e.getTarget();
23028 if(target.nodeName.toLowerCase() === 'i'){
23029 target = Roo.get(target).dom.parentNode;
23032 var nodeName = target.nodeName;
23033 var className = target.className;
23034 var html = target.innerHTML;
23036 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23040 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23042 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23048 picker : function()
23050 return this.pickerEl;
23053 fillMonths: function()
23056 var months = this.picker().select('>.datepicker-months td', true).first();
23058 months.dom.innerHTML = '';
23064 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23067 months.createChild(month);
23076 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23077 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23080 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23081 e.removeClass('active');
23083 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23084 e.addClass('active');
23091 if(this.isInline) {
23095 this.picker().removeClass(['bottom', 'top']);
23097 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23099 * place to the top of element!
23103 this.picker().addClass('top');
23104 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23109 this.picker().addClass('bottom');
23111 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23114 onFocus : function()
23116 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23120 onBlur : function()
23122 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23124 var d = this.inputEl().getValue();
23133 this.picker().show();
23134 this.picker().select('>.datepicker-months', true).first().show();
23138 this.fireEvent('show', this, this.date);
23143 if(this.isInline) {
23146 this.picker().hide();
23147 this.fireEvent('hide', this, this.date);
23151 onMousedown: function(e)
23153 e.stopPropagation();
23154 e.preventDefault();
23159 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23163 fireKey: function(e)
23165 if (!this.picker().isVisible()){
23166 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23177 e.preventDefault();
23181 dir = e.keyCode == 37 ? -1 : 1;
23183 this.vIndex = this.vIndex + dir;
23185 if(this.vIndex < 0){
23189 if(this.vIndex > 11){
23193 if(isNaN(this.vIndex)){
23197 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23203 dir = e.keyCode == 38 ? -1 : 1;
23205 this.vIndex = this.vIndex + dir * 4;
23207 if(this.vIndex < 0){
23211 if(this.vIndex > 11){
23215 if(isNaN(this.vIndex)){
23219 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23224 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23225 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23229 e.preventDefault();
23232 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23233 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23249 this.picker().remove();
23254 Roo.apply(Roo.bootstrap.MonthField, {
23273 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23274 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23279 Roo.apply(Roo.bootstrap.MonthField, {
23283 cls: 'datepicker dropdown-menu roo-dynamic',
23287 cls: 'datepicker-months',
23291 cls: 'table-condensed',
23293 Roo.bootstrap.DateField.content
23313 * @class Roo.bootstrap.CheckBox
23314 * @extends Roo.bootstrap.Input
23315 * Bootstrap CheckBox class
23317 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23318 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23319 * @cfg {String} boxLabel The text that appears beside the checkbox
23320 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23321 * @cfg {Boolean} checked initnal the element
23322 * @cfg {Boolean} inline inline the element (default false)
23323 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23324 * @cfg {String} tooltip label tooltip
23327 * Create a new CheckBox
23328 * @param {Object} config The config object
23331 Roo.bootstrap.CheckBox = function(config){
23332 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23337 * Fires when the element is checked or unchecked.
23338 * @param {Roo.bootstrap.CheckBox} this This input
23339 * @param {Boolean} checked The new checked value
23344 * Fires when the element is click.
23345 * @param {Roo.bootstrap.CheckBox} this This input
23352 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23354 inputType: 'checkbox',
23363 // checkbox success does not make any sense really..
23368 getAutoCreate : function()
23370 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23376 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23379 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23385 type : this.inputType,
23386 value : this.inputValue,
23387 cls : 'roo-' + this.inputType, //'form-box',
23388 placeholder : this.placeholder || ''
23392 if(this.inputType != 'radio'){
23396 cls : 'roo-hidden-value',
23397 value : this.checked ? this.inputValue : this.valueOff
23402 if (this.weight) { // Validity check?
23403 cfg.cls += " " + this.inputType + "-" + this.weight;
23406 if (this.disabled) {
23407 input.disabled=true;
23411 input.checked = this.checked;
23416 input.name = this.name;
23418 if(this.inputType != 'radio'){
23419 hidden.name = this.name;
23420 input.name = '_hidden_' + this.name;
23425 input.cls += ' input-' + this.size;
23430 ['xs','sm','md','lg'].map(function(size){
23431 if (settings[size]) {
23432 cfg.cls += ' col-' + size + '-' + settings[size];
23436 var inputblock = input;
23438 if (this.before || this.after) {
23441 cls : 'input-group',
23446 inputblock.cn.push({
23448 cls : 'input-group-addon',
23453 inputblock.cn.push(input);
23455 if(this.inputType != 'radio'){
23456 inputblock.cn.push(hidden);
23460 inputblock.cn.push({
23462 cls : 'input-group-addon',
23468 var boxLabelCfg = false;
23474 //'for': id, // box label is handled by onclick - so no for...
23476 html: this.boxLabel
23479 boxLabelCfg.tooltip = this.tooltip;
23485 if (align ==='left' && this.fieldLabel.length) {
23486 // Roo.log("left and has label");
23491 cls : 'control-label',
23492 html : this.fieldLabel
23503 cfg.cn[1].cn.push(boxLabelCfg);
23506 if(this.labelWidth > 12){
23507 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23510 if(this.labelWidth < 13 && this.labelmd == 0){
23511 this.labelmd = this.labelWidth;
23514 if(this.labellg > 0){
23515 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23516 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23519 if(this.labelmd > 0){
23520 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23521 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23524 if(this.labelsm > 0){
23525 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23526 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23529 if(this.labelxs > 0){
23530 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23531 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23534 } else if ( this.fieldLabel.length) {
23535 // Roo.log(" label");
23539 tag: this.boxLabel ? 'span' : 'label',
23541 cls: 'control-label box-input-label',
23542 //cls : 'input-group-addon',
23543 html : this.fieldLabel
23550 cfg.cn.push(boxLabelCfg);
23555 // Roo.log(" no label && no align");
23556 cfg.cn = [ inputblock ] ;
23558 cfg.cn.push(boxLabelCfg);
23566 if(this.inputType != 'radio'){
23567 cfg.cn.push(hidden);
23575 * return the real input element.
23577 inputEl: function ()
23579 return this.el.select('input.roo-' + this.inputType,true).first();
23581 hiddenEl: function ()
23583 return this.el.select('input.roo-hidden-value',true).first();
23586 labelEl: function()
23588 return this.el.select('label.control-label',true).first();
23590 /* depricated... */
23594 return this.labelEl();
23597 boxLabelEl: function()
23599 return this.el.select('label.box-label',true).first();
23602 initEvents : function()
23604 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23606 this.inputEl().on('click', this.onClick, this);
23608 if (this.boxLabel) {
23609 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23612 this.startValue = this.getValue();
23615 Roo.bootstrap.CheckBox.register(this);
23619 onClick : function(e)
23621 if(this.fireEvent('click', this, e) !== false){
23622 this.setChecked(!this.checked);
23627 setChecked : function(state,suppressEvent)
23629 this.startValue = this.getValue();
23631 if(this.inputType == 'radio'){
23633 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23634 e.dom.checked = false;
23637 this.inputEl().dom.checked = true;
23639 this.inputEl().dom.value = this.inputValue;
23641 if(suppressEvent !== true){
23642 this.fireEvent('check', this, true);
23650 this.checked = state;
23652 this.inputEl().dom.checked = state;
23655 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23657 if(suppressEvent !== true){
23658 this.fireEvent('check', this, state);
23664 getValue : function()
23666 if(this.inputType == 'radio'){
23667 return this.getGroupValue();
23670 return this.hiddenEl().dom.value;
23674 getGroupValue : function()
23676 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23680 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23683 setValue : function(v,suppressEvent)
23685 if(this.inputType == 'radio'){
23686 this.setGroupValue(v, suppressEvent);
23690 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23695 setGroupValue : function(v, suppressEvent)
23697 this.startValue = this.getValue();
23699 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23700 e.dom.checked = false;
23702 if(e.dom.value == v){
23703 e.dom.checked = true;
23707 if(suppressEvent !== true){
23708 this.fireEvent('check', this, true);
23716 validate : function()
23718 if(this.getVisibilityEl().hasClass('hidden')){
23724 (this.inputType == 'radio' && this.validateRadio()) ||
23725 (this.inputType == 'checkbox' && this.validateCheckbox())
23731 this.markInvalid();
23735 validateRadio : function()
23737 if(this.getVisibilityEl().hasClass('hidden')){
23741 if(this.allowBlank){
23747 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23748 if(!e.dom.checked){
23760 validateCheckbox : function()
23763 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23764 //return (this.getValue() == this.inputValue) ? true : false;
23767 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23775 for(var i in group){
23776 if(group[i].el.isVisible(true)){
23784 for(var i in group){
23789 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23796 * Mark this field as valid
23798 markValid : function()
23802 this.fireEvent('valid', this);
23804 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23807 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23814 if(this.inputType == 'radio'){
23815 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23816 var fg = e.findParent('.form-group', false, true);
23817 if (Roo.bootstrap.version == 3) {
23818 fg.removeClass([_this.invalidClass, _this.validClass]);
23819 fg.addClass(_this.validClass);
23821 fg.removeClass(['is-valid', 'is-invalid']);
23822 fg.addClass('is-valid');
23830 var fg = this.el.findParent('.form-group', false, true);
23831 if (Roo.bootstrap.version == 3) {
23832 fg.removeClass([this.invalidClass, this.validClass]);
23833 fg.addClass(this.validClass);
23835 fg.removeClass(['is-valid', 'is-invalid']);
23836 fg.addClass('is-valid');
23841 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23847 for(var i in group){
23848 var fg = group[i].el.findParent('.form-group', false, true);
23849 if (Roo.bootstrap.version == 3) {
23850 fg.removeClass([this.invalidClass, this.validClass]);
23851 fg.addClass(this.validClass);
23853 fg.removeClass(['is-valid', 'is-invalid']);
23854 fg.addClass('is-valid');
23860 * Mark this field as invalid
23861 * @param {String} msg The validation message
23863 markInvalid : function(msg)
23865 if(this.allowBlank){
23871 this.fireEvent('invalid', this, msg);
23873 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23876 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23880 label.markInvalid();
23883 if(this.inputType == 'radio'){
23885 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23886 var fg = e.findParent('.form-group', false, true);
23887 if (Roo.bootstrap.version == 3) {
23888 fg.removeClass([_this.invalidClass, _this.validClass]);
23889 fg.addClass(_this.invalidClass);
23891 fg.removeClass(['is-invalid', 'is-valid']);
23892 fg.addClass('is-invalid');
23900 var fg = this.el.findParent('.form-group', false, true);
23901 if (Roo.bootstrap.version == 3) {
23902 fg.removeClass([_this.invalidClass, _this.validClass]);
23903 fg.addClass(_this.invalidClass);
23905 fg.removeClass(['is-invalid', 'is-valid']);
23906 fg.addClass('is-invalid');
23911 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23917 for(var i in group){
23918 var fg = group[i].el.findParent('.form-group', false, true);
23919 if (Roo.bootstrap.version == 3) {
23920 fg.removeClass([_this.invalidClass, _this.validClass]);
23921 fg.addClass(_this.invalidClass);
23923 fg.removeClass(['is-invalid', 'is-valid']);
23924 fg.addClass('is-invalid');
23930 clearInvalid : function()
23932 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23934 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23936 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23938 if (label && label.iconEl) {
23939 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23940 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23944 disable : function()
23946 if(this.inputType != 'radio'){
23947 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23954 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23955 _this.getActionEl().addClass(this.disabledClass);
23956 e.dom.disabled = true;
23960 this.disabled = true;
23961 this.fireEvent("disable", this);
23965 enable : function()
23967 if(this.inputType != 'radio'){
23968 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23975 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23976 _this.getActionEl().removeClass(this.disabledClass);
23977 e.dom.disabled = false;
23981 this.disabled = false;
23982 this.fireEvent("enable", this);
23986 setBoxLabel : function(v)
23991 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23997 Roo.apply(Roo.bootstrap.CheckBox, {
24002 * register a CheckBox Group
24003 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24005 register : function(checkbox)
24007 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24008 this.groups[checkbox.groupId] = {};
24011 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24015 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24019 * fetch a CheckBox Group based on the group ID
24020 * @param {string} the group ID
24021 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24023 get: function(groupId) {
24024 if (typeof(this.groups[groupId]) == 'undefined') {
24028 return this.groups[groupId] ;
24041 * @class Roo.bootstrap.Radio
24042 * @extends Roo.bootstrap.Component
24043 * Bootstrap Radio class
24044 * @cfg {String} boxLabel - the label associated
24045 * @cfg {String} value - the value of radio
24048 * Create a new Radio
24049 * @param {Object} config The config object
24051 Roo.bootstrap.Radio = function(config){
24052 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24056 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24062 getAutoCreate : function()
24066 cls : 'form-group radio',
24071 html : this.boxLabel
24079 initEvents : function()
24081 this.parent().register(this);
24083 this.el.on('click', this.onClick, this);
24087 onClick : function(e)
24089 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24090 this.setChecked(true);
24094 setChecked : function(state, suppressEvent)
24096 this.parent().setValue(this.value, suppressEvent);
24100 setBoxLabel : function(v)
24105 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24120 * @class Roo.bootstrap.SecurePass
24121 * @extends Roo.bootstrap.Input
24122 * Bootstrap SecurePass class
24126 * Create a new SecurePass
24127 * @param {Object} config The config object
24130 Roo.bootstrap.SecurePass = function (config) {
24131 // these go here, so the translation tool can replace them..
24133 PwdEmpty: "Please type a password, and then retype it to confirm.",
24134 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24135 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24136 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24137 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24138 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24139 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24140 TooWeak: "Your password is Too Weak."
24142 this.meterLabel = "Password strength:";
24143 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24144 this.meterClass = [
24145 "roo-password-meter-tooweak",
24146 "roo-password-meter-weak",
24147 "roo-password-meter-medium",
24148 "roo-password-meter-strong",
24149 "roo-password-meter-grey"
24154 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24157 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24159 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24161 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24162 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24163 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24164 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24165 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24166 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24167 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24177 * @cfg {String/Object} Label for the strength meter (defaults to
24178 * 'Password strength:')
24183 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24184 * ['Weak', 'Medium', 'Strong'])
24187 pwdStrengths: false,
24200 initEvents: function ()
24202 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24204 if (this.el.is('input[type=password]') && Roo.isSafari) {
24205 this.el.on('keydown', this.SafariOnKeyDown, this);
24208 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24211 onRender: function (ct, position)
24213 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24214 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24215 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24217 this.trigger.createChild({
24222 cls: 'roo-password-meter-grey col-xs-12',
24225 //width: this.meterWidth + 'px'
24229 cls: 'roo-password-meter-text'
24235 if (this.hideTrigger) {
24236 this.trigger.setDisplayed(false);
24238 this.setSize(this.width || '', this.height || '');
24241 onDestroy: function ()
24243 if (this.trigger) {
24244 this.trigger.removeAllListeners();
24245 this.trigger.remove();
24248 this.wrap.remove();
24250 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24253 checkStrength: function ()
24255 var pwd = this.inputEl().getValue();
24256 if (pwd == this._lastPwd) {
24261 if (this.ClientSideStrongPassword(pwd)) {
24263 } else if (this.ClientSideMediumPassword(pwd)) {
24265 } else if (this.ClientSideWeakPassword(pwd)) {
24271 Roo.log('strength1: ' + strength);
24273 //var pm = this.trigger.child('div/div/div').dom;
24274 var pm = this.trigger.child('div/div');
24275 pm.removeClass(this.meterClass);
24276 pm.addClass(this.meterClass[strength]);
24279 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24281 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24283 this._lastPwd = pwd;
24287 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24289 this._lastPwd = '';
24291 var pm = this.trigger.child('div/div');
24292 pm.removeClass(this.meterClass);
24293 pm.addClass('roo-password-meter-grey');
24296 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24299 this.inputEl().dom.type='password';
24302 validateValue: function (value)
24304 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24307 if (value.length == 0) {
24308 if (this.allowBlank) {
24309 this.clearInvalid();
24313 this.markInvalid(this.errors.PwdEmpty);
24314 this.errorMsg = this.errors.PwdEmpty;
24322 if (!value.match(/[\x21-\x7e]+/)) {
24323 this.markInvalid(this.errors.PwdBadChar);
24324 this.errorMsg = this.errors.PwdBadChar;
24327 if (value.length < 6) {
24328 this.markInvalid(this.errors.PwdShort);
24329 this.errorMsg = this.errors.PwdShort;
24332 if (value.length > 16) {
24333 this.markInvalid(this.errors.PwdLong);
24334 this.errorMsg = this.errors.PwdLong;
24338 if (this.ClientSideStrongPassword(value)) {
24340 } else if (this.ClientSideMediumPassword(value)) {
24342 } else if (this.ClientSideWeakPassword(value)) {
24349 if (strength < 2) {
24350 //this.markInvalid(this.errors.TooWeak);
24351 this.errorMsg = this.errors.TooWeak;
24356 console.log('strength2: ' + strength);
24358 //var pm = this.trigger.child('div/div/div').dom;
24360 var pm = this.trigger.child('div/div');
24361 pm.removeClass(this.meterClass);
24362 pm.addClass(this.meterClass[strength]);
24364 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24366 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24368 this.errorMsg = '';
24372 CharacterSetChecks: function (type)
24375 this.fResult = false;
24378 isctype: function (character, type)
24381 case this.kCapitalLetter:
24382 if (character >= 'A' && character <= 'Z') {
24387 case this.kSmallLetter:
24388 if (character >= 'a' && character <= 'z') {
24394 if (character >= '0' && character <= '9') {
24399 case this.kPunctuation:
24400 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24411 IsLongEnough: function (pwd, size)
24413 return !(pwd == null || isNaN(size) || pwd.length < size);
24416 SpansEnoughCharacterSets: function (word, nb)
24418 if (!this.IsLongEnough(word, nb))
24423 var characterSetChecks = new Array(
24424 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24425 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24428 for (var index = 0; index < word.length; ++index) {
24429 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24430 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24431 characterSetChecks[nCharSet].fResult = true;
24438 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24439 if (characterSetChecks[nCharSet].fResult) {
24444 if (nCharSets < nb) {
24450 ClientSideStrongPassword: function (pwd)
24452 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24455 ClientSideMediumPassword: function (pwd)
24457 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24460 ClientSideWeakPassword: function (pwd)
24462 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24465 })//<script type="text/javascript">
24468 * Based Ext JS Library 1.1.1
24469 * Copyright(c) 2006-2007, Ext JS, LLC.
24475 * @class Roo.HtmlEditorCore
24476 * @extends Roo.Component
24477 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24479 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24482 Roo.HtmlEditorCore = function(config){
24485 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24490 * @event initialize
24491 * Fires when the editor is fully initialized (including the iframe)
24492 * @param {Roo.HtmlEditorCore} this
24497 * Fires when the editor is first receives the focus. Any insertion must wait
24498 * until after this event.
24499 * @param {Roo.HtmlEditorCore} this
24503 * @event beforesync
24504 * Fires before the textarea is updated with content from the editor iframe. Return false
24505 * to cancel the sync.
24506 * @param {Roo.HtmlEditorCore} this
24507 * @param {String} html
24511 * @event beforepush
24512 * Fires before the iframe editor is updated with content from the textarea. Return false
24513 * to cancel the push.
24514 * @param {Roo.HtmlEditorCore} this
24515 * @param {String} html
24520 * Fires when the textarea is updated with content from the editor iframe.
24521 * @param {Roo.HtmlEditorCore} this
24522 * @param {String} html
24527 * Fires when the iframe editor is updated with content from the textarea.
24528 * @param {Roo.HtmlEditorCore} this
24529 * @param {String} html
24534 * @event editorevent
24535 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24536 * @param {Roo.HtmlEditorCore} this
24542 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24544 // defaults : white / black...
24545 this.applyBlacklists();
24552 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24556 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24562 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24567 * @cfg {Number} height (in pixels)
24571 * @cfg {Number} width (in pixels)
24576 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24579 stylesheets: false,
24584 // private properties
24585 validationEvent : false,
24587 initialized : false,
24589 sourceEditMode : false,
24590 onFocus : Roo.emptyFn,
24592 hideMode:'offsets',
24596 // blacklist + whitelisted elements..
24603 * Protected method that will not generally be called directly. It
24604 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24605 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24607 getDocMarkup : function(){
24611 // inherit styels from page...??
24612 if (this.stylesheets === false) {
24614 Roo.get(document.head).select('style').each(function(node) {
24615 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24618 Roo.get(document.head).select('link').each(function(node) {
24619 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24622 } else if (!this.stylesheets.length) {
24624 st = '<style type="text/css">' +
24625 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24628 for (var i in this.stylesheets) {
24629 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24634 st += '<style type="text/css">' +
24635 'IMG { cursor: pointer } ' +
24638 var cls = 'roo-htmleditor-body';
24640 if(this.bodyCls.length){
24641 cls += ' ' + this.bodyCls;
24644 return '<html><head>' + st +
24645 //<style type="text/css">' +
24646 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24648 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24652 onRender : function(ct, position)
24655 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24656 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24659 this.el.dom.style.border = '0 none';
24660 this.el.dom.setAttribute('tabIndex', -1);
24661 this.el.addClass('x-hidden hide');
24665 if(Roo.isIE){ // fix IE 1px bogus margin
24666 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24670 this.frameId = Roo.id();
24674 var iframe = this.owner.wrap.createChild({
24676 cls: 'form-control', // bootstrap..
24678 name: this.frameId,
24679 frameBorder : 'no',
24680 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24685 this.iframe = iframe.dom;
24687 this.assignDocWin();
24689 this.doc.designMode = 'on';
24692 this.doc.write(this.getDocMarkup());
24696 var task = { // must defer to wait for browser to be ready
24698 //console.log("run task?" + this.doc.readyState);
24699 this.assignDocWin();
24700 if(this.doc.body || this.doc.readyState == 'complete'){
24702 this.doc.designMode="on";
24706 Roo.TaskMgr.stop(task);
24707 this.initEditor.defer(10, this);
24714 Roo.TaskMgr.start(task);
24719 onResize : function(w, h)
24721 Roo.log('resize: ' +w + ',' + h );
24722 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24726 if(typeof w == 'number'){
24728 this.iframe.style.width = w + 'px';
24730 if(typeof h == 'number'){
24732 this.iframe.style.height = h + 'px';
24734 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24741 * Toggles the editor between standard and source edit mode.
24742 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24744 toggleSourceEdit : function(sourceEditMode){
24746 this.sourceEditMode = sourceEditMode === true;
24748 if(this.sourceEditMode){
24750 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24753 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24754 //this.iframe.className = '';
24757 //this.setSize(this.owner.wrap.getSize());
24758 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24765 * Protected method that will not generally be called directly. If you need/want
24766 * custom HTML cleanup, this is the method you should override.
24767 * @param {String} html The HTML to be cleaned
24768 * return {String} The cleaned HTML
24770 cleanHtml : function(html){
24771 html = String(html);
24772 if(html.length > 5){
24773 if(Roo.isSafari){ // strip safari nonsense
24774 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24777 if(html == ' '){
24784 * HTML Editor -> Textarea
24785 * Protected method that will not generally be called directly. Syncs the contents
24786 * of the editor iframe with the textarea.
24788 syncValue : function(){
24789 if(this.initialized){
24790 var bd = (this.doc.body || this.doc.documentElement);
24791 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24792 var html = bd.innerHTML;
24794 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24795 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24797 html = '<div style="'+m[0]+'">' + html + '</div>';
24800 html = this.cleanHtml(html);
24801 // fix up the special chars.. normaly like back quotes in word...
24802 // however we do not want to do this with chinese..
24803 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24805 var cc = match.charCodeAt();
24807 // Get the character value, handling surrogate pairs
24808 if (match.length == 2) {
24809 // It's a surrogate pair, calculate the Unicode code point
24810 var high = match.charCodeAt(0) - 0xD800;
24811 var low = match.charCodeAt(1) - 0xDC00;
24812 cc = (high * 0x400) + low + 0x10000;
24814 (cc >= 0x4E00 && cc < 0xA000 ) ||
24815 (cc >= 0x3400 && cc < 0x4E00 ) ||
24816 (cc >= 0xf900 && cc < 0xfb00 )
24821 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24822 return "&#" + cc + ";";
24829 if(this.owner.fireEvent('beforesync', this, html) !== false){
24830 this.el.dom.value = html;
24831 this.owner.fireEvent('sync', this, html);
24837 * Protected method that will not generally be called directly. Pushes the value of the textarea
24838 * into the iframe editor.
24840 pushValue : function(){
24841 if(this.initialized){
24842 var v = this.el.dom.value.trim();
24844 // if(v.length < 1){
24848 if(this.owner.fireEvent('beforepush', this, v) !== false){
24849 var d = (this.doc.body || this.doc.documentElement);
24851 this.cleanUpPaste();
24852 this.el.dom.value = d.innerHTML;
24853 this.owner.fireEvent('push', this, v);
24859 deferFocus : function(){
24860 this.focus.defer(10, this);
24864 focus : function(){
24865 if(this.win && !this.sourceEditMode){
24872 assignDocWin: function()
24874 var iframe = this.iframe;
24877 this.doc = iframe.contentWindow.document;
24878 this.win = iframe.contentWindow;
24880 // if (!Roo.get(this.frameId)) {
24883 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24884 // this.win = Roo.get(this.frameId).dom.contentWindow;
24886 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24890 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24891 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24896 initEditor : function(){
24897 //console.log("INIT EDITOR");
24898 this.assignDocWin();
24902 this.doc.designMode="on";
24904 this.doc.write(this.getDocMarkup());
24907 var dbody = (this.doc.body || this.doc.documentElement);
24908 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24909 // this copies styles from the containing element into thsi one..
24910 // not sure why we need all of this..
24911 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24913 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24914 //ss['background-attachment'] = 'fixed'; // w3c
24915 dbody.bgProperties = 'fixed'; // ie
24916 //Roo.DomHelper.applyStyles(dbody, ss);
24917 Roo.EventManager.on(this.doc, {
24918 //'mousedown': this.onEditorEvent,
24919 'mouseup': this.onEditorEvent,
24920 'dblclick': this.onEditorEvent,
24921 'click': this.onEditorEvent,
24922 'keyup': this.onEditorEvent,
24927 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24929 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24930 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24932 this.initialized = true;
24934 this.owner.fireEvent('initialize', this);
24939 onDestroy : function(){
24945 //for (var i =0; i < this.toolbars.length;i++) {
24946 // // fixme - ask toolbars for heights?
24947 // this.toolbars[i].onDestroy();
24950 //this.wrap.dom.innerHTML = '';
24951 //this.wrap.remove();
24956 onFirstFocus : function(){
24958 this.assignDocWin();
24961 this.activated = true;
24964 if(Roo.isGecko){ // prevent silly gecko errors
24966 var s = this.win.getSelection();
24967 if(!s.focusNode || s.focusNode.nodeType != 3){
24968 var r = s.getRangeAt(0);
24969 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24974 this.execCmd('useCSS', true);
24975 this.execCmd('styleWithCSS', false);
24978 this.owner.fireEvent('activate', this);
24982 adjustFont: function(btn){
24983 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24984 //if(Roo.isSafari){ // safari
24987 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24988 if(Roo.isSafari){ // safari
24989 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24990 v = (v < 10) ? 10 : v;
24991 v = (v > 48) ? 48 : v;
24992 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24997 v = Math.max(1, v+adjust);
24999 this.execCmd('FontSize', v );
25002 onEditorEvent : function(e)
25004 this.owner.fireEvent('editorevent', this, e);
25005 // this.updateToolbar();
25006 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25009 insertTag : function(tg)
25011 // could be a bit smarter... -> wrap the current selected tRoo..
25012 if (tg.toLowerCase() == 'span' ||
25013 tg.toLowerCase() == 'code' ||
25014 tg.toLowerCase() == 'sup' ||
25015 tg.toLowerCase() == 'sub'
25018 range = this.createRange(this.getSelection());
25019 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25020 wrappingNode.appendChild(range.extractContents());
25021 range.insertNode(wrappingNode);
25028 this.execCmd("formatblock", tg);
25032 insertText : function(txt)
25036 var range = this.createRange();
25037 range.deleteContents();
25038 //alert(Sender.getAttribute('label'));
25040 range.insertNode(this.doc.createTextNode(txt));
25046 * Executes a Midas editor command on the editor document and performs necessary focus and
25047 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25048 * @param {String} cmd The Midas command
25049 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25051 relayCmd : function(cmd, value){
25053 this.execCmd(cmd, value);
25054 this.owner.fireEvent('editorevent', this);
25055 //this.updateToolbar();
25056 this.owner.deferFocus();
25060 * Executes a Midas editor command directly on the editor document.
25061 * For visual commands, you should use {@link #relayCmd} instead.
25062 * <b>This should only be called after the editor is initialized.</b>
25063 * @param {String} cmd The Midas command
25064 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25066 execCmd : function(cmd, value){
25067 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25074 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25076 * @param {String} text | dom node..
25078 insertAtCursor : function(text)
25081 if(!this.activated){
25087 var r = this.doc.selection.createRange();
25098 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25102 // from jquery ui (MIT licenced)
25104 var win = this.win;
25106 if (win.getSelection && win.getSelection().getRangeAt) {
25107 range = win.getSelection().getRangeAt(0);
25108 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25109 range.insertNode(node);
25110 } else if (win.document.selection && win.document.selection.createRange) {
25111 // no firefox support
25112 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25113 win.document.selection.createRange().pasteHTML(txt);
25115 // no firefox support
25116 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25117 this.execCmd('InsertHTML', txt);
25126 mozKeyPress : function(e){
25128 var c = e.getCharCode(), cmd;
25131 c = String.fromCharCode(c).toLowerCase();
25145 this.cleanUpPaste.defer(100, this);
25153 e.preventDefault();
25161 fixKeys : function(){ // load time branching for fastest keydown performance
25163 return function(e){
25164 var k = e.getKey(), r;
25167 r = this.doc.selection.createRange();
25170 r.pasteHTML('    ');
25177 r = this.doc.selection.createRange();
25179 var target = r.parentElement();
25180 if(!target || target.tagName.toLowerCase() != 'li'){
25182 r.pasteHTML('<br />');
25188 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25189 this.cleanUpPaste.defer(100, this);
25195 }else if(Roo.isOpera){
25196 return function(e){
25197 var k = e.getKey();
25201 this.execCmd('InsertHTML','    ');
25204 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25205 this.cleanUpPaste.defer(100, this);
25210 }else if(Roo.isSafari){
25211 return function(e){
25212 var k = e.getKey();
25216 this.execCmd('InsertText','\t');
25220 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25221 this.cleanUpPaste.defer(100, this);
25229 getAllAncestors: function()
25231 var p = this.getSelectedNode();
25234 a.push(p); // push blank onto stack..
25235 p = this.getParentElement();
25239 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25243 a.push(this.doc.body);
25247 lastSelNode : false,
25250 getSelection : function()
25252 this.assignDocWin();
25253 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25256 getSelectedNode: function()
25258 // this may only work on Gecko!!!
25260 // should we cache this!!!!
25265 var range = this.createRange(this.getSelection()).cloneRange();
25268 var parent = range.parentElement();
25270 var testRange = range.duplicate();
25271 testRange.moveToElementText(parent);
25272 if (testRange.inRange(range)) {
25275 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25278 parent = parent.parentElement;
25283 // is ancestor a text element.
25284 var ac = range.commonAncestorContainer;
25285 if (ac.nodeType == 3) {
25286 ac = ac.parentNode;
25289 var ar = ac.childNodes;
25292 var other_nodes = [];
25293 var has_other_nodes = false;
25294 for (var i=0;i<ar.length;i++) {
25295 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25298 // fullly contained node.
25300 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25305 // probably selected..
25306 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25307 other_nodes.push(ar[i]);
25311 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25316 has_other_nodes = true;
25318 if (!nodes.length && other_nodes.length) {
25319 nodes= other_nodes;
25321 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25327 createRange: function(sel)
25329 // this has strange effects when using with
25330 // top toolbar - not sure if it's a great idea.
25331 //this.editor.contentWindow.focus();
25332 if (typeof sel != "undefined") {
25334 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25336 return this.doc.createRange();
25339 return this.doc.createRange();
25342 getParentElement: function()
25345 this.assignDocWin();
25346 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25348 var range = this.createRange(sel);
25351 var p = range.commonAncestorContainer;
25352 while (p.nodeType == 3) { // text node
25363 * Range intersection.. the hard stuff...
25367 * [ -- selected range --- ]
25371 * if end is before start or hits it. fail.
25372 * if start is after end or hits it fail.
25374 * if either hits (but other is outside. - then it's not
25380 // @see http://www.thismuchiknow.co.uk/?p=64.
25381 rangeIntersectsNode : function(range, node)
25383 var nodeRange = node.ownerDocument.createRange();
25385 nodeRange.selectNode(node);
25387 nodeRange.selectNodeContents(node);
25390 var rangeStartRange = range.cloneRange();
25391 rangeStartRange.collapse(true);
25393 var rangeEndRange = range.cloneRange();
25394 rangeEndRange.collapse(false);
25396 var nodeStartRange = nodeRange.cloneRange();
25397 nodeStartRange.collapse(true);
25399 var nodeEndRange = nodeRange.cloneRange();
25400 nodeEndRange.collapse(false);
25402 return rangeStartRange.compareBoundaryPoints(
25403 Range.START_TO_START, nodeEndRange) == -1 &&
25404 rangeEndRange.compareBoundaryPoints(
25405 Range.START_TO_START, nodeStartRange) == 1;
25409 rangeCompareNode : function(range, node)
25411 var nodeRange = node.ownerDocument.createRange();
25413 nodeRange.selectNode(node);
25415 nodeRange.selectNodeContents(node);
25419 range.collapse(true);
25421 nodeRange.collapse(true);
25423 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25424 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25426 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25428 var nodeIsBefore = ss == 1;
25429 var nodeIsAfter = ee == -1;
25431 if (nodeIsBefore && nodeIsAfter) {
25434 if (!nodeIsBefore && nodeIsAfter) {
25435 return 1; //right trailed.
25438 if (nodeIsBefore && !nodeIsAfter) {
25439 return 2; // left trailed.
25445 // private? - in a new class?
25446 cleanUpPaste : function()
25448 // cleans up the whole document..
25449 Roo.log('cleanuppaste');
25451 this.cleanUpChildren(this.doc.body);
25452 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25453 if (clean != this.doc.body.innerHTML) {
25454 this.doc.body.innerHTML = clean;
25459 cleanWordChars : function(input) {// change the chars to hex code
25460 var he = Roo.HtmlEditorCore;
25462 var output = input;
25463 Roo.each(he.swapCodes, function(sw) {
25464 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25466 output = output.replace(swapper, sw[1]);
25473 cleanUpChildren : function (n)
25475 if (!n.childNodes.length) {
25478 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25479 this.cleanUpChild(n.childNodes[i]);
25486 cleanUpChild : function (node)
25489 //console.log(node);
25490 if (node.nodeName == "#text") {
25491 // clean up silly Windows -- stuff?
25494 if (node.nodeName == "#comment") {
25495 node.parentNode.removeChild(node);
25496 // clean up silly Windows -- stuff?
25499 var lcname = node.tagName.toLowerCase();
25500 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25501 // whitelist of tags..
25503 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25505 node.parentNode.removeChild(node);
25510 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25512 // spans with no attributes - just remove them..
25513 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25514 remove_keep_children = true;
25517 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25518 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25520 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25521 // remove_keep_children = true;
25524 if (remove_keep_children) {
25525 this.cleanUpChildren(node);
25526 // inserts everything just before this node...
25527 while (node.childNodes.length) {
25528 var cn = node.childNodes[0];
25529 node.removeChild(cn);
25530 node.parentNode.insertBefore(cn, node);
25532 node.parentNode.removeChild(node);
25536 if (!node.attributes || !node.attributes.length) {
25541 this.cleanUpChildren(node);
25545 function cleanAttr(n,v)
25548 if (v.match(/^\./) || v.match(/^\//)) {
25551 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25554 if (v.match(/^#/)) {
25557 if (v.match(/^\{/)) { // allow template editing.
25560 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25561 node.removeAttribute(n);
25565 var cwhite = this.cwhite;
25566 var cblack = this.cblack;
25568 function cleanStyle(n,v)
25570 if (v.match(/expression/)) { //XSS?? should we even bother..
25571 node.removeAttribute(n);
25575 var parts = v.split(/;/);
25578 Roo.each(parts, function(p) {
25579 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25583 var l = p.split(':').shift().replace(/\s+/g,'');
25584 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25586 if ( cwhite.length && cblack.indexOf(l) > -1) {
25587 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25588 //node.removeAttribute(n);
25592 // only allow 'c whitelisted system attributes'
25593 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25594 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25595 //node.removeAttribute(n);
25605 if (clean.length) {
25606 node.setAttribute(n, clean.join(';'));
25608 node.removeAttribute(n);
25614 for (var i = node.attributes.length-1; i > -1 ; i--) {
25615 var a = node.attributes[i];
25618 if (a.name.toLowerCase().substr(0,2)=='on') {
25619 node.removeAttribute(a.name);
25622 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25623 node.removeAttribute(a.name);
25626 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25627 cleanAttr(a.name,a.value); // fixme..
25630 if (a.name == 'style') {
25631 cleanStyle(a.name,a.value);
25634 /// clean up MS crap..
25635 // tecnically this should be a list of valid class'es..
25638 if (a.name == 'class') {
25639 if (a.value.match(/^Mso/)) {
25640 node.removeAttribute('class');
25643 if (a.value.match(/^body$/)) {
25644 node.removeAttribute('class');
25655 this.cleanUpChildren(node);
25661 * Clean up MS wordisms...
25663 cleanWord : function(node)
25666 this.cleanWord(this.doc.body);
25671 node.nodeName == 'SPAN' &&
25672 !node.hasAttributes() &&
25673 node.childNodes.length == 1 &&
25674 node.firstChild.nodeName == "#text"
25676 var textNode = node.firstChild;
25677 node.removeChild(textNode);
25678 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25679 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25681 node.parentNode.insertBefore(textNode, node);
25682 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25683 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25685 node.parentNode.removeChild(node);
25688 if (node.nodeName == "#text") {
25689 // clean up silly Windows -- stuff?
25692 if (node.nodeName == "#comment") {
25693 node.parentNode.removeChild(node);
25694 // clean up silly Windows -- stuff?
25698 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25699 node.parentNode.removeChild(node);
25702 //Roo.log(node.tagName);
25703 // remove - but keep children..
25704 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25705 //Roo.log('-- removed');
25706 while (node.childNodes.length) {
25707 var cn = node.childNodes[0];
25708 node.removeChild(cn);
25709 node.parentNode.insertBefore(cn, node);
25710 // move node to parent - and clean it..
25711 this.cleanWord(cn);
25713 node.parentNode.removeChild(node);
25714 /// no need to iterate chidlren = it's got none..
25715 //this.iterateChildren(node, this.cleanWord);
25719 if (node.className.length) {
25721 var cn = node.className.split(/\W+/);
25723 Roo.each(cn, function(cls) {
25724 if (cls.match(/Mso[a-zA-Z]+/)) {
25729 node.className = cna.length ? cna.join(' ') : '';
25731 node.removeAttribute("class");
25735 if (node.hasAttribute("lang")) {
25736 node.removeAttribute("lang");
25739 if (node.hasAttribute("style")) {
25741 var styles = node.getAttribute("style").split(";");
25743 Roo.each(styles, function(s) {
25744 if (!s.match(/:/)) {
25747 var kv = s.split(":");
25748 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25751 // what ever is left... we allow.
25754 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25755 if (!nstyle.length) {
25756 node.removeAttribute('style');
25759 this.iterateChildren(node, this.cleanWord);
25765 * iterateChildren of a Node, calling fn each time, using this as the scole..
25766 * @param {DomNode} node node to iterate children of.
25767 * @param {Function} fn method of this class to call on each item.
25769 iterateChildren : function(node, fn)
25771 if (!node.childNodes.length) {
25774 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25775 fn.call(this, node.childNodes[i])
25781 * cleanTableWidths.
25783 * Quite often pasting from word etc.. results in tables with column and widths.
25784 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25787 cleanTableWidths : function(node)
25792 this.cleanTableWidths(this.doc.body);
25797 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25800 Roo.log(node.tagName);
25801 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25802 this.iterateChildren(node, this.cleanTableWidths);
25805 if (node.hasAttribute('width')) {
25806 node.removeAttribute('width');
25810 if (node.hasAttribute("style")) {
25813 var styles = node.getAttribute("style").split(";");
25815 Roo.each(styles, function(s) {
25816 if (!s.match(/:/)) {
25819 var kv = s.split(":");
25820 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25823 // what ever is left... we allow.
25826 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25827 if (!nstyle.length) {
25828 node.removeAttribute('style');
25832 this.iterateChildren(node, this.cleanTableWidths);
25840 domToHTML : function(currentElement, depth, nopadtext) {
25842 depth = depth || 0;
25843 nopadtext = nopadtext || false;
25845 if (!currentElement) {
25846 return this.domToHTML(this.doc.body);
25849 //Roo.log(currentElement);
25851 var allText = false;
25852 var nodeName = currentElement.nodeName;
25853 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25855 if (nodeName == '#text') {
25857 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25862 if (nodeName != 'BODY') {
25865 // Prints the node tagName, such as <A>, <IMG>, etc
25868 for(i = 0; i < currentElement.attributes.length;i++) {
25870 var aname = currentElement.attributes.item(i).name;
25871 if (!currentElement.attributes.item(i).value.length) {
25874 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25877 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25886 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25889 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25894 // Traverse the tree
25896 var currentElementChild = currentElement.childNodes.item(i);
25897 var allText = true;
25898 var innerHTML = '';
25900 while (currentElementChild) {
25901 // Formatting code (indent the tree so it looks nice on the screen)
25902 var nopad = nopadtext;
25903 if (lastnode == 'SPAN') {
25907 if (currentElementChild.nodeName == '#text') {
25908 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25909 toadd = nopadtext ? toadd : toadd.trim();
25910 if (!nopad && toadd.length > 80) {
25911 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25913 innerHTML += toadd;
25916 currentElementChild = currentElement.childNodes.item(i);
25922 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25924 // Recursively traverse the tree structure of the child node
25925 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25926 lastnode = currentElementChild.nodeName;
25928 currentElementChild=currentElement.childNodes.item(i);
25934 // The remaining code is mostly for formatting the tree
25935 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25940 ret+= "</"+tagName+">";
25946 applyBlacklists : function()
25948 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25949 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25953 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25954 if (b.indexOf(tag) > -1) {
25957 this.white.push(tag);
25961 Roo.each(w, function(tag) {
25962 if (b.indexOf(tag) > -1) {
25965 if (this.white.indexOf(tag) > -1) {
25968 this.white.push(tag);
25973 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25974 if (w.indexOf(tag) > -1) {
25977 this.black.push(tag);
25981 Roo.each(b, function(tag) {
25982 if (w.indexOf(tag) > -1) {
25985 if (this.black.indexOf(tag) > -1) {
25988 this.black.push(tag);
25993 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25994 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25998 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25999 if (b.indexOf(tag) > -1) {
26002 this.cwhite.push(tag);
26006 Roo.each(w, function(tag) {
26007 if (b.indexOf(tag) > -1) {
26010 if (this.cwhite.indexOf(tag) > -1) {
26013 this.cwhite.push(tag);
26018 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26019 if (w.indexOf(tag) > -1) {
26022 this.cblack.push(tag);
26026 Roo.each(b, function(tag) {
26027 if (w.indexOf(tag) > -1) {
26030 if (this.cblack.indexOf(tag) > -1) {
26033 this.cblack.push(tag);
26038 setStylesheets : function(stylesheets)
26040 if(typeof(stylesheets) == 'string'){
26041 Roo.get(this.iframe.contentDocument.head).createChild({
26043 rel : 'stylesheet',
26052 Roo.each(stylesheets, function(s) {
26057 Roo.get(_this.iframe.contentDocument.head).createChild({
26059 rel : 'stylesheet',
26068 removeStylesheets : function()
26072 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26077 setStyle : function(style)
26079 Roo.get(this.iframe.contentDocument.head).createChild({
26088 // hide stuff that is not compatible
26102 * @event specialkey
26106 * @cfg {String} fieldClass @hide
26109 * @cfg {String} focusClass @hide
26112 * @cfg {String} autoCreate @hide
26115 * @cfg {String} inputType @hide
26118 * @cfg {String} invalidClass @hide
26121 * @cfg {String} invalidText @hide
26124 * @cfg {String} msgFx @hide
26127 * @cfg {String} validateOnBlur @hide
26131 Roo.HtmlEditorCore.white = [
26132 'area', 'br', 'img', 'input', 'hr', 'wbr',
26134 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26135 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26136 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26137 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26138 'table', 'ul', 'xmp',
26140 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26143 'dir', 'menu', 'ol', 'ul', 'dl',
26149 Roo.HtmlEditorCore.black = [
26150 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26152 'base', 'basefont', 'bgsound', 'blink', 'body',
26153 'frame', 'frameset', 'head', 'html', 'ilayer',
26154 'iframe', 'layer', 'link', 'meta', 'object',
26155 'script', 'style' ,'title', 'xml' // clean later..
26157 Roo.HtmlEditorCore.clean = [
26158 'script', 'style', 'title', 'xml'
26160 Roo.HtmlEditorCore.remove = [
26165 Roo.HtmlEditorCore.ablack = [
26169 Roo.HtmlEditorCore.aclean = [
26170 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26174 Roo.HtmlEditorCore.pwhite= [
26175 'http', 'https', 'mailto'
26178 // white listed style attributes.
26179 Roo.HtmlEditorCore.cwhite= [
26180 // 'text-align', /// default is to allow most things..
26186 // black listed style attributes.
26187 Roo.HtmlEditorCore.cblack= [
26188 // 'font-size' -- this can be set by the project
26192 Roo.HtmlEditorCore.swapCodes =[
26193 [ 8211, "–" ],
26194 [ 8212, "—" ],
26211 * @class Roo.bootstrap.HtmlEditor
26212 * @extends Roo.bootstrap.TextArea
26213 * Bootstrap HtmlEditor class
26216 * Create a new HtmlEditor
26217 * @param {Object} config The config object
26220 Roo.bootstrap.HtmlEditor = function(config){
26221 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26222 if (!this.toolbars) {
26223 this.toolbars = [];
26226 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26229 * @event initialize
26230 * Fires when the editor is fully initialized (including the iframe)
26231 * @param {HtmlEditor} this
26236 * Fires when the editor is first receives the focus. Any insertion must wait
26237 * until after this event.
26238 * @param {HtmlEditor} this
26242 * @event beforesync
26243 * Fires before the textarea is updated with content from the editor iframe. Return false
26244 * to cancel the sync.
26245 * @param {HtmlEditor} this
26246 * @param {String} html
26250 * @event beforepush
26251 * Fires before the iframe editor is updated with content from the textarea. Return false
26252 * to cancel the push.
26253 * @param {HtmlEditor} this
26254 * @param {String} html
26259 * Fires when the textarea is updated with content from the editor iframe.
26260 * @param {HtmlEditor} this
26261 * @param {String} html
26266 * Fires when the iframe editor is updated with content from the textarea.
26267 * @param {HtmlEditor} this
26268 * @param {String} html
26272 * @event editmodechange
26273 * Fires when the editor switches edit modes
26274 * @param {HtmlEditor} this
26275 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26277 editmodechange: true,
26279 * @event editorevent
26280 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26281 * @param {HtmlEditor} this
26285 * @event firstfocus
26286 * Fires when on first focus - needed by toolbars..
26287 * @param {HtmlEditor} this
26292 * Auto save the htmlEditor value as a file into Events
26293 * @param {HtmlEditor} this
26297 * @event savedpreview
26298 * preview the saved version of htmlEditor
26299 * @param {HtmlEditor} this
26306 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26310 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26315 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26320 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26325 * @cfg {Number} height (in pixels)
26329 * @cfg {Number} width (in pixels)
26334 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26337 stylesheets: false,
26342 // private properties
26343 validationEvent : false,
26345 initialized : false,
26348 onFocus : Roo.emptyFn,
26350 hideMode:'offsets',
26352 tbContainer : false,
26356 toolbarContainer :function() {
26357 return this.wrap.select('.x-html-editor-tb',true).first();
26361 * Protected method that will not generally be called directly. It
26362 * is called when the editor creates its toolbar. Override this method if you need to
26363 * add custom toolbar buttons.
26364 * @param {HtmlEditor} editor
26366 createToolbar : function(){
26367 Roo.log('renewing');
26368 Roo.log("create toolbars");
26370 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26371 this.toolbars[0].render(this.toolbarContainer());
26375 // if (!editor.toolbars || !editor.toolbars.length) {
26376 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26379 // for (var i =0 ; i < editor.toolbars.length;i++) {
26380 // editor.toolbars[i] = Roo.factory(
26381 // typeof(editor.toolbars[i]) == 'string' ?
26382 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26383 // Roo.bootstrap.HtmlEditor);
26384 // editor.toolbars[i].init(editor);
26390 onRender : function(ct, position)
26392 // Roo.log("Call onRender: " + this.xtype);
26394 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26396 this.wrap = this.inputEl().wrap({
26397 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26400 this.editorcore.onRender(ct, position);
26402 if (this.resizable) {
26403 this.resizeEl = new Roo.Resizable(this.wrap, {
26407 minHeight : this.height,
26408 height: this.height,
26409 handles : this.resizable,
26412 resize : function(r, w, h) {
26413 _t.onResize(w,h); // -something
26419 this.createToolbar(this);
26422 if(!this.width && this.resizable){
26423 this.setSize(this.wrap.getSize());
26425 if (this.resizeEl) {
26426 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26427 // should trigger onReize..
26433 onResize : function(w, h)
26435 Roo.log('resize: ' +w + ',' + h );
26436 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26440 if(this.inputEl() ){
26441 if(typeof w == 'number'){
26442 var aw = w - this.wrap.getFrameWidth('lr');
26443 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26446 if(typeof h == 'number'){
26447 var tbh = -11; // fixme it needs to tool bar size!
26448 for (var i =0; i < this.toolbars.length;i++) {
26449 // fixme - ask toolbars for heights?
26450 tbh += this.toolbars[i].el.getHeight();
26451 //if (this.toolbars[i].footer) {
26452 // tbh += this.toolbars[i].footer.el.getHeight();
26460 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26461 ah -= 5; // knock a few pixes off for look..
26462 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26466 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26467 this.editorcore.onResize(ew,eh);
26472 * Toggles the editor between standard and source edit mode.
26473 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26475 toggleSourceEdit : function(sourceEditMode)
26477 this.editorcore.toggleSourceEdit(sourceEditMode);
26479 if(this.editorcore.sourceEditMode){
26480 Roo.log('editor - showing textarea');
26483 // Roo.log(this.syncValue());
26485 this.inputEl().removeClass(['hide', 'x-hidden']);
26486 this.inputEl().dom.removeAttribute('tabIndex');
26487 this.inputEl().focus();
26489 Roo.log('editor - hiding textarea');
26491 // Roo.log(this.pushValue());
26494 this.inputEl().addClass(['hide', 'x-hidden']);
26495 this.inputEl().dom.setAttribute('tabIndex', -1);
26496 //this.deferFocus();
26499 if(this.resizable){
26500 this.setSize(this.wrap.getSize());
26503 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26506 // private (for BoxComponent)
26507 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26509 // private (for BoxComponent)
26510 getResizeEl : function(){
26514 // private (for BoxComponent)
26515 getPositionEl : function(){
26520 initEvents : function(){
26521 this.originalValue = this.getValue();
26525 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26528 // markInvalid : Roo.emptyFn,
26530 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26533 // clearInvalid : Roo.emptyFn,
26535 setValue : function(v){
26536 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26537 this.editorcore.pushValue();
26542 deferFocus : function(){
26543 this.focus.defer(10, this);
26547 focus : function(){
26548 this.editorcore.focus();
26554 onDestroy : function(){
26560 for (var i =0; i < this.toolbars.length;i++) {
26561 // fixme - ask toolbars for heights?
26562 this.toolbars[i].onDestroy();
26565 this.wrap.dom.innerHTML = '';
26566 this.wrap.remove();
26571 onFirstFocus : function(){
26572 //Roo.log("onFirstFocus");
26573 this.editorcore.onFirstFocus();
26574 for (var i =0; i < this.toolbars.length;i++) {
26575 this.toolbars[i].onFirstFocus();
26581 syncValue : function()
26583 this.editorcore.syncValue();
26586 pushValue : function()
26588 this.editorcore.pushValue();
26592 // hide stuff that is not compatible
26606 * @event specialkey
26610 * @cfg {String} fieldClass @hide
26613 * @cfg {String} focusClass @hide
26616 * @cfg {String} autoCreate @hide
26619 * @cfg {String} inputType @hide
26623 * @cfg {String} invalidText @hide
26626 * @cfg {String} msgFx @hide
26629 * @cfg {String} validateOnBlur @hide
26638 Roo.namespace('Roo.bootstrap.htmleditor');
26640 * @class Roo.bootstrap.HtmlEditorToolbar1
26646 new Roo.bootstrap.HtmlEditor({
26649 new Roo.bootstrap.HtmlEditorToolbar1({
26650 disable : { fonts: 1 , format: 1, ..., ... , ...],
26656 * @cfg {Object} disable List of elements to disable..
26657 * @cfg {Array} btns List of additional buttons.
26661 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26664 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26667 Roo.apply(this, config);
26669 // default disabled, based on 'good practice'..
26670 this.disable = this.disable || {};
26671 Roo.applyIf(this.disable, {
26674 specialElements : true
26676 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26678 this.editor = config.editor;
26679 this.editorcore = config.editor.editorcore;
26681 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26683 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26684 // dont call parent... till later.
26686 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26691 editorcore : false,
26696 "h1","h2","h3","h4","h5","h6",
26698 "abbr", "acronym", "address", "cite", "samp", "var",
26702 onRender : function(ct, position)
26704 // Roo.log("Call onRender: " + this.xtype);
26706 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26708 this.el.dom.style.marginBottom = '0';
26710 var editorcore = this.editorcore;
26711 var editor= this.editor;
26714 var btn = function(id,cmd , toggle, handler, html){
26716 var event = toggle ? 'toggle' : 'click';
26721 xns: Roo.bootstrap,
26725 enableToggle:toggle !== false,
26727 pressed : toggle ? false : null,
26730 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26731 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26737 // var cb_box = function...
26742 xns: Roo.bootstrap,
26747 xns: Roo.bootstrap,
26751 Roo.each(this.formats, function(f) {
26752 style.menu.items.push({
26754 xns: Roo.bootstrap,
26755 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26760 editorcore.insertTag(this.tagname);
26767 children.push(style);
26769 btn('bold',false,true);
26770 btn('italic',false,true);
26771 btn('align-left', 'justifyleft',true);
26772 btn('align-center', 'justifycenter',true);
26773 btn('align-right' , 'justifyright',true);
26774 btn('link', false, false, function(btn) {
26775 //Roo.log("create link?");
26776 var url = prompt(this.createLinkText, this.defaultLinkValue);
26777 if(url && url != 'http:/'+'/'){
26778 this.editorcore.relayCmd('createlink', url);
26781 btn('list','insertunorderedlist',true);
26782 btn('pencil', false,true, function(btn){
26784 this.toggleSourceEdit(btn.pressed);
26787 if (this.editor.btns.length > 0) {
26788 for (var i = 0; i<this.editor.btns.length; i++) {
26789 children.push(this.editor.btns[i]);
26797 xns: Roo.bootstrap,
26802 xns: Roo.bootstrap,
26807 cog.menu.items.push({
26809 xns: Roo.bootstrap,
26810 html : Clean styles,
26815 editorcore.insertTag(this.tagname);
26824 this.xtype = 'NavSimplebar';
26826 for(var i=0;i< children.length;i++) {
26828 this.buttons.add(this.addxtypeChild(children[i]));
26832 editor.on('editorevent', this.updateToolbar, this);
26834 onBtnClick : function(id)
26836 this.editorcore.relayCmd(id);
26837 this.editorcore.focus();
26841 * Protected method that will not generally be called directly. It triggers
26842 * a toolbar update by reading the markup state of the current selection in the editor.
26844 updateToolbar: function(){
26846 if(!this.editorcore.activated){
26847 this.editor.onFirstFocus(); // is this neeed?
26851 var btns = this.buttons;
26852 var doc = this.editorcore.doc;
26853 btns.get('bold').setActive(doc.queryCommandState('bold'));
26854 btns.get('italic').setActive(doc.queryCommandState('italic'));
26855 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26857 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26858 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26859 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26861 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26862 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26865 var ans = this.editorcore.getAllAncestors();
26866 if (this.formatCombo) {
26869 var store = this.formatCombo.store;
26870 this.formatCombo.setValue("");
26871 for (var i =0; i < ans.length;i++) {
26872 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26874 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26882 // hides menus... - so this cant be on a menu...
26883 Roo.bootstrap.MenuMgr.hideAll();
26885 Roo.bootstrap.MenuMgr.hideAll();
26886 //this.editorsyncValue();
26888 onFirstFocus: function() {
26889 this.buttons.each(function(item){
26893 toggleSourceEdit : function(sourceEditMode){
26896 if(sourceEditMode){
26897 Roo.log("disabling buttons");
26898 this.buttons.each( function(item){
26899 if(item.cmd != 'pencil'){
26905 Roo.log("enabling buttons");
26906 if(this.editorcore.initialized){
26907 this.buttons.each( function(item){
26913 Roo.log("calling toggole on editor");
26914 // tell the editor that it's been pressed..
26915 this.editor.toggleSourceEdit(sourceEditMode);
26929 * @class Roo.bootstrap.Markdown
26930 * @extends Roo.bootstrap.TextArea
26931 * Bootstrap Showdown editable area
26932 * @cfg {string} content
26935 * Create a new Showdown
26938 Roo.bootstrap.Markdown = function(config){
26939 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26943 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26947 initEvents : function()
26950 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26951 this.markdownEl = this.el.createChild({
26952 cls : 'roo-markdown-area'
26954 this.inputEl().addClass('d-none');
26955 if (this.getValue() == '') {
26956 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26959 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26961 this.markdownEl.on('click', this.toggleTextEdit, this);
26962 this.on('blur', this.toggleTextEdit, this);
26963 this.on('specialkey', this.resizeTextArea, this);
26966 toggleTextEdit : function()
26968 var sh = this.markdownEl.getHeight();
26969 this.inputEl().addClass('d-none');
26970 this.markdownEl.addClass('d-none');
26971 if (!this.editing) {
26973 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26974 this.inputEl().removeClass('d-none');
26975 this.inputEl().focus();
26976 this.editing = true;
26979 // show showdown...
26980 this.updateMarkdown();
26981 this.markdownEl.removeClass('d-none');
26982 this.editing = false;
26985 updateMarkdown : function()
26987 if (this.getValue() == '') {
26988 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26992 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26995 resizeTextArea: function () {
26998 Roo.log([sh, this.getValue().split("\n").length * 30]);
26999 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27001 setValue : function(val)
27003 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27004 if (!this.editing) {
27005 this.updateMarkdown();
27011 if (!this.editing) {
27012 this.toggleTextEdit();
27020 * @class Roo.bootstrap.Table.AbstractSelectionModel
27021 * @extends Roo.util.Observable
27022 * Abstract base class for grid SelectionModels. It provides the interface that should be
27023 * implemented by descendant classes. This class should not be directly instantiated.
27026 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27027 this.locked = false;
27028 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27032 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27033 /** @ignore Called by the grid automatically. Do not call directly. */
27034 init : function(grid){
27040 * Locks the selections.
27043 this.locked = true;
27047 * Unlocks the selections.
27049 unlock : function(){
27050 this.locked = false;
27054 * Returns true if the selections are locked.
27055 * @return {Boolean}
27057 isLocked : function(){
27058 return this.locked;
27062 initEvents : function ()
27068 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27069 * @class Roo.bootstrap.Table.RowSelectionModel
27070 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27071 * It supports multiple selections and keyboard selection/navigation.
27073 * @param {Object} config
27076 Roo.bootstrap.Table.RowSelectionModel = function(config){
27077 Roo.apply(this, config);
27078 this.selections = new Roo.util.MixedCollection(false, function(o){
27083 this.lastActive = false;
27087 * @event selectionchange
27088 * Fires when the selection changes
27089 * @param {SelectionModel} this
27091 "selectionchange" : true,
27093 * @event afterselectionchange
27094 * Fires after the selection changes (eg. by key press or clicking)
27095 * @param {SelectionModel} this
27097 "afterselectionchange" : true,
27099 * @event beforerowselect
27100 * Fires when a row is selected being selected, return false to cancel.
27101 * @param {SelectionModel} this
27102 * @param {Number} rowIndex The selected index
27103 * @param {Boolean} keepExisting False if other selections will be cleared
27105 "beforerowselect" : true,
27108 * Fires when a row is selected.
27109 * @param {SelectionModel} this
27110 * @param {Number} rowIndex The selected index
27111 * @param {Roo.data.Record} r The record
27113 "rowselect" : true,
27115 * @event rowdeselect
27116 * Fires when a row is deselected.
27117 * @param {SelectionModel} this
27118 * @param {Number} rowIndex The selected index
27120 "rowdeselect" : true
27122 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27123 this.locked = false;
27126 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27128 * @cfg {Boolean} singleSelect
27129 * True to allow selection of only one row at a time (defaults to false)
27131 singleSelect : false,
27134 initEvents : function()
27137 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27138 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27139 //}else{ // allow click to work like normal
27140 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27142 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27143 this.grid.on("rowclick", this.handleMouseDown, this);
27145 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27146 "up" : function(e){
27148 this.selectPrevious(e.shiftKey);
27149 }else if(this.last !== false && this.lastActive !== false){
27150 var last = this.last;
27151 this.selectRange(this.last, this.lastActive-1);
27152 this.grid.getView().focusRow(this.lastActive);
27153 if(last !== false){
27157 this.selectFirstRow();
27159 this.fireEvent("afterselectionchange", this);
27161 "down" : function(e){
27163 this.selectNext(e.shiftKey);
27164 }else if(this.last !== false && this.lastActive !== false){
27165 var last = this.last;
27166 this.selectRange(this.last, this.lastActive+1);
27167 this.grid.getView().focusRow(this.lastActive);
27168 if(last !== false){
27172 this.selectFirstRow();
27174 this.fireEvent("afterselectionchange", this);
27178 this.grid.store.on('load', function(){
27179 this.selections.clear();
27182 var view = this.grid.view;
27183 view.on("refresh", this.onRefresh, this);
27184 view.on("rowupdated", this.onRowUpdated, this);
27185 view.on("rowremoved", this.onRemove, this);
27190 onRefresh : function()
27192 var ds = this.grid.store, i, v = this.grid.view;
27193 var s = this.selections;
27194 s.each(function(r){
27195 if((i = ds.indexOfId(r.id)) != -1){
27204 onRemove : function(v, index, r){
27205 this.selections.remove(r);
27209 onRowUpdated : function(v, index, r){
27210 if(this.isSelected(r)){
27211 v.onRowSelect(index);
27217 * @param {Array} records The records to select
27218 * @param {Boolean} keepExisting (optional) True to keep existing selections
27220 selectRecords : function(records, keepExisting)
27223 this.clearSelections();
27225 var ds = this.grid.store;
27226 for(var i = 0, len = records.length; i < len; i++){
27227 this.selectRow(ds.indexOf(records[i]), true);
27232 * Gets the number of selected rows.
27235 getCount : function(){
27236 return this.selections.length;
27240 * Selects the first row in the grid.
27242 selectFirstRow : function(){
27247 * Select the last row.
27248 * @param {Boolean} keepExisting (optional) True to keep existing selections
27250 selectLastRow : function(keepExisting){
27251 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27252 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27256 * Selects the row immediately following the last selected row.
27257 * @param {Boolean} keepExisting (optional) True to keep existing selections
27259 selectNext : function(keepExisting)
27261 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27262 this.selectRow(this.last+1, keepExisting);
27263 this.grid.getView().focusRow(this.last);
27268 * Selects the row that precedes the last selected row.
27269 * @param {Boolean} keepExisting (optional) True to keep existing selections
27271 selectPrevious : function(keepExisting){
27273 this.selectRow(this.last-1, keepExisting);
27274 this.grid.getView().focusRow(this.last);
27279 * Returns the selected records
27280 * @return {Array} Array of selected records
27282 getSelections : function(){
27283 return [].concat(this.selections.items);
27287 * Returns the first selected record.
27290 getSelected : function(){
27291 return this.selections.itemAt(0);
27296 * Clears all selections.
27298 clearSelections : function(fast)
27304 var ds = this.grid.store;
27305 var s = this.selections;
27306 s.each(function(r){
27307 this.deselectRow(ds.indexOfId(r.id));
27311 this.selections.clear();
27318 * Selects all rows.
27320 selectAll : function(){
27324 this.selections.clear();
27325 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27326 this.selectRow(i, true);
27331 * Returns True if there is a selection.
27332 * @return {Boolean}
27334 hasSelection : function(){
27335 return this.selections.length > 0;
27339 * Returns True if the specified row is selected.
27340 * @param {Number/Record} record The record or index of the record to check
27341 * @return {Boolean}
27343 isSelected : function(index){
27344 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27345 return (r && this.selections.key(r.id) ? true : false);
27349 * Returns True if the specified record id is selected.
27350 * @param {String} id The id of record to check
27351 * @return {Boolean}
27353 isIdSelected : function(id){
27354 return (this.selections.key(id) ? true : false);
27359 handleMouseDBClick : function(e, t){
27363 handleMouseDown : function(e, t)
27365 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27366 if(this.isLocked() || rowIndex < 0 ){
27369 if(e.shiftKey && this.last !== false){
27370 var last = this.last;
27371 this.selectRange(last, rowIndex, e.ctrlKey);
27372 this.last = last; // reset the last
27376 var isSelected = this.isSelected(rowIndex);
27377 //Roo.log("select row:" + rowIndex);
27379 this.deselectRow(rowIndex);
27381 this.selectRow(rowIndex, true);
27385 if(e.button !== 0 && isSelected){
27386 alert('rowIndex 2: ' + rowIndex);
27387 view.focusRow(rowIndex);
27388 }else if(e.ctrlKey && isSelected){
27389 this.deselectRow(rowIndex);
27390 }else if(!isSelected){
27391 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27392 view.focusRow(rowIndex);
27396 this.fireEvent("afterselectionchange", this);
27399 handleDragableRowClick : function(grid, rowIndex, e)
27401 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27402 this.selectRow(rowIndex, false);
27403 grid.view.focusRow(rowIndex);
27404 this.fireEvent("afterselectionchange", this);
27409 * Selects multiple rows.
27410 * @param {Array} rows Array of the indexes of the row to select
27411 * @param {Boolean} keepExisting (optional) True to keep existing selections
27413 selectRows : function(rows, keepExisting){
27415 this.clearSelections();
27417 for(var i = 0, len = rows.length; i < len; i++){
27418 this.selectRow(rows[i], true);
27423 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27424 * @param {Number} startRow The index of the first row in the range
27425 * @param {Number} endRow The index of the last row in the range
27426 * @param {Boolean} keepExisting (optional) True to retain existing selections
27428 selectRange : function(startRow, endRow, keepExisting){
27433 this.clearSelections();
27435 if(startRow <= endRow){
27436 for(var i = startRow; i <= endRow; i++){
27437 this.selectRow(i, true);
27440 for(var i = startRow; i >= endRow; i--){
27441 this.selectRow(i, true);
27447 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27448 * @param {Number} startRow The index of the first row in the range
27449 * @param {Number} endRow The index of the last row in the range
27451 deselectRange : function(startRow, endRow, preventViewNotify){
27455 for(var i = startRow; i <= endRow; i++){
27456 this.deselectRow(i, preventViewNotify);
27462 * @param {Number} row The index of the row to select
27463 * @param {Boolean} keepExisting (optional) True to keep existing selections
27465 selectRow : function(index, keepExisting, preventViewNotify)
27467 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27470 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27471 if(!keepExisting || this.singleSelect){
27472 this.clearSelections();
27475 var r = this.grid.store.getAt(index);
27476 //console.log('selectRow - record id :' + r.id);
27478 this.selections.add(r);
27479 this.last = this.lastActive = index;
27480 if(!preventViewNotify){
27481 var proxy = new Roo.Element(
27482 this.grid.getRowDom(index)
27484 proxy.addClass('bg-info info');
27486 this.fireEvent("rowselect", this, index, r);
27487 this.fireEvent("selectionchange", this);
27493 * @param {Number} row The index of the row to deselect
27495 deselectRow : function(index, preventViewNotify)
27500 if(this.last == index){
27503 if(this.lastActive == index){
27504 this.lastActive = false;
27507 var r = this.grid.store.getAt(index);
27512 this.selections.remove(r);
27513 //.console.log('deselectRow - record id :' + r.id);
27514 if(!preventViewNotify){
27516 var proxy = new Roo.Element(
27517 this.grid.getRowDom(index)
27519 proxy.removeClass('bg-info info');
27521 this.fireEvent("rowdeselect", this, index);
27522 this.fireEvent("selectionchange", this);
27526 restoreLast : function(){
27528 this.last = this._last;
27533 acceptsNav : function(row, col, cm){
27534 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27538 onEditorKey : function(field, e){
27539 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27544 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27546 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27548 }else if(k == e.ENTER && !e.ctrlKey){
27552 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27554 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27556 }else if(k == e.ESC){
27560 g.startEditing(newCell[0], newCell[1]);
27566 * Ext JS Library 1.1.1
27567 * Copyright(c) 2006-2007, Ext JS, LLC.
27569 * Originally Released Under LGPL - original licence link has changed is not relivant.
27572 * <script type="text/javascript">
27576 * @class Roo.bootstrap.PagingToolbar
27577 * @extends Roo.bootstrap.NavSimplebar
27578 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27580 * Create a new PagingToolbar
27581 * @param {Object} config The config object
27582 * @param {Roo.data.Store} store
27584 Roo.bootstrap.PagingToolbar = function(config)
27586 // old args format still supported... - xtype is prefered..
27587 // created from xtype...
27589 this.ds = config.dataSource;
27591 if (config.store && !this.ds) {
27592 this.store= Roo.factory(config.store, Roo.data);
27593 this.ds = this.store;
27594 this.ds.xmodule = this.xmodule || false;
27597 this.toolbarItems = [];
27598 if (config.items) {
27599 this.toolbarItems = config.items;
27602 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27607 this.bind(this.ds);
27610 if (Roo.bootstrap.version == 4) {
27611 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27613 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27618 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27620 * @cfg {Roo.data.Store} dataSource
27621 * The underlying data store providing the paged data
27624 * @cfg {String/HTMLElement/Element} container
27625 * container The id or element that will contain the toolbar
27628 * @cfg {Boolean} displayInfo
27629 * True to display the displayMsg (defaults to false)
27632 * @cfg {Number} pageSize
27633 * The number of records to display per page (defaults to 20)
27637 * @cfg {String} displayMsg
27638 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27640 displayMsg : 'Displaying {0} - {1} of {2}',
27642 * @cfg {String} emptyMsg
27643 * The message to display when no records are found (defaults to "No data to display")
27645 emptyMsg : 'No data to display',
27647 * Customizable piece of the default paging text (defaults to "Page")
27650 beforePageText : "Page",
27652 * Customizable piece of the default paging text (defaults to "of %0")
27655 afterPageText : "of {0}",
27657 * Customizable piece of the default paging text (defaults to "First Page")
27660 firstText : "First Page",
27662 * Customizable piece of the default paging text (defaults to "Previous Page")
27665 prevText : "Previous Page",
27667 * Customizable piece of the default paging text (defaults to "Next Page")
27670 nextText : "Next Page",
27672 * Customizable piece of the default paging text (defaults to "Last Page")
27675 lastText : "Last Page",
27677 * Customizable piece of the default paging text (defaults to "Refresh")
27680 refreshText : "Refresh",
27684 onRender : function(ct, position)
27686 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27687 this.navgroup.parentId = this.id;
27688 this.navgroup.onRender(this.el, null);
27689 // add the buttons to the navgroup
27691 if(this.displayInfo){
27692 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27693 this.displayEl = this.el.select('.x-paging-info', true).first();
27694 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27695 // this.displayEl = navel.el.select('span',true).first();
27701 Roo.each(_this.buttons, function(e){ // this might need to use render????
27702 Roo.factory(e).render(_this.el);
27706 Roo.each(_this.toolbarItems, function(e) {
27707 _this.navgroup.addItem(e);
27711 this.first = this.navgroup.addItem({
27712 tooltip: this.firstText,
27713 cls: "prev btn-outline-secondary",
27714 html : ' <i class="fa fa-step-backward"></i>',
27716 preventDefault: true,
27717 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27720 this.prev = this.navgroup.addItem({
27721 tooltip: this.prevText,
27722 cls: "prev btn-outline-secondary",
27723 html : ' <i class="fa fa-backward"></i>',
27725 preventDefault: true,
27726 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27728 //this.addSeparator();
27731 var field = this.navgroup.addItem( {
27733 cls : 'x-paging-position btn-outline-secondary',
27735 html : this.beforePageText +
27736 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27737 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27740 this.field = field.el.select('input', true).first();
27741 this.field.on("keydown", this.onPagingKeydown, this);
27742 this.field.on("focus", function(){this.dom.select();});
27745 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27746 //this.field.setHeight(18);
27747 //this.addSeparator();
27748 this.next = this.navgroup.addItem({
27749 tooltip: this.nextText,
27750 cls: "next btn-outline-secondary",
27751 html : ' <i class="fa fa-forward"></i>',
27753 preventDefault: true,
27754 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27756 this.last = this.navgroup.addItem({
27757 tooltip: this.lastText,
27758 html : ' <i class="fa fa-step-forward"></i>',
27759 cls: "next btn-outline-secondary",
27761 preventDefault: true,
27762 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27764 //this.addSeparator();
27765 this.loading = this.navgroup.addItem({
27766 tooltip: this.refreshText,
27767 cls: "btn-outline-secondary",
27768 html : ' <i class="fa fa-refresh"></i>',
27769 preventDefault: true,
27770 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27776 updateInfo : function(){
27777 if(this.displayEl){
27778 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27779 var msg = count == 0 ?
27783 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27785 this.displayEl.update(msg);
27790 onLoad : function(ds, r, o)
27792 this.cursor = o.params && o.params.start ? o.params.start : 0;
27794 var d = this.getPageData(),
27799 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27800 this.field.dom.value = ap;
27801 this.first.setDisabled(ap == 1);
27802 this.prev.setDisabled(ap == 1);
27803 this.next.setDisabled(ap == ps);
27804 this.last.setDisabled(ap == ps);
27805 this.loading.enable();
27810 getPageData : function(){
27811 var total = this.ds.getTotalCount();
27814 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27815 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27820 onLoadError : function(){
27821 this.loading.enable();
27825 onPagingKeydown : function(e){
27826 var k = e.getKey();
27827 var d = this.getPageData();
27829 var v = this.field.dom.value, pageNum;
27830 if(!v || isNaN(pageNum = parseInt(v, 10))){
27831 this.field.dom.value = d.activePage;
27834 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27835 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27838 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))
27840 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27841 this.field.dom.value = pageNum;
27842 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27845 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27847 var v = this.field.dom.value, pageNum;
27848 var increment = (e.shiftKey) ? 10 : 1;
27849 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27852 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27853 this.field.dom.value = d.activePage;
27856 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27858 this.field.dom.value = parseInt(v, 10) + increment;
27859 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27860 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27867 beforeLoad : function(){
27869 this.loading.disable();
27874 onClick : function(which){
27883 ds.load({params:{start: 0, limit: this.pageSize}});
27886 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27889 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27892 var total = ds.getTotalCount();
27893 var extra = total % this.pageSize;
27894 var lastStart = extra ? (total - extra) : total-this.pageSize;
27895 ds.load({params:{start: lastStart, limit: this.pageSize}});
27898 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27904 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27905 * @param {Roo.data.Store} store The data store to unbind
27907 unbind : function(ds){
27908 ds.un("beforeload", this.beforeLoad, this);
27909 ds.un("load", this.onLoad, this);
27910 ds.un("loadexception", this.onLoadError, this);
27911 ds.un("remove", this.updateInfo, this);
27912 ds.un("add", this.updateInfo, this);
27913 this.ds = undefined;
27917 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27918 * @param {Roo.data.Store} store The data store to bind
27920 bind : function(ds){
27921 ds.on("beforeload", this.beforeLoad, this);
27922 ds.on("load", this.onLoad, this);
27923 ds.on("loadexception", this.onLoadError, this);
27924 ds.on("remove", this.updateInfo, this);
27925 ds.on("add", this.updateInfo, this);
27936 * @class Roo.bootstrap.MessageBar
27937 * @extends Roo.bootstrap.Component
27938 * Bootstrap MessageBar class
27939 * @cfg {String} html contents of the MessageBar
27940 * @cfg {String} weight (info | success | warning | danger) default info
27941 * @cfg {String} beforeClass insert the bar before the given class
27942 * @cfg {Boolean} closable (true | false) default false
27943 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27946 * Create a new Element
27947 * @param {Object} config The config object
27950 Roo.bootstrap.MessageBar = function(config){
27951 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27954 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27960 beforeClass: 'bootstrap-sticky-wrap',
27962 getAutoCreate : function(){
27966 cls: 'alert alert-dismissable alert-' + this.weight,
27971 html: this.html || ''
27977 cfg.cls += ' alert-messages-fixed';
27991 onRender : function(ct, position)
27993 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27996 var cfg = Roo.apply({}, this.getAutoCreate());
28000 cfg.cls += ' ' + this.cls;
28003 cfg.style = this.style;
28005 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28007 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28010 this.el.select('>button.close').on('click', this.hide, this);
28016 if (!this.rendered) {
28022 this.fireEvent('show', this);
28028 if (!this.rendered) {
28034 this.fireEvent('hide', this);
28037 update : function()
28039 // var e = this.el.dom.firstChild;
28041 // if(this.closable){
28042 // e = e.nextSibling;
28045 // e.data = this.html || '';
28047 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28063 * @class Roo.bootstrap.Graph
28064 * @extends Roo.bootstrap.Component
28065 * Bootstrap Graph class
28069 @cfg {String} graphtype bar | vbar | pie
28070 @cfg {number} g_x coodinator | centre x (pie)
28071 @cfg {number} g_y coodinator | centre y (pie)
28072 @cfg {number} g_r radius (pie)
28073 @cfg {number} g_height height of the chart (respected by all elements in the set)
28074 @cfg {number} g_width width of the chart (respected by all elements in the set)
28075 @cfg {Object} title The title of the chart
28078 -opts (object) options for the chart
28080 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28081 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28083 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.
28084 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28086 o stretch (boolean)
28088 -opts (object) options for the pie
28091 o startAngle (number)
28092 o endAngle (number)
28096 * Create a new Input
28097 * @param {Object} config The config object
28100 Roo.bootstrap.Graph = function(config){
28101 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28107 * The img click event for the img.
28108 * @param {Roo.EventObject} e
28114 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28125 //g_colors: this.colors,
28132 getAutoCreate : function(){
28143 onRender : function(ct,position){
28146 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28148 if (typeof(Raphael) == 'undefined') {
28149 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28153 this.raphael = Raphael(this.el.dom);
28155 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28156 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28157 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28158 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28160 r.text(160, 10, "Single Series Chart").attr(txtattr);
28161 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28162 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28163 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28165 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28166 r.barchart(330, 10, 300, 220, data1);
28167 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28168 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28171 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28172 // r.barchart(30, 30, 560, 250, xdata, {
28173 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28174 // axis : "0 0 1 1",
28175 // axisxlabels : xdata
28176 // //yvalues : cols,
28179 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28181 // this.load(null,xdata,{
28182 // axis : "0 0 1 1",
28183 // axisxlabels : xdata
28188 load : function(graphtype,xdata,opts)
28190 this.raphael.clear();
28192 graphtype = this.graphtype;
28197 var r = this.raphael,
28198 fin = function () {
28199 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28201 fout = function () {
28202 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28204 pfin = function() {
28205 this.sector.stop();
28206 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28209 this.label[0].stop();
28210 this.label[0].attr({ r: 7.5 });
28211 this.label[1].attr({ "font-weight": 800 });
28214 pfout = function() {
28215 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28218 this.label[0].animate({ r: 5 }, 500, "bounce");
28219 this.label[1].attr({ "font-weight": 400 });
28225 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28228 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28231 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28232 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28234 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28241 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28246 setTitle: function(o)
28251 initEvents: function() {
28254 this.el.on('click', this.onClick, this);
28258 onClick : function(e)
28260 Roo.log('img onclick');
28261 this.fireEvent('click', this, e);
28273 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28276 * @class Roo.bootstrap.dash.NumberBox
28277 * @extends Roo.bootstrap.Component
28278 * Bootstrap NumberBox class
28279 * @cfg {String} headline Box headline
28280 * @cfg {String} content Box content
28281 * @cfg {String} icon Box icon
28282 * @cfg {String} footer Footer text
28283 * @cfg {String} fhref Footer href
28286 * Create a new NumberBox
28287 * @param {Object} config The config object
28291 Roo.bootstrap.dash.NumberBox = function(config){
28292 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28296 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28305 getAutoCreate : function(){
28309 cls : 'small-box ',
28317 cls : 'roo-headline',
28318 html : this.headline
28322 cls : 'roo-content',
28323 html : this.content
28337 cls : 'ion ' + this.icon
28346 cls : 'small-box-footer',
28347 href : this.fhref || '#',
28351 cfg.cn.push(footer);
28358 onRender : function(ct,position){
28359 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28366 setHeadline: function (value)
28368 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28371 setFooter: function (value, href)
28373 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28376 this.el.select('a.small-box-footer',true).first().attr('href', href);
28381 setContent: function (value)
28383 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28386 initEvents: function()
28400 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28403 * @class Roo.bootstrap.dash.TabBox
28404 * @extends Roo.bootstrap.Component
28405 * Bootstrap TabBox class
28406 * @cfg {String} title Title of the TabBox
28407 * @cfg {String} icon Icon of the TabBox
28408 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28409 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28412 * Create a new TabBox
28413 * @param {Object} config The config object
28417 Roo.bootstrap.dash.TabBox = function(config){
28418 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28423 * When a pane is added
28424 * @param {Roo.bootstrap.dash.TabPane} pane
28428 * @event activatepane
28429 * When a pane is activated
28430 * @param {Roo.bootstrap.dash.TabPane} pane
28432 "activatepane" : true
28440 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28445 tabScrollable : false,
28447 getChildContainer : function()
28449 return this.el.select('.tab-content', true).first();
28452 getAutoCreate : function(){
28456 cls: 'pull-left header',
28464 cls: 'fa ' + this.icon
28470 cls: 'nav nav-tabs pull-right',
28476 if(this.tabScrollable){
28483 cls: 'nav nav-tabs pull-right',
28494 cls: 'nav-tabs-custom',
28499 cls: 'tab-content no-padding',
28507 initEvents : function()
28509 //Roo.log('add add pane handler');
28510 this.on('addpane', this.onAddPane, this);
28513 * Updates the box title
28514 * @param {String} html to set the title to.
28516 setTitle : function(value)
28518 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28520 onAddPane : function(pane)
28522 this.panes.push(pane);
28523 //Roo.log('addpane');
28525 // tabs are rendere left to right..
28526 if(!this.showtabs){
28530 var ctr = this.el.select('.nav-tabs', true).first();
28533 var existing = ctr.select('.nav-tab',true);
28534 var qty = existing.getCount();;
28537 var tab = ctr.createChild({
28539 cls : 'nav-tab' + (qty ? '' : ' active'),
28547 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28550 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28552 pane.el.addClass('active');
28557 onTabClick : function(ev,un,ob,pane)
28559 //Roo.log('tab - prev default');
28560 ev.preventDefault();
28563 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28564 pane.tab.addClass('active');
28565 //Roo.log(pane.title);
28566 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28567 // technically we should have a deactivate event.. but maybe add later.
28568 // and it should not de-activate the selected tab...
28569 this.fireEvent('activatepane', pane);
28570 pane.el.addClass('active');
28571 pane.fireEvent('activate');
28576 getActivePane : function()
28579 Roo.each(this.panes, function(p) {
28580 if(p.el.hasClass('active')){
28601 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28603 * @class Roo.bootstrap.TabPane
28604 * @extends Roo.bootstrap.Component
28605 * Bootstrap TabPane class
28606 * @cfg {Boolean} active (false | true) Default false
28607 * @cfg {String} title title of panel
28611 * Create a new TabPane
28612 * @param {Object} config The config object
28615 Roo.bootstrap.dash.TabPane = function(config){
28616 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28622 * When a pane is activated
28623 * @param {Roo.bootstrap.dash.TabPane} pane
28630 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28635 // the tabBox that this is attached to.
28638 getAutoCreate : function()
28646 cfg.cls += ' active';
28651 initEvents : function()
28653 //Roo.log('trigger add pane handler');
28654 this.parent().fireEvent('addpane', this)
28658 * Updates the tab title
28659 * @param {String} html to set the title to.
28661 setTitle: function(str)
28667 this.tab.select('a', true).first().dom.innerHTML = str;
28684 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28687 * @class Roo.bootstrap.menu.Menu
28688 * @extends Roo.bootstrap.Component
28689 * Bootstrap Menu class - container for Menu
28690 * @cfg {String} html Text of the menu
28691 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28692 * @cfg {String} icon Font awesome icon
28693 * @cfg {String} pos Menu align to (top | bottom) default bottom
28697 * Create a new Menu
28698 * @param {Object} config The config object
28702 Roo.bootstrap.menu.Menu = function(config){
28703 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28707 * @event beforeshow
28708 * Fires before this menu is displayed
28709 * @param {Roo.bootstrap.menu.Menu} this
28713 * @event beforehide
28714 * Fires before this menu is hidden
28715 * @param {Roo.bootstrap.menu.Menu} this
28720 * Fires after this menu is displayed
28721 * @param {Roo.bootstrap.menu.Menu} this
28726 * Fires after this menu is hidden
28727 * @param {Roo.bootstrap.menu.Menu} this
28732 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28733 * @param {Roo.bootstrap.menu.Menu} this
28734 * @param {Roo.EventObject} e
28741 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28745 weight : 'default',
28750 getChildContainer : function() {
28751 if(this.isSubMenu){
28755 return this.el.select('ul.dropdown-menu', true).first();
28758 getAutoCreate : function()
28763 cls : 'roo-menu-text',
28771 cls : 'fa ' + this.icon
28782 cls : 'dropdown-button btn btn-' + this.weight,
28787 cls : 'dropdown-toggle btn btn-' + this.weight,
28797 cls : 'dropdown-menu'
28803 if(this.pos == 'top'){
28804 cfg.cls += ' dropup';
28807 if(this.isSubMenu){
28810 cls : 'dropdown-menu'
28817 onRender : function(ct, position)
28819 this.isSubMenu = ct.hasClass('dropdown-submenu');
28821 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28824 initEvents : function()
28826 if(this.isSubMenu){
28830 this.hidden = true;
28832 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28833 this.triggerEl.on('click', this.onTriggerPress, this);
28835 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28836 this.buttonEl.on('click', this.onClick, this);
28842 if(this.isSubMenu){
28846 return this.el.select('ul.dropdown-menu', true).first();
28849 onClick : function(e)
28851 this.fireEvent("click", this, e);
28854 onTriggerPress : function(e)
28856 if (this.isVisible()) {
28863 isVisible : function(){
28864 return !this.hidden;
28869 this.fireEvent("beforeshow", this);
28871 this.hidden = false;
28872 this.el.addClass('open');
28874 Roo.get(document).on("mouseup", this.onMouseUp, this);
28876 this.fireEvent("show", this);
28883 this.fireEvent("beforehide", this);
28885 this.hidden = true;
28886 this.el.removeClass('open');
28888 Roo.get(document).un("mouseup", this.onMouseUp);
28890 this.fireEvent("hide", this);
28893 onMouseUp : function()
28907 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28910 * @class Roo.bootstrap.menu.Item
28911 * @extends Roo.bootstrap.Component
28912 * Bootstrap MenuItem class
28913 * @cfg {Boolean} submenu (true | false) default false
28914 * @cfg {String} html text of the item
28915 * @cfg {String} href the link
28916 * @cfg {Boolean} disable (true | false) default false
28917 * @cfg {Boolean} preventDefault (true | false) default true
28918 * @cfg {String} icon Font awesome icon
28919 * @cfg {String} pos Submenu align to (left | right) default right
28923 * Create a new Item
28924 * @param {Object} config The config object
28928 Roo.bootstrap.menu.Item = function(config){
28929 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28933 * Fires when the mouse is hovering over this menu
28934 * @param {Roo.bootstrap.menu.Item} this
28935 * @param {Roo.EventObject} e
28940 * Fires when the mouse exits this menu
28941 * @param {Roo.bootstrap.menu.Item} this
28942 * @param {Roo.EventObject} e
28948 * The raw click event for the entire grid.
28949 * @param {Roo.EventObject} e
28955 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28960 preventDefault: true,
28965 getAutoCreate : function()
28970 cls : 'roo-menu-item-text',
28978 cls : 'fa ' + this.icon
28987 href : this.href || '#',
28994 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28998 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29000 if(this.pos == 'left'){
29001 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29008 initEvents : function()
29010 this.el.on('mouseover', this.onMouseOver, this);
29011 this.el.on('mouseout', this.onMouseOut, this);
29013 this.el.select('a', true).first().on('click', this.onClick, this);
29017 onClick : function(e)
29019 if(this.preventDefault){
29020 e.preventDefault();
29023 this.fireEvent("click", this, e);
29026 onMouseOver : function(e)
29028 if(this.submenu && this.pos == 'left'){
29029 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29032 this.fireEvent("mouseover", this, e);
29035 onMouseOut : function(e)
29037 this.fireEvent("mouseout", this, e);
29049 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29052 * @class Roo.bootstrap.menu.Separator
29053 * @extends Roo.bootstrap.Component
29054 * Bootstrap Separator class
29057 * Create a new Separator
29058 * @param {Object} config The config object
29062 Roo.bootstrap.menu.Separator = function(config){
29063 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29066 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29068 getAutoCreate : function(){
29071 cls: 'dropdown-divider divider'
29089 * @class Roo.bootstrap.Tooltip
29090 * Bootstrap Tooltip class
29091 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29092 * to determine which dom element triggers the tooltip.
29094 * It needs to add support for additional attributes like tooltip-position
29097 * Create a new Toolti
29098 * @param {Object} config The config object
29101 Roo.bootstrap.Tooltip = function(config){
29102 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29104 this.alignment = Roo.bootstrap.Tooltip.alignment;
29106 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29107 this.alignment = config.alignment;
29112 Roo.apply(Roo.bootstrap.Tooltip, {
29114 * @function init initialize tooltip monitoring.
29118 currentTip : false,
29119 currentRegion : false,
29125 Roo.get(document).on('mouseover', this.enter ,this);
29126 Roo.get(document).on('mouseout', this.leave, this);
29129 this.currentTip = new Roo.bootstrap.Tooltip();
29132 enter : function(ev)
29134 var dom = ev.getTarget();
29136 //Roo.log(['enter',dom]);
29137 var el = Roo.fly(dom);
29138 if (this.currentEl) {
29140 //Roo.log(this.currentEl);
29141 //Roo.log(this.currentEl.contains(dom));
29142 if (this.currentEl == el) {
29145 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29151 if (this.currentTip.el) {
29152 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29156 if(!el || el.dom == document){
29162 if (!el.attr('tooltip')) {
29163 pel = el.findParent("[tooltip]");
29165 bindEl = Roo.get(pel);
29171 // you can not look for children, as if el is the body.. then everythign is the child..
29172 if (!pel && !el.attr('tooltip')) { //
29173 if (!el.select("[tooltip]").elements.length) {
29176 // is the mouse over this child...?
29177 bindEl = el.select("[tooltip]").first();
29178 var xy = ev.getXY();
29179 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29180 //Roo.log("not in region.");
29183 //Roo.log("child element over..");
29186 this.currentEl = el;
29187 this.currentTip.bind(bindEl);
29188 this.currentRegion = Roo.lib.Region.getRegion(dom);
29189 this.currentTip.enter();
29192 leave : function(ev)
29194 var dom = ev.getTarget();
29195 //Roo.log(['leave',dom]);
29196 if (!this.currentEl) {
29201 if (dom != this.currentEl.dom) {
29204 var xy = ev.getXY();
29205 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29208 // only activate leave if mouse cursor is outside... bounding box..
29213 if (this.currentTip) {
29214 this.currentTip.leave();
29216 //Roo.log('clear currentEl');
29217 this.currentEl = false;
29222 'left' : ['r-l', [-2,0], 'right'],
29223 'right' : ['l-r', [2,0], 'left'],
29224 'bottom' : ['t-b', [0,2], 'top'],
29225 'top' : [ 'b-t', [0,-2], 'bottom']
29231 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29236 delay : null, // can be { show : 300 , hide: 500}
29240 hoverState : null, //???
29242 placement : 'bottom',
29246 getAutoCreate : function(){
29253 cls : 'tooltip-arrow arrow'
29256 cls : 'tooltip-inner'
29263 bind : function(el)
29268 initEvents : function()
29270 this.arrowEl = this.el.select('.arrow', true).first();
29271 this.innerEl = this.el.select('.tooltip-inner', true).first();
29274 enter : function () {
29276 if (this.timeout != null) {
29277 clearTimeout(this.timeout);
29280 this.hoverState = 'in';
29281 //Roo.log("enter - show");
29282 if (!this.delay || !this.delay.show) {
29287 this.timeout = setTimeout(function () {
29288 if (_t.hoverState == 'in') {
29291 }, this.delay.show);
29295 clearTimeout(this.timeout);
29297 this.hoverState = 'out';
29298 if (!this.delay || !this.delay.hide) {
29304 this.timeout = setTimeout(function () {
29305 //Roo.log("leave - timeout");
29307 if (_t.hoverState == 'out') {
29309 Roo.bootstrap.Tooltip.currentEl = false;
29314 show : function (msg)
29317 this.render(document.body);
29320 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29322 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29324 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29326 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29327 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29329 var placement = typeof this.placement == 'function' ?
29330 this.placement.call(this, this.el, on_el) :
29333 var autoToken = /\s?auto?\s?/i;
29334 var autoPlace = autoToken.test(placement);
29336 placement = placement.replace(autoToken, '') || 'top';
29340 //this.el.setXY([0,0]);
29342 //this.el.dom.style.display='block';
29344 //this.el.appendTo(on_el);
29346 var p = this.getPosition();
29347 var box = this.el.getBox();
29353 var align = this.alignment[placement];
29355 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29357 if(placement == 'top' || placement == 'bottom'){
29359 placement = 'right';
29362 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29363 placement = 'left';
29366 var scroll = Roo.select('body', true).first().getScroll();
29368 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29372 align = this.alignment[placement];
29374 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29378 var elems = document.getElementsByTagName('div');
29379 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29380 for (var i = 0; i < elems.length; i++) {
29381 var zindex = Number.parseInt(
29382 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29385 if (zindex > highest) {
29392 this.el.dom.style.zIndex = highest;
29394 this.el.alignTo(this.bindEl, align[0],align[1]);
29395 //var arrow = this.el.select('.arrow',true).first();
29396 //arrow.set(align[2],
29398 this.el.addClass(placement);
29399 this.el.addClass("bs-tooltip-"+ placement);
29401 this.el.addClass('in fade show');
29403 this.hoverState = null;
29405 if (this.el.hasClass('fade')) {
29420 //this.el.setXY([0,0]);
29421 this.el.removeClass(['show', 'in']);
29437 * @class Roo.bootstrap.LocationPicker
29438 * @extends Roo.bootstrap.Component
29439 * Bootstrap LocationPicker class
29440 * @cfg {Number} latitude Position when init default 0
29441 * @cfg {Number} longitude Position when init default 0
29442 * @cfg {Number} zoom default 15
29443 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29444 * @cfg {Boolean} mapTypeControl default false
29445 * @cfg {Boolean} disableDoubleClickZoom default false
29446 * @cfg {Boolean} scrollwheel default true
29447 * @cfg {Boolean} streetViewControl default false
29448 * @cfg {Number} radius default 0
29449 * @cfg {String} locationName
29450 * @cfg {Boolean} draggable default true
29451 * @cfg {Boolean} enableAutocomplete default false
29452 * @cfg {Boolean} enableReverseGeocode default true
29453 * @cfg {String} markerTitle
29456 * Create a new LocationPicker
29457 * @param {Object} config The config object
29461 Roo.bootstrap.LocationPicker = function(config){
29463 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29468 * Fires when the picker initialized.
29469 * @param {Roo.bootstrap.LocationPicker} this
29470 * @param {Google Location} location
29474 * @event positionchanged
29475 * Fires when the picker position changed.
29476 * @param {Roo.bootstrap.LocationPicker} this
29477 * @param {Google Location} location
29479 positionchanged : true,
29482 * Fires when the map resize.
29483 * @param {Roo.bootstrap.LocationPicker} this
29488 * Fires when the map show.
29489 * @param {Roo.bootstrap.LocationPicker} this
29494 * Fires when the map hide.
29495 * @param {Roo.bootstrap.LocationPicker} this
29500 * Fires when click the map.
29501 * @param {Roo.bootstrap.LocationPicker} this
29502 * @param {Map event} e
29506 * @event mapRightClick
29507 * Fires when right click the map.
29508 * @param {Roo.bootstrap.LocationPicker} this
29509 * @param {Map event} e
29511 mapRightClick : true,
29513 * @event markerClick
29514 * Fires when click the marker.
29515 * @param {Roo.bootstrap.LocationPicker} this
29516 * @param {Map event} e
29518 markerClick : true,
29520 * @event markerRightClick
29521 * Fires when right click the marker.
29522 * @param {Roo.bootstrap.LocationPicker} this
29523 * @param {Map event} e
29525 markerRightClick : true,
29527 * @event OverlayViewDraw
29528 * Fires when OverlayView Draw
29529 * @param {Roo.bootstrap.LocationPicker} this
29531 OverlayViewDraw : true,
29533 * @event OverlayViewOnAdd
29534 * Fires when OverlayView Draw
29535 * @param {Roo.bootstrap.LocationPicker} this
29537 OverlayViewOnAdd : true,
29539 * @event OverlayViewOnRemove
29540 * Fires when OverlayView Draw
29541 * @param {Roo.bootstrap.LocationPicker} this
29543 OverlayViewOnRemove : true,
29545 * @event OverlayViewShow
29546 * Fires when OverlayView Draw
29547 * @param {Roo.bootstrap.LocationPicker} this
29548 * @param {Pixel} cpx
29550 OverlayViewShow : true,
29552 * @event OverlayViewHide
29553 * Fires when OverlayView Draw
29554 * @param {Roo.bootstrap.LocationPicker} this
29556 OverlayViewHide : true,
29558 * @event loadexception
29559 * Fires when load google lib failed.
29560 * @param {Roo.bootstrap.LocationPicker} this
29562 loadexception : true
29567 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29569 gMapContext: false,
29575 mapTypeControl: false,
29576 disableDoubleClickZoom: false,
29578 streetViewControl: false,
29582 enableAutocomplete: false,
29583 enableReverseGeocode: true,
29586 getAutoCreate: function()
29591 cls: 'roo-location-picker'
29597 initEvents: function(ct, position)
29599 if(!this.el.getWidth() || this.isApplied()){
29603 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29608 initial: function()
29610 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29611 this.fireEvent('loadexception', this);
29615 if(!this.mapTypeId){
29616 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29619 this.gMapContext = this.GMapContext();
29621 this.initOverlayView();
29623 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29627 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29628 _this.setPosition(_this.gMapContext.marker.position);
29631 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29632 _this.fireEvent('mapClick', this, event);
29636 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29637 _this.fireEvent('mapRightClick', this, event);
29641 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29642 _this.fireEvent('markerClick', this, event);
29646 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29647 _this.fireEvent('markerRightClick', this, event);
29651 this.setPosition(this.gMapContext.location);
29653 this.fireEvent('initial', this, this.gMapContext.location);
29656 initOverlayView: function()
29660 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29664 _this.fireEvent('OverlayViewDraw', _this);
29669 _this.fireEvent('OverlayViewOnAdd', _this);
29672 onRemove: function()
29674 _this.fireEvent('OverlayViewOnRemove', _this);
29677 show: function(cpx)
29679 _this.fireEvent('OverlayViewShow', _this, cpx);
29684 _this.fireEvent('OverlayViewHide', _this);
29690 fromLatLngToContainerPixel: function(event)
29692 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29695 isApplied: function()
29697 return this.getGmapContext() == false ? false : true;
29700 getGmapContext: function()
29702 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29705 GMapContext: function()
29707 var position = new google.maps.LatLng(this.latitude, this.longitude);
29709 var _map = new google.maps.Map(this.el.dom, {
29712 mapTypeId: this.mapTypeId,
29713 mapTypeControl: this.mapTypeControl,
29714 disableDoubleClickZoom: this.disableDoubleClickZoom,
29715 scrollwheel: this.scrollwheel,
29716 streetViewControl: this.streetViewControl,
29717 locationName: this.locationName,
29718 draggable: this.draggable,
29719 enableAutocomplete: this.enableAutocomplete,
29720 enableReverseGeocode: this.enableReverseGeocode
29723 var _marker = new google.maps.Marker({
29724 position: position,
29726 title: this.markerTitle,
29727 draggable: this.draggable
29734 location: position,
29735 radius: this.radius,
29736 locationName: this.locationName,
29737 addressComponents: {
29738 formatted_address: null,
29739 addressLine1: null,
29740 addressLine2: null,
29742 streetNumber: null,
29746 stateOrProvince: null
29749 domContainer: this.el.dom,
29750 geodecoder: new google.maps.Geocoder()
29754 drawCircle: function(center, radius, options)
29756 if (this.gMapContext.circle != null) {
29757 this.gMapContext.circle.setMap(null);
29761 options = Roo.apply({}, options, {
29762 strokeColor: "#0000FF",
29763 strokeOpacity: .35,
29765 fillColor: "#0000FF",
29769 options.map = this.gMapContext.map;
29770 options.radius = radius;
29771 options.center = center;
29772 this.gMapContext.circle = new google.maps.Circle(options);
29773 return this.gMapContext.circle;
29779 setPosition: function(location)
29781 this.gMapContext.location = location;
29782 this.gMapContext.marker.setPosition(location);
29783 this.gMapContext.map.panTo(location);
29784 this.drawCircle(location, this.gMapContext.radius, {});
29788 if (this.gMapContext.settings.enableReverseGeocode) {
29789 this.gMapContext.geodecoder.geocode({
29790 latLng: this.gMapContext.location
29791 }, function(results, status) {
29793 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29794 _this.gMapContext.locationName = results[0].formatted_address;
29795 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29797 _this.fireEvent('positionchanged', this, location);
29804 this.fireEvent('positionchanged', this, location);
29809 google.maps.event.trigger(this.gMapContext.map, "resize");
29811 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29813 this.fireEvent('resize', this);
29816 setPositionByLatLng: function(latitude, longitude)
29818 this.setPosition(new google.maps.LatLng(latitude, longitude));
29821 getCurrentPosition: function()
29824 latitude: this.gMapContext.location.lat(),
29825 longitude: this.gMapContext.location.lng()
29829 getAddressName: function()
29831 return this.gMapContext.locationName;
29834 getAddressComponents: function()
29836 return this.gMapContext.addressComponents;
29839 address_component_from_google_geocode: function(address_components)
29843 for (var i = 0; i < address_components.length; i++) {
29844 var component = address_components[i];
29845 if (component.types.indexOf("postal_code") >= 0) {
29846 result.postalCode = component.short_name;
29847 } else if (component.types.indexOf("street_number") >= 0) {
29848 result.streetNumber = component.short_name;
29849 } else if (component.types.indexOf("route") >= 0) {
29850 result.streetName = component.short_name;
29851 } else if (component.types.indexOf("neighborhood") >= 0) {
29852 result.city = component.short_name;
29853 } else if (component.types.indexOf("locality") >= 0) {
29854 result.city = component.short_name;
29855 } else if (component.types.indexOf("sublocality") >= 0) {
29856 result.district = component.short_name;
29857 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29858 result.stateOrProvince = component.short_name;
29859 } else if (component.types.indexOf("country") >= 0) {
29860 result.country = component.short_name;
29864 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29865 result.addressLine2 = "";
29869 setZoomLevel: function(zoom)
29871 this.gMapContext.map.setZoom(zoom);
29884 this.fireEvent('show', this);
29895 this.fireEvent('hide', this);
29900 Roo.apply(Roo.bootstrap.LocationPicker, {
29902 OverlayView : function(map, options)
29904 options = options || {};
29911 * @class Roo.bootstrap.Alert
29912 * @extends Roo.bootstrap.Component
29913 * Bootstrap Alert class - shows an alert area box
29915 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29916 Enter a valid email address
29919 * @cfg {String} title The title of alert
29920 * @cfg {String} html The content of alert
29921 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29922 * @cfg {String} fa font-awesomeicon
29923 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29924 * @cfg {Boolean} close true to show a x closer
29928 * Create a new alert
29929 * @param {Object} config The config object
29933 Roo.bootstrap.Alert = function(config){
29934 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29938 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29944 faicon: false, // BC
29948 getAutoCreate : function()
29960 style : this.close ? '' : 'display:none'
29964 cls : 'roo-alert-icon'
29969 cls : 'roo-alert-title',
29974 cls : 'roo-alert-text',
29981 cfg.cn[0].cls += ' fa ' + this.faicon;
29984 cfg.cn[0].cls += ' fa ' + this.fa;
29988 cfg.cls += ' alert-' + this.weight;
29994 initEvents: function()
29996 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29997 this.titleEl = this.el.select('.roo-alert-title',true).first();
29998 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29999 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30000 if (this.seconds > 0) {
30001 this.hide.defer(this.seconds, this);
30005 * Set the Title Message HTML
30006 * @param {String} html
30008 setTitle : function(str)
30010 this.titleEl.dom.innerHTML = str;
30014 * Set the Body Message HTML
30015 * @param {String} html
30017 setHtml : function(str)
30019 this.htmlEl.dom.innerHTML = str;
30022 * Set the Weight of the alert
30023 * @param {String} (success|info|warning|danger) weight
30026 setWeight : function(weight)
30029 this.el.removeClass('alert-' + this.weight);
30032 this.weight = weight;
30034 this.el.addClass('alert-' + this.weight);
30037 * Set the Icon of the alert
30038 * @param {String} see fontawsome names (name without the 'fa-' bit)
30040 setIcon : function(icon)
30043 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30046 this.faicon = icon;
30048 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30073 * @class Roo.bootstrap.UploadCropbox
30074 * @extends Roo.bootstrap.Component
30075 * Bootstrap UploadCropbox class
30076 * @cfg {String} emptyText show when image has been loaded
30077 * @cfg {String} rotateNotify show when image too small to rotate
30078 * @cfg {Number} errorTimeout default 3000
30079 * @cfg {Number} minWidth default 300
30080 * @cfg {Number} minHeight default 300
30081 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30082 * @cfg {Boolean} isDocument (true|false) default false
30083 * @cfg {String} url action url
30084 * @cfg {String} paramName default 'imageUpload'
30085 * @cfg {String} method default POST
30086 * @cfg {Boolean} loadMask (true|false) default true
30087 * @cfg {Boolean} loadingText default 'Loading...'
30090 * Create a new UploadCropbox
30091 * @param {Object} config The config object
30094 Roo.bootstrap.UploadCropbox = function(config){
30095 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30099 * @event beforeselectfile
30100 * Fire before select file
30101 * @param {Roo.bootstrap.UploadCropbox} this
30103 "beforeselectfile" : true,
30106 * Fire after initEvent
30107 * @param {Roo.bootstrap.UploadCropbox} this
30112 * Fire after initEvent
30113 * @param {Roo.bootstrap.UploadCropbox} this
30114 * @param {String} data
30119 * Fire when preparing the file data
30120 * @param {Roo.bootstrap.UploadCropbox} this
30121 * @param {Object} file
30126 * Fire when get exception
30127 * @param {Roo.bootstrap.UploadCropbox} this
30128 * @param {XMLHttpRequest} xhr
30130 "exception" : true,
30132 * @event beforeloadcanvas
30133 * Fire before load the canvas
30134 * @param {Roo.bootstrap.UploadCropbox} this
30135 * @param {String} src
30137 "beforeloadcanvas" : true,
30140 * Fire when trash image
30141 * @param {Roo.bootstrap.UploadCropbox} this
30146 * Fire when download the image
30147 * @param {Roo.bootstrap.UploadCropbox} this
30151 * @event footerbuttonclick
30152 * Fire when footerbuttonclick
30153 * @param {Roo.bootstrap.UploadCropbox} this
30154 * @param {String} type
30156 "footerbuttonclick" : true,
30160 * @param {Roo.bootstrap.UploadCropbox} this
30165 * Fire when rotate the image
30166 * @param {Roo.bootstrap.UploadCropbox} this
30167 * @param {String} pos
30172 * Fire when inspect the file
30173 * @param {Roo.bootstrap.UploadCropbox} this
30174 * @param {Object} file
30179 * Fire when xhr upload the file
30180 * @param {Roo.bootstrap.UploadCropbox} this
30181 * @param {Object} data
30186 * Fire when arrange the file data
30187 * @param {Roo.bootstrap.UploadCropbox} this
30188 * @param {Object} formData
30193 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30196 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30198 emptyText : 'Click to upload image',
30199 rotateNotify : 'Image is too small to rotate',
30200 errorTimeout : 3000,
30214 cropType : 'image/jpeg',
30216 canvasLoaded : false,
30217 isDocument : false,
30219 paramName : 'imageUpload',
30221 loadingText : 'Loading...',
30224 getAutoCreate : function()
30228 cls : 'roo-upload-cropbox',
30232 cls : 'roo-upload-cropbox-selector',
30237 cls : 'roo-upload-cropbox-body',
30238 style : 'cursor:pointer',
30242 cls : 'roo-upload-cropbox-preview'
30246 cls : 'roo-upload-cropbox-thumb'
30250 cls : 'roo-upload-cropbox-empty-notify',
30251 html : this.emptyText
30255 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30256 html : this.rotateNotify
30262 cls : 'roo-upload-cropbox-footer',
30265 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30275 onRender : function(ct, position)
30277 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30279 if (this.buttons.length) {
30281 Roo.each(this.buttons, function(bb) {
30283 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30285 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30291 this.maskEl = this.el;
30295 initEvents : function()
30297 this.urlAPI = (window.createObjectURL && window) ||
30298 (window.URL && URL.revokeObjectURL && URL) ||
30299 (window.webkitURL && webkitURL);
30301 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30302 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30304 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30305 this.selectorEl.hide();
30307 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30308 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30310 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30311 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30312 this.thumbEl.hide();
30314 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30315 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30317 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30318 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30319 this.errorEl.hide();
30321 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30322 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30323 this.footerEl.hide();
30325 this.setThumbBoxSize();
30331 this.fireEvent('initial', this);
30338 window.addEventListener("resize", function() { _this.resize(); } );
30340 this.bodyEl.on('click', this.beforeSelectFile, this);
30343 this.bodyEl.on('touchstart', this.onTouchStart, this);
30344 this.bodyEl.on('touchmove', this.onTouchMove, this);
30345 this.bodyEl.on('touchend', this.onTouchEnd, this);
30349 this.bodyEl.on('mousedown', this.onMouseDown, this);
30350 this.bodyEl.on('mousemove', this.onMouseMove, this);
30351 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30352 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30353 Roo.get(document).on('mouseup', this.onMouseUp, this);
30356 this.selectorEl.on('change', this.onFileSelected, this);
30362 this.baseScale = 1;
30364 this.baseRotate = 1;
30365 this.dragable = false;
30366 this.pinching = false;
30369 this.cropData = false;
30370 this.notifyEl.dom.innerHTML = this.emptyText;
30372 this.selectorEl.dom.value = '';
30376 resize : function()
30378 if(this.fireEvent('resize', this) != false){
30379 this.setThumbBoxPosition();
30380 this.setCanvasPosition();
30384 onFooterButtonClick : function(e, el, o, type)
30387 case 'rotate-left' :
30388 this.onRotateLeft(e);
30390 case 'rotate-right' :
30391 this.onRotateRight(e);
30394 this.beforeSelectFile(e);
30409 this.fireEvent('footerbuttonclick', this, type);
30412 beforeSelectFile : function(e)
30414 e.preventDefault();
30416 if(this.fireEvent('beforeselectfile', this) != false){
30417 this.selectorEl.dom.click();
30421 onFileSelected : function(e)
30423 e.preventDefault();
30425 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30429 var file = this.selectorEl.dom.files[0];
30431 if(this.fireEvent('inspect', this, file) != false){
30432 this.prepare(file);
30437 trash : function(e)
30439 this.fireEvent('trash', this);
30442 download : function(e)
30444 this.fireEvent('download', this);
30447 loadCanvas : function(src)
30449 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30453 this.imageEl = document.createElement('img');
30457 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30459 this.imageEl.src = src;
30463 onLoadCanvas : function()
30465 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30466 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30468 this.bodyEl.un('click', this.beforeSelectFile, this);
30470 this.notifyEl.hide();
30471 this.thumbEl.show();
30472 this.footerEl.show();
30474 this.baseRotateLevel();
30476 if(this.isDocument){
30477 this.setThumbBoxSize();
30480 this.setThumbBoxPosition();
30482 this.baseScaleLevel();
30488 this.canvasLoaded = true;
30491 this.maskEl.unmask();
30496 setCanvasPosition : function()
30498 if(!this.canvasEl){
30502 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30503 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30505 this.previewEl.setLeft(pw);
30506 this.previewEl.setTop(ph);
30510 onMouseDown : function(e)
30514 this.dragable = true;
30515 this.pinching = false;
30517 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30518 this.dragable = false;
30522 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30523 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30527 onMouseMove : function(e)
30531 if(!this.canvasLoaded){
30535 if (!this.dragable){
30539 var minX = Math.ceil(this.thumbEl.getLeft(true));
30540 var minY = Math.ceil(this.thumbEl.getTop(true));
30542 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30543 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30545 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30546 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30548 x = x - this.mouseX;
30549 y = y - this.mouseY;
30551 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30552 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30554 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30555 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30557 this.previewEl.setLeft(bgX);
30558 this.previewEl.setTop(bgY);
30560 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30561 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30564 onMouseUp : function(e)
30568 this.dragable = false;
30571 onMouseWheel : function(e)
30575 this.startScale = this.scale;
30577 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30579 if(!this.zoomable()){
30580 this.scale = this.startScale;
30589 zoomable : function()
30591 var minScale = this.thumbEl.getWidth() / this.minWidth;
30593 if(this.minWidth < this.minHeight){
30594 minScale = this.thumbEl.getHeight() / this.minHeight;
30597 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30598 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30602 (this.rotate == 0 || this.rotate == 180) &&
30604 width > this.imageEl.OriginWidth ||
30605 height > this.imageEl.OriginHeight ||
30606 (width < this.minWidth && height < this.minHeight)
30614 (this.rotate == 90 || this.rotate == 270) &&
30616 width > this.imageEl.OriginWidth ||
30617 height > this.imageEl.OriginHeight ||
30618 (width < this.minHeight && height < this.minWidth)
30625 !this.isDocument &&
30626 (this.rotate == 0 || this.rotate == 180) &&
30628 width < this.minWidth ||
30629 width > this.imageEl.OriginWidth ||
30630 height < this.minHeight ||
30631 height > this.imageEl.OriginHeight
30638 !this.isDocument &&
30639 (this.rotate == 90 || this.rotate == 270) &&
30641 width < this.minHeight ||
30642 width > this.imageEl.OriginWidth ||
30643 height < this.minWidth ||
30644 height > this.imageEl.OriginHeight
30654 onRotateLeft : function(e)
30656 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30658 var minScale = this.thumbEl.getWidth() / this.minWidth;
30660 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30661 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30663 this.startScale = this.scale;
30665 while (this.getScaleLevel() < minScale){
30667 this.scale = this.scale + 1;
30669 if(!this.zoomable()){
30674 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30675 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30680 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30687 this.scale = this.startScale;
30689 this.onRotateFail();
30694 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30696 if(this.isDocument){
30697 this.setThumbBoxSize();
30698 this.setThumbBoxPosition();
30699 this.setCanvasPosition();
30704 this.fireEvent('rotate', this, 'left');
30708 onRotateRight : function(e)
30710 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30712 var minScale = this.thumbEl.getWidth() / this.minWidth;
30714 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30715 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30717 this.startScale = this.scale;
30719 while (this.getScaleLevel() < minScale){
30721 this.scale = this.scale + 1;
30723 if(!this.zoomable()){
30728 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30729 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30734 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30741 this.scale = this.startScale;
30743 this.onRotateFail();
30748 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30750 if(this.isDocument){
30751 this.setThumbBoxSize();
30752 this.setThumbBoxPosition();
30753 this.setCanvasPosition();
30758 this.fireEvent('rotate', this, 'right');
30761 onRotateFail : function()
30763 this.errorEl.show(true);
30767 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30772 this.previewEl.dom.innerHTML = '';
30774 var canvasEl = document.createElement("canvas");
30776 var contextEl = canvasEl.getContext("2d");
30778 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30779 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30780 var center = this.imageEl.OriginWidth / 2;
30782 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30783 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30784 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30785 center = this.imageEl.OriginHeight / 2;
30788 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30790 contextEl.translate(center, center);
30791 contextEl.rotate(this.rotate * Math.PI / 180);
30793 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30795 this.canvasEl = document.createElement("canvas");
30797 this.contextEl = this.canvasEl.getContext("2d");
30799 switch (this.rotate) {
30802 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30803 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30805 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30810 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30811 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30813 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30814 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);
30818 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30823 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30824 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30826 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30827 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);
30831 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);
30836 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30837 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30839 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30840 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30844 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);
30851 this.previewEl.appendChild(this.canvasEl);
30853 this.setCanvasPosition();
30858 if(!this.canvasLoaded){
30862 var imageCanvas = document.createElement("canvas");
30864 var imageContext = imageCanvas.getContext("2d");
30866 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30867 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30869 var center = imageCanvas.width / 2;
30871 imageContext.translate(center, center);
30873 imageContext.rotate(this.rotate * Math.PI / 180);
30875 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30877 var canvas = document.createElement("canvas");
30879 var context = canvas.getContext("2d");
30881 canvas.width = this.minWidth;
30882 canvas.height = this.minHeight;
30884 switch (this.rotate) {
30887 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30888 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30890 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30891 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30893 var targetWidth = this.minWidth - 2 * x;
30894 var targetHeight = this.minHeight - 2 * y;
30898 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30899 scale = targetWidth / width;
30902 if(x > 0 && y == 0){
30903 scale = targetHeight / height;
30906 if(x > 0 && y > 0){
30907 scale = targetWidth / width;
30909 if(width < height){
30910 scale = targetHeight / height;
30914 context.scale(scale, scale);
30916 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30917 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30919 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30920 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30922 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30927 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30928 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30930 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30931 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30933 var targetWidth = this.minWidth - 2 * x;
30934 var targetHeight = this.minHeight - 2 * y;
30938 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30939 scale = targetWidth / width;
30942 if(x > 0 && y == 0){
30943 scale = targetHeight / height;
30946 if(x > 0 && y > 0){
30947 scale = targetWidth / width;
30949 if(width < height){
30950 scale = targetHeight / height;
30954 context.scale(scale, scale);
30956 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30957 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30959 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30960 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30962 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30964 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30969 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30970 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30972 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30973 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30975 var targetWidth = this.minWidth - 2 * x;
30976 var targetHeight = this.minHeight - 2 * y;
30980 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30981 scale = targetWidth / width;
30984 if(x > 0 && y == 0){
30985 scale = targetHeight / height;
30988 if(x > 0 && y > 0){
30989 scale = targetWidth / width;
30991 if(width < height){
30992 scale = targetHeight / height;
30996 context.scale(scale, scale);
30998 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30999 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31001 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31002 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31004 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31005 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31007 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31012 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31013 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31015 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31016 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31018 var targetWidth = this.minWidth - 2 * x;
31019 var targetHeight = this.minHeight - 2 * y;
31023 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31024 scale = targetWidth / width;
31027 if(x > 0 && y == 0){
31028 scale = targetHeight / height;
31031 if(x > 0 && y > 0){
31032 scale = targetWidth / width;
31034 if(width < height){
31035 scale = targetHeight / height;
31039 context.scale(scale, scale);
31041 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31042 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31044 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31045 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31047 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31049 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31056 this.cropData = canvas.toDataURL(this.cropType);
31058 if(this.fireEvent('crop', this, this.cropData) !== false){
31059 this.process(this.file, this.cropData);
31066 setThumbBoxSize : function()
31070 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31071 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31072 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31074 this.minWidth = width;
31075 this.minHeight = height;
31077 if(this.rotate == 90 || this.rotate == 270){
31078 this.minWidth = height;
31079 this.minHeight = width;
31084 width = Math.ceil(this.minWidth * height / this.minHeight);
31086 if(this.minWidth > this.minHeight){
31088 height = Math.ceil(this.minHeight * width / this.minWidth);
31091 this.thumbEl.setStyle({
31092 width : width + 'px',
31093 height : height + 'px'
31100 setThumbBoxPosition : function()
31102 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31103 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31105 this.thumbEl.setLeft(x);
31106 this.thumbEl.setTop(y);
31110 baseRotateLevel : function()
31112 this.baseRotate = 1;
31115 typeof(this.exif) != 'undefined' &&
31116 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31117 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31119 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31122 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31126 baseScaleLevel : function()
31130 if(this.isDocument){
31132 if(this.baseRotate == 6 || this.baseRotate == 8){
31134 height = this.thumbEl.getHeight();
31135 this.baseScale = height / this.imageEl.OriginWidth;
31137 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31138 width = this.thumbEl.getWidth();
31139 this.baseScale = width / this.imageEl.OriginHeight;
31145 height = this.thumbEl.getHeight();
31146 this.baseScale = height / this.imageEl.OriginHeight;
31148 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31149 width = this.thumbEl.getWidth();
31150 this.baseScale = width / this.imageEl.OriginWidth;
31156 if(this.baseRotate == 6 || this.baseRotate == 8){
31158 width = this.thumbEl.getHeight();
31159 this.baseScale = width / this.imageEl.OriginHeight;
31161 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31162 height = this.thumbEl.getWidth();
31163 this.baseScale = height / this.imageEl.OriginHeight;
31166 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31167 height = this.thumbEl.getWidth();
31168 this.baseScale = height / this.imageEl.OriginHeight;
31170 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31171 width = this.thumbEl.getHeight();
31172 this.baseScale = width / this.imageEl.OriginWidth;
31179 width = this.thumbEl.getWidth();
31180 this.baseScale = width / this.imageEl.OriginWidth;
31182 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31183 height = this.thumbEl.getHeight();
31184 this.baseScale = height / this.imageEl.OriginHeight;
31187 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31189 height = this.thumbEl.getHeight();
31190 this.baseScale = height / this.imageEl.OriginHeight;
31192 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31193 width = this.thumbEl.getWidth();
31194 this.baseScale = width / this.imageEl.OriginWidth;
31202 getScaleLevel : function()
31204 return this.baseScale * Math.pow(1.1, this.scale);
31207 onTouchStart : function(e)
31209 if(!this.canvasLoaded){
31210 this.beforeSelectFile(e);
31214 var touches = e.browserEvent.touches;
31220 if(touches.length == 1){
31221 this.onMouseDown(e);
31225 if(touches.length != 2){
31231 for(var i = 0, finger; finger = touches[i]; i++){
31232 coords.push(finger.pageX, finger.pageY);
31235 var x = Math.pow(coords[0] - coords[2], 2);
31236 var y = Math.pow(coords[1] - coords[3], 2);
31238 this.startDistance = Math.sqrt(x + y);
31240 this.startScale = this.scale;
31242 this.pinching = true;
31243 this.dragable = false;
31247 onTouchMove : function(e)
31249 if(!this.pinching && !this.dragable){
31253 var touches = e.browserEvent.touches;
31260 this.onMouseMove(e);
31266 for(var i = 0, finger; finger = touches[i]; i++){
31267 coords.push(finger.pageX, finger.pageY);
31270 var x = Math.pow(coords[0] - coords[2], 2);
31271 var y = Math.pow(coords[1] - coords[3], 2);
31273 this.endDistance = Math.sqrt(x + y);
31275 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31277 if(!this.zoomable()){
31278 this.scale = this.startScale;
31286 onTouchEnd : function(e)
31288 this.pinching = false;
31289 this.dragable = false;
31293 process : function(file, crop)
31296 this.maskEl.mask(this.loadingText);
31299 this.xhr = new XMLHttpRequest();
31301 file.xhr = this.xhr;
31303 this.xhr.open(this.method, this.url, true);
31306 "Accept": "application/json",
31307 "Cache-Control": "no-cache",
31308 "X-Requested-With": "XMLHttpRequest"
31311 for (var headerName in headers) {
31312 var headerValue = headers[headerName];
31314 this.xhr.setRequestHeader(headerName, headerValue);
31320 this.xhr.onload = function()
31322 _this.xhrOnLoad(_this.xhr);
31325 this.xhr.onerror = function()
31327 _this.xhrOnError(_this.xhr);
31330 var formData = new FormData();
31332 formData.append('returnHTML', 'NO');
31335 formData.append('crop', crop);
31338 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31339 formData.append(this.paramName, file, file.name);
31342 if(typeof(file.filename) != 'undefined'){
31343 formData.append('filename', file.filename);
31346 if(typeof(file.mimetype) != 'undefined'){
31347 formData.append('mimetype', file.mimetype);
31350 if(this.fireEvent('arrange', this, formData) != false){
31351 this.xhr.send(formData);
31355 xhrOnLoad : function(xhr)
31358 this.maskEl.unmask();
31361 if (xhr.readyState !== 4) {
31362 this.fireEvent('exception', this, xhr);
31366 var response = Roo.decode(xhr.responseText);
31368 if(!response.success){
31369 this.fireEvent('exception', this, xhr);
31373 var response = Roo.decode(xhr.responseText);
31375 this.fireEvent('upload', this, response);
31379 xhrOnError : function()
31382 this.maskEl.unmask();
31385 Roo.log('xhr on error');
31387 var response = Roo.decode(xhr.responseText);
31393 prepare : function(file)
31396 this.maskEl.mask(this.loadingText);
31402 if(typeof(file) === 'string'){
31403 this.loadCanvas(file);
31407 if(!file || !this.urlAPI){
31412 this.cropType = file.type;
31416 if(this.fireEvent('prepare', this, this.file) != false){
31418 var reader = new FileReader();
31420 reader.onload = function (e) {
31421 if (e.target.error) {
31422 Roo.log(e.target.error);
31426 var buffer = e.target.result,
31427 dataView = new DataView(buffer),
31429 maxOffset = dataView.byteLength - 4,
31433 if (dataView.getUint16(0) === 0xffd8) {
31434 while (offset < maxOffset) {
31435 markerBytes = dataView.getUint16(offset);
31437 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31438 markerLength = dataView.getUint16(offset + 2) + 2;
31439 if (offset + markerLength > dataView.byteLength) {
31440 Roo.log('Invalid meta data: Invalid segment size.');
31444 if(markerBytes == 0xffe1){
31445 _this.parseExifData(
31452 offset += markerLength;
31462 var url = _this.urlAPI.createObjectURL(_this.file);
31464 _this.loadCanvas(url);
31469 reader.readAsArrayBuffer(this.file);
31475 parseExifData : function(dataView, offset, length)
31477 var tiffOffset = offset + 10,
31481 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31482 // No Exif data, might be XMP data instead
31486 // Check for the ASCII code for "Exif" (0x45786966):
31487 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31488 // No Exif data, might be XMP data instead
31491 if (tiffOffset + 8 > dataView.byteLength) {
31492 Roo.log('Invalid Exif data: Invalid segment size.');
31495 // Check for the two null bytes:
31496 if (dataView.getUint16(offset + 8) !== 0x0000) {
31497 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31500 // Check the byte alignment:
31501 switch (dataView.getUint16(tiffOffset)) {
31503 littleEndian = true;
31506 littleEndian = false;
31509 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31512 // Check for the TIFF tag marker (0x002A):
31513 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31514 Roo.log('Invalid Exif data: Missing TIFF marker.');
31517 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31518 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31520 this.parseExifTags(
31523 tiffOffset + dirOffset,
31528 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31533 if (dirOffset + 6 > dataView.byteLength) {
31534 Roo.log('Invalid Exif data: Invalid directory offset.');
31537 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31538 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31539 if (dirEndOffset + 4 > dataView.byteLength) {
31540 Roo.log('Invalid Exif data: Invalid directory size.');
31543 for (i = 0; i < tagsNumber; i += 1) {
31547 dirOffset + 2 + 12 * i, // tag offset
31551 // Return the offset to the next directory:
31552 return dataView.getUint32(dirEndOffset, littleEndian);
31555 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31557 var tag = dataView.getUint16(offset, littleEndian);
31559 this.exif[tag] = this.getExifValue(
31563 dataView.getUint16(offset + 2, littleEndian), // tag type
31564 dataView.getUint32(offset + 4, littleEndian), // tag length
31569 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31571 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31580 Roo.log('Invalid Exif data: Invalid tag type.');
31584 tagSize = tagType.size * length;
31585 // Determine if the value is contained in the dataOffset bytes,
31586 // or if the value at the dataOffset is a pointer to the actual data:
31587 dataOffset = tagSize > 4 ?
31588 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31589 if (dataOffset + tagSize > dataView.byteLength) {
31590 Roo.log('Invalid Exif data: Invalid data offset.');
31593 if (length === 1) {
31594 return tagType.getValue(dataView, dataOffset, littleEndian);
31597 for (i = 0; i < length; i += 1) {
31598 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31601 if (tagType.ascii) {
31603 // Concatenate the chars:
31604 for (i = 0; i < values.length; i += 1) {
31606 // Ignore the terminating NULL byte(s):
31607 if (c === '\u0000') {
31619 Roo.apply(Roo.bootstrap.UploadCropbox, {
31621 'Orientation': 0x0112
31625 1: 0, //'top-left',
31627 3: 180, //'bottom-right',
31628 // 4: 'bottom-left',
31630 6: 90, //'right-top',
31631 // 7: 'right-bottom',
31632 8: 270 //'left-bottom'
31636 // byte, 8-bit unsigned int:
31638 getValue: function (dataView, dataOffset) {
31639 return dataView.getUint8(dataOffset);
31643 // ascii, 8-bit byte:
31645 getValue: function (dataView, dataOffset) {
31646 return String.fromCharCode(dataView.getUint8(dataOffset));
31651 // short, 16 bit int:
31653 getValue: function (dataView, dataOffset, littleEndian) {
31654 return dataView.getUint16(dataOffset, littleEndian);
31658 // long, 32 bit int:
31660 getValue: function (dataView, dataOffset, littleEndian) {
31661 return dataView.getUint32(dataOffset, littleEndian);
31665 // rational = two long values, first is numerator, second is denominator:
31667 getValue: function (dataView, dataOffset, littleEndian) {
31668 return dataView.getUint32(dataOffset, littleEndian) /
31669 dataView.getUint32(dataOffset + 4, littleEndian);
31673 // slong, 32 bit signed int:
31675 getValue: function (dataView, dataOffset, littleEndian) {
31676 return dataView.getInt32(dataOffset, littleEndian);
31680 // srational, two slongs, first is numerator, second is denominator:
31682 getValue: function (dataView, dataOffset, littleEndian) {
31683 return dataView.getInt32(dataOffset, littleEndian) /
31684 dataView.getInt32(dataOffset + 4, littleEndian);
31694 cls : 'btn-group roo-upload-cropbox-rotate-left',
31695 action : 'rotate-left',
31699 cls : 'btn btn-default',
31700 html : '<i class="fa fa-undo"></i>'
31706 cls : 'btn-group roo-upload-cropbox-picture',
31707 action : 'picture',
31711 cls : 'btn btn-default',
31712 html : '<i class="fa fa-picture-o"></i>'
31718 cls : 'btn-group roo-upload-cropbox-rotate-right',
31719 action : 'rotate-right',
31723 cls : 'btn btn-default',
31724 html : '<i class="fa fa-repeat"></i>'
31732 cls : 'btn-group roo-upload-cropbox-rotate-left',
31733 action : 'rotate-left',
31737 cls : 'btn btn-default',
31738 html : '<i class="fa fa-undo"></i>'
31744 cls : 'btn-group roo-upload-cropbox-download',
31745 action : 'download',
31749 cls : 'btn btn-default',
31750 html : '<i class="fa fa-download"></i>'
31756 cls : 'btn-group roo-upload-cropbox-crop',
31761 cls : 'btn btn-default',
31762 html : '<i class="fa fa-crop"></i>'
31768 cls : 'btn-group roo-upload-cropbox-trash',
31773 cls : 'btn btn-default',
31774 html : '<i class="fa fa-trash"></i>'
31780 cls : 'btn-group roo-upload-cropbox-rotate-right',
31781 action : 'rotate-right',
31785 cls : 'btn btn-default',
31786 html : '<i class="fa fa-repeat"></i>'
31794 cls : 'btn-group roo-upload-cropbox-rotate-left',
31795 action : 'rotate-left',
31799 cls : 'btn btn-default',
31800 html : '<i class="fa fa-undo"></i>'
31806 cls : 'btn-group roo-upload-cropbox-rotate-right',
31807 action : 'rotate-right',
31811 cls : 'btn btn-default',
31812 html : '<i class="fa fa-repeat"></i>'
31825 * @class Roo.bootstrap.DocumentManager
31826 * @extends Roo.bootstrap.Component
31827 * Bootstrap DocumentManager class
31828 * @cfg {String} paramName default 'imageUpload'
31829 * @cfg {String} toolTipName default 'filename'
31830 * @cfg {String} method default POST
31831 * @cfg {String} url action url
31832 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31833 * @cfg {Boolean} multiple multiple upload default true
31834 * @cfg {Number} thumbSize default 300
31835 * @cfg {String} fieldLabel
31836 * @cfg {Number} labelWidth default 4
31837 * @cfg {String} labelAlign (left|top) default left
31838 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31839 * @cfg {Number} labellg set the width of label (1-12)
31840 * @cfg {Number} labelmd set the width of label (1-12)
31841 * @cfg {Number} labelsm set the width of label (1-12)
31842 * @cfg {Number} labelxs set the width of label (1-12)
31845 * Create a new DocumentManager
31846 * @param {Object} config The config object
31849 Roo.bootstrap.DocumentManager = function(config){
31850 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31853 this.delegates = [];
31858 * Fire when initial the DocumentManager
31859 * @param {Roo.bootstrap.DocumentManager} this
31864 * inspect selected file
31865 * @param {Roo.bootstrap.DocumentManager} this
31866 * @param {File} file
31871 * Fire when xhr load exception
31872 * @param {Roo.bootstrap.DocumentManager} this
31873 * @param {XMLHttpRequest} xhr
31875 "exception" : true,
31877 * @event afterupload
31878 * Fire when xhr load exception
31879 * @param {Roo.bootstrap.DocumentManager} this
31880 * @param {XMLHttpRequest} xhr
31882 "afterupload" : true,
31885 * prepare the form data
31886 * @param {Roo.bootstrap.DocumentManager} this
31887 * @param {Object} formData
31892 * Fire when remove the file
31893 * @param {Roo.bootstrap.DocumentManager} this
31894 * @param {Object} file
31899 * Fire after refresh the file
31900 * @param {Roo.bootstrap.DocumentManager} this
31905 * Fire after click the image
31906 * @param {Roo.bootstrap.DocumentManager} this
31907 * @param {Object} file
31912 * Fire when upload a image and editable set to true
31913 * @param {Roo.bootstrap.DocumentManager} this
31914 * @param {Object} file
31918 * @event beforeselectfile
31919 * Fire before select file
31920 * @param {Roo.bootstrap.DocumentManager} this
31922 "beforeselectfile" : true,
31925 * Fire before process file
31926 * @param {Roo.bootstrap.DocumentManager} this
31927 * @param {Object} file
31931 * @event previewrendered
31932 * Fire when preview rendered
31933 * @param {Roo.bootstrap.DocumentManager} this
31934 * @param {Object} file
31936 "previewrendered" : true,
31939 "previewResize" : true
31944 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31953 paramName : 'imageUpload',
31954 toolTipName : 'filename',
31957 labelAlign : 'left',
31967 getAutoCreate : function()
31969 var managerWidget = {
31971 cls : 'roo-document-manager',
31975 cls : 'roo-document-manager-selector',
31980 cls : 'roo-document-manager-uploader',
31984 cls : 'roo-document-manager-upload-btn',
31985 html : '<i class="fa fa-plus"></i>'
31996 cls : 'column col-md-12',
32001 if(this.fieldLabel.length){
32006 cls : 'column col-md-12',
32007 html : this.fieldLabel
32011 cls : 'column col-md-12',
32016 if(this.labelAlign == 'left'){
32021 html : this.fieldLabel
32030 if(this.labelWidth > 12){
32031 content[0].style = "width: " + this.labelWidth + 'px';
32034 if(this.labelWidth < 13 && this.labelmd == 0){
32035 this.labelmd = this.labelWidth;
32038 if(this.labellg > 0){
32039 content[0].cls += ' col-lg-' + this.labellg;
32040 content[1].cls += ' col-lg-' + (12 - this.labellg);
32043 if(this.labelmd > 0){
32044 content[0].cls += ' col-md-' + this.labelmd;
32045 content[1].cls += ' col-md-' + (12 - this.labelmd);
32048 if(this.labelsm > 0){
32049 content[0].cls += ' col-sm-' + this.labelsm;
32050 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32053 if(this.labelxs > 0){
32054 content[0].cls += ' col-xs-' + this.labelxs;
32055 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32063 cls : 'row clearfix',
32071 initEvents : function()
32073 this.managerEl = this.el.select('.roo-document-manager', true).first();
32074 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32076 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32077 this.selectorEl.hide();
32080 this.selectorEl.attr('multiple', 'multiple');
32083 this.selectorEl.on('change', this.onFileSelected, this);
32085 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32086 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32088 this.uploader.on('click', this.onUploaderClick, this);
32090 this.renderProgressDialog();
32094 window.addEventListener("resize", function() { _this.refresh(); } );
32096 this.fireEvent('initial', this);
32099 renderProgressDialog : function()
32103 this.progressDialog = new Roo.bootstrap.Modal({
32104 cls : 'roo-document-manager-progress-dialog',
32105 allow_close : false,
32116 btnclick : function() {
32117 _this.uploadCancel();
32123 this.progressDialog.render(Roo.get(document.body));
32125 this.progress = new Roo.bootstrap.Progress({
32126 cls : 'roo-document-manager-progress',
32131 this.progress.render(this.progressDialog.getChildContainer());
32133 this.progressBar = new Roo.bootstrap.ProgressBar({
32134 cls : 'roo-document-manager-progress-bar',
32137 aria_valuemax : 12,
32141 this.progressBar.render(this.progress.getChildContainer());
32144 onUploaderClick : function(e)
32146 e.preventDefault();
32148 if(this.fireEvent('beforeselectfile', this) != false){
32149 this.selectorEl.dom.click();
32154 onFileSelected : function(e)
32156 e.preventDefault();
32158 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32162 Roo.each(this.selectorEl.dom.files, function(file){
32163 if(this.fireEvent('inspect', this, file) != false){
32164 this.files.push(file);
32174 this.selectorEl.dom.value = '';
32176 if(!this.files || !this.files.length){
32180 if(this.boxes > 0 && this.files.length > this.boxes){
32181 this.files = this.files.slice(0, this.boxes);
32184 this.uploader.show();
32186 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32187 this.uploader.hide();
32196 Roo.each(this.files, function(file){
32198 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32199 var f = this.renderPreview(file);
32204 if(file.type.indexOf('image') != -1){
32205 this.delegates.push(
32207 _this.process(file);
32208 }).createDelegate(this)
32216 _this.process(file);
32217 }).createDelegate(this)
32222 this.files = files;
32224 this.delegates = this.delegates.concat(docs);
32226 if(!this.delegates.length){
32231 this.progressBar.aria_valuemax = this.delegates.length;
32238 arrange : function()
32240 if(!this.delegates.length){
32241 this.progressDialog.hide();
32246 var delegate = this.delegates.shift();
32248 this.progressDialog.show();
32250 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32252 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32257 refresh : function()
32259 this.uploader.show();
32261 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32262 this.uploader.hide();
32265 Roo.isTouch ? this.closable(false) : this.closable(true);
32267 this.fireEvent('refresh', this);
32270 onRemove : function(e, el, o)
32272 e.preventDefault();
32274 this.fireEvent('remove', this, o);
32278 remove : function(o)
32282 Roo.each(this.files, function(file){
32283 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32292 this.files = files;
32299 Roo.each(this.files, function(file){
32304 file.target.remove();
32313 onClick : function(e, el, o)
32315 e.preventDefault();
32317 this.fireEvent('click', this, o);
32321 closable : function(closable)
32323 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32325 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32337 xhrOnLoad : function(xhr)
32339 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32343 if (xhr.readyState !== 4) {
32345 this.fireEvent('exception', this, xhr);
32349 var response = Roo.decode(xhr.responseText);
32351 if(!response.success){
32353 this.fireEvent('exception', this, xhr);
32357 var file = this.renderPreview(response.data);
32359 this.files.push(file);
32363 this.fireEvent('afterupload', this, xhr);
32367 xhrOnError : function(xhr)
32369 Roo.log('xhr on error');
32371 var response = Roo.decode(xhr.responseText);
32378 process : function(file)
32380 if(this.fireEvent('process', this, file) !== false){
32381 if(this.editable && file.type.indexOf('image') != -1){
32382 this.fireEvent('edit', this, file);
32386 this.uploadStart(file, false);
32393 uploadStart : function(file, crop)
32395 this.xhr = new XMLHttpRequest();
32397 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32402 file.xhr = this.xhr;
32404 this.managerEl.createChild({
32406 cls : 'roo-document-manager-loading',
32410 tooltip : file.name,
32411 cls : 'roo-document-manager-thumb',
32412 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32418 this.xhr.open(this.method, this.url, true);
32421 "Accept": "application/json",
32422 "Cache-Control": "no-cache",
32423 "X-Requested-With": "XMLHttpRequest"
32426 for (var headerName in headers) {
32427 var headerValue = headers[headerName];
32429 this.xhr.setRequestHeader(headerName, headerValue);
32435 this.xhr.onload = function()
32437 _this.xhrOnLoad(_this.xhr);
32440 this.xhr.onerror = function()
32442 _this.xhrOnError(_this.xhr);
32445 var formData = new FormData();
32447 formData.append('returnHTML', 'NO');
32450 formData.append('crop', crop);
32453 formData.append(this.paramName, file, file.name);
32460 if(this.fireEvent('prepare', this, formData, options) != false){
32462 if(options.manually){
32466 this.xhr.send(formData);
32470 this.uploadCancel();
32473 uploadCancel : function()
32479 this.delegates = [];
32481 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32488 renderPreview : function(file)
32490 if(typeof(file.target) != 'undefined' && file.target){
32494 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32496 var previewEl = this.managerEl.createChild({
32498 cls : 'roo-document-manager-preview',
32502 tooltip : file[this.toolTipName],
32503 cls : 'roo-document-manager-thumb',
32504 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32509 html : '<i class="fa fa-times-circle"></i>'
32514 var close = previewEl.select('button.close', true).first();
32516 close.on('click', this.onRemove, this, file);
32518 file.target = previewEl;
32520 var image = previewEl.select('img', true).first();
32524 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32526 image.on('click', this.onClick, this, file);
32528 this.fireEvent('previewrendered', this, file);
32534 onPreviewLoad : function(file, image)
32536 if(typeof(file.target) == 'undefined' || !file.target){
32540 var width = image.dom.naturalWidth || image.dom.width;
32541 var height = image.dom.naturalHeight || image.dom.height;
32543 if(!this.previewResize) {
32547 if(width > height){
32548 file.target.addClass('wide');
32552 file.target.addClass('tall');
32557 uploadFromSource : function(file, crop)
32559 this.xhr = new XMLHttpRequest();
32561 this.managerEl.createChild({
32563 cls : 'roo-document-manager-loading',
32567 tooltip : file.name,
32568 cls : 'roo-document-manager-thumb',
32569 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32575 this.xhr.open(this.method, this.url, true);
32578 "Accept": "application/json",
32579 "Cache-Control": "no-cache",
32580 "X-Requested-With": "XMLHttpRequest"
32583 for (var headerName in headers) {
32584 var headerValue = headers[headerName];
32586 this.xhr.setRequestHeader(headerName, headerValue);
32592 this.xhr.onload = function()
32594 _this.xhrOnLoad(_this.xhr);
32597 this.xhr.onerror = function()
32599 _this.xhrOnError(_this.xhr);
32602 var formData = new FormData();
32604 formData.append('returnHTML', 'NO');
32606 formData.append('crop', crop);
32608 if(typeof(file.filename) != 'undefined'){
32609 formData.append('filename', file.filename);
32612 if(typeof(file.mimetype) != 'undefined'){
32613 formData.append('mimetype', file.mimetype);
32618 if(this.fireEvent('prepare', this, formData) != false){
32619 this.xhr.send(formData);
32629 * @class Roo.bootstrap.DocumentViewer
32630 * @extends Roo.bootstrap.Component
32631 * Bootstrap DocumentViewer class
32632 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32633 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32636 * Create a new DocumentViewer
32637 * @param {Object} config The config object
32640 Roo.bootstrap.DocumentViewer = function(config){
32641 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32646 * Fire after initEvent
32647 * @param {Roo.bootstrap.DocumentViewer} this
32653 * @param {Roo.bootstrap.DocumentViewer} this
32658 * Fire after download button
32659 * @param {Roo.bootstrap.DocumentViewer} this
32664 * Fire after trash button
32665 * @param {Roo.bootstrap.DocumentViewer} this
32672 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32674 showDownload : true,
32678 getAutoCreate : function()
32682 cls : 'roo-document-viewer',
32686 cls : 'roo-document-viewer-body',
32690 cls : 'roo-document-viewer-thumb',
32694 cls : 'roo-document-viewer-image'
32702 cls : 'roo-document-viewer-footer',
32705 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32709 cls : 'btn-group roo-document-viewer-download',
32713 cls : 'btn btn-default',
32714 html : '<i class="fa fa-download"></i>'
32720 cls : 'btn-group roo-document-viewer-trash',
32724 cls : 'btn btn-default',
32725 html : '<i class="fa fa-trash"></i>'
32738 initEvents : function()
32740 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32741 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32743 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32744 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32746 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32747 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32749 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32750 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32752 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32753 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32755 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32756 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32758 this.bodyEl.on('click', this.onClick, this);
32759 this.downloadBtn.on('click', this.onDownload, this);
32760 this.trashBtn.on('click', this.onTrash, this);
32762 this.downloadBtn.hide();
32763 this.trashBtn.hide();
32765 if(this.showDownload){
32766 this.downloadBtn.show();
32769 if(this.showTrash){
32770 this.trashBtn.show();
32773 if(!this.showDownload && !this.showTrash) {
32774 this.footerEl.hide();
32779 initial : function()
32781 this.fireEvent('initial', this);
32785 onClick : function(e)
32787 e.preventDefault();
32789 this.fireEvent('click', this);
32792 onDownload : function(e)
32794 e.preventDefault();
32796 this.fireEvent('download', this);
32799 onTrash : function(e)
32801 e.preventDefault();
32803 this.fireEvent('trash', this);
32815 * @class Roo.bootstrap.NavProgressBar
32816 * @extends Roo.bootstrap.Component
32817 * Bootstrap NavProgressBar class
32820 * Create a new nav progress bar
32821 * @param {Object} config The config object
32824 Roo.bootstrap.NavProgressBar = function(config){
32825 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32827 this.bullets = this.bullets || [];
32829 // Roo.bootstrap.NavProgressBar.register(this);
32833 * Fires when the active item changes
32834 * @param {Roo.bootstrap.NavProgressBar} this
32835 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32836 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32843 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32848 getAutoCreate : function()
32850 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32854 cls : 'roo-navigation-bar-group',
32858 cls : 'roo-navigation-top-bar'
32862 cls : 'roo-navigation-bullets-bar',
32866 cls : 'roo-navigation-bar'
32873 cls : 'roo-navigation-bottom-bar'
32883 initEvents: function()
32888 onRender : function(ct, position)
32890 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32892 if(this.bullets.length){
32893 Roo.each(this.bullets, function(b){
32902 addItem : function(cfg)
32904 var item = new Roo.bootstrap.NavProgressItem(cfg);
32906 item.parentId = this.id;
32907 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32910 var top = new Roo.bootstrap.Element({
32912 cls : 'roo-navigation-bar-text'
32915 var bottom = new Roo.bootstrap.Element({
32917 cls : 'roo-navigation-bar-text'
32920 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32921 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32923 var topText = new Roo.bootstrap.Element({
32925 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32928 var bottomText = new Roo.bootstrap.Element({
32930 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32933 topText.onRender(top.el, null);
32934 bottomText.onRender(bottom.el, null);
32937 item.bottomEl = bottom;
32940 this.barItems.push(item);
32945 getActive : function()
32947 var active = false;
32949 Roo.each(this.barItems, function(v){
32951 if (!v.isActive()) {
32963 setActiveItem : function(item)
32967 Roo.each(this.barItems, function(v){
32968 if (v.rid == item.rid) {
32972 if (v.isActive()) {
32973 v.setActive(false);
32978 item.setActive(true);
32980 this.fireEvent('changed', this, item, prev);
32983 getBarItem: function(rid)
32987 Roo.each(this.barItems, function(e) {
32988 if (e.rid != rid) {
32999 indexOfItem : function(item)
33003 Roo.each(this.barItems, function(v, i){
33005 if (v.rid != item.rid) {
33016 setActiveNext : function()
33018 var i = this.indexOfItem(this.getActive());
33020 if (i > this.barItems.length) {
33024 this.setActiveItem(this.barItems[i+1]);
33027 setActivePrev : function()
33029 var i = this.indexOfItem(this.getActive());
33035 this.setActiveItem(this.barItems[i-1]);
33038 format : function()
33040 if(!this.barItems.length){
33044 var width = 100 / this.barItems.length;
33046 Roo.each(this.barItems, function(i){
33047 i.el.setStyle('width', width + '%');
33048 i.topEl.el.setStyle('width', width + '%');
33049 i.bottomEl.el.setStyle('width', width + '%');
33058 * Nav Progress Item
33063 * @class Roo.bootstrap.NavProgressItem
33064 * @extends Roo.bootstrap.Component
33065 * Bootstrap NavProgressItem class
33066 * @cfg {String} rid the reference id
33067 * @cfg {Boolean} active (true|false) Is item active default false
33068 * @cfg {Boolean} disabled (true|false) Is item active default false
33069 * @cfg {String} html
33070 * @cfg {String} position (top|bottom) text position default bottom
33071 * @cfg {String} icon show icon instead of number
33074 * Create a new NavProgressItem
33075 * @param {Object} config The config object
33077 Roo.bootstrap.NavProgressItem = function(config){
33078 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33083 * The raw click event for the entire grid.
33084 * @param {Roo.bootstrap.NavProgressItem} this
33085 * @param {Roo.EventObject} e
33092 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33098 position : 'bottom',
33101 getAutoCreate : function()
33103 var iconCls = 'roo-navigation-bar-item-icon';
33105 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33109 cls: 'roo-navigation-bar-item',
33119 cfg.cls += ' active';
33122 cfg.cls += ' disabled';
33128 disable : function()
33130 this.setDisabled(true);
33133 enable : function()
33135 this.setDisabled(false);
33138 initEvents: function()
33140 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33142 this.iconEl.on('click', this.onClick, this);
33145 onClick : function(e)
33147 e.preventDefault();
33153 if(this.fireEvent('click', this, e) === false){
33157 this.parent().setActiveItem(this);
33160 isActive: function ()
33162 return this.active;
33165 setActive : function(state)
33167 if(this.active == state){
33171 this.active = state;
33174 this.el.addClass('active');
33178 this.el.removeClass('active');
33183 setDisabled : function(state)
33185 if(this.disabled == state){
33189 this.disabled = state;
33192 this.el.addClass('disabled');
33196 this.el.removeClass('disabled');
33199 tooltipEl : function()
33201 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33214 * @class Roo.bootstrap.FieldLabel
33215 * @extends Roo.bootstrap.Component
33216 * Bootstrap FieldLabel class
33217 * @cfg {String} html contents of the element
33218 * @cfg {String} tag tag of the element default label
33219 * @cfg {String} cls class of the element
33220 * @cfg {String} target label target
33221 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33222 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33223 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33224 * @cfg {String} iconTooltip default "This field is required"
33225 * @cfg {String} indicatorpos (left|right) default left
33228 * Create a new FieldLabel
33229 * @param {Object} config The config object
33232 Roo.bootstrap.FieldLabel = function(config){
33233 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33238 * Fires after the field has been marked as invalid.
33239 * @param {Roo.form.FieldLabel} this
33240 * @param {String} msg The validation message
33245 * Fires after the field has been validated with no errors.
33246 * @param {Roo.form.FieldLabel} this
33252 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33259 invalidClass : 'has-warning',
33260 validClass : 'has-success',
33261 iconTooltip : 'This field is required',
33262 indicatorpos : 'left',
33264 getAutoCreate : function(){
33267 if (!this.allowBlank) {
33273 cls : 'roo-bootstrap-field-label ' + this.cls,
33278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33279 tooltip : this.iconTooltip
33288 if(this.indicatorpos == 'right'){
33291 cls : 'roo-bootstrap-field-label ' + this.cls,
33300 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33301 tooltip : this.iconTooltip
33310 initEvents: function()
33312 Roo.bootstrap.Element.superclass.initEvents.call(this);
33314 this.indicator = this.indicatorEl();
33316 if(this.indicator){
33317 this.indicator.removeClass('visible');
33318 this.indicator.addClass('invisible');
33321 Roo.bootstrap.FieldLabel.register(this);
33324 indicatorEl : function()
33326 var indicator = this.el.select('i.roo-required-indicator',true).first();
33337 * Mark this field as valid
33339 markValid : function()
33341 if(this.indicator){
33342 this.indicator.removeClass('visible');
33343 this.indicator.addClass('invisible');
33345 if (Roo.bootstrap.version == 3) {
33346 this.el.removeClass(this.invalidClass);
33347 this.el.addClass(this.validClass);
33349 this.el.removeClass('is-invalid');
33350 this.el.addClass('is-valid');
33354 this.fireEvent('valid', this);
33358 * Mark this field as invalid
33359 * @param {String} msg The validation message
33361 markInvalid : function(msg)
33363 if(this.indicator){
33364 this.indicator.removeClass('invisible');
33365 this.indicator.addClass('visible');
33367 if (Roo.bootstrap.version == 3) {
33368 this.el.removeClass(this.validClass);
33369 this.el.addClass(this.invalidClass);
33371 this.el.removeClass('is-valid');
33372 this.el.addClass('is-invalid');
33376 this.fireEvent('invalid', this, msg);
33382 Roo.apply(Roo.bootstrap.FieldLabel, {
33387 * register a FieldLabel Group
33388 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33390 register : function(label)
33392 if(this.groups.hasOwnProperty(label.target)){
33396 this.groups[label.target] = label;
33400 * fetch a FieldLabel Group based on the target
33401 * @param {string} target
33402 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33404 get: function(target) {
33405 if (typeof(this.groups[target]) == 'undefined') {
33409 return this.groups[target] ;
33418 * page DateSplitField.
33424 * @class Roo.bootstrap.DateSplitField
33425 * @extends Roo.bootstrap.Component
33426 * Bootstrap DateSplitField class
33427 * @cfg {string} fieldLabel - the label associated
33428 * @cfg {Number} labelWidth set the width of label (0-12)
33429 * @cfg {String} labelAlign (top|left)
33430 * @cfg {Boolean} dayAllowBlank (true|false) default false
33431 * @cfg {Boolean} monthAllowBlank (true|false) default false
33432 * @cfg {Boolean} yearAllowBlank (true|false) default false
33433 * @cfg {string} dayPlaceholder
33434 * @cfg {string} monthPlaceholder
33435 * @cfg {string} yearPlaceholder
33436 * @cfg {string} dayFormat default 'd'
33437 * @cfg {string} monthFormat default 'm'
33438 * @cfg {string} yearFormat default 'Y'
33439 * @cfg {Number} labellg set the width of label (1-12)
33440 * @cfg {Number} labelmd set the width of label (1-12)
33441 * @cfg {Number} labelsm set the width of label (1-12)
33442 * @cfg {Number} labelxs set the width of label (1-12)
33446 * Create a new DateSplitField
33447 * @param {Object} config The config object
33450 Roo.bootstrap.DateSplitField = function(config){
33451 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33457 * getting the data of years
33458 * @param {Roo.bootstrap.DateSplitField} this
33459 * @param {Object} years
33464 * getting the data of days
33465 * @param {Roo.bootstrap.DateSplitField} this
33466 * @param {Object} days
33471 * Fires after the field has been marked as invalid.
33472 * @param {Roo.form.Field} this
33473 * @param {String} msg The validation message
33478 * Fires after the field has been validated with no errors.
33479 * @param {Roo.form.Field} this
33485 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33488 labelAlign : 'top',
33490 dayAllowBlank : false,
33491 monthAllowBlank : false,
33492 yearAllowBlank : false,
33493 dayPlaceholder : '',
33494 monthPlaceholder : '',
33495 yearPlaceholder : '',
33499 isFormField : true,
33505 getAutoCreate : function()
33509 cls : 'row roo-date-split-field-group',
33514 cls : 'form-hidden-field roo-date-split-field-group-value',
33520 var labelCls = 'col-md-12';
33521 var contentCls = 'col-md-4';
33523 if(this.fieldLabel){
33527 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33531 html : this.fieldLabel
33536 if(this.labelAlign == 'left'){
33538 if(this.labelWidth > 12){
33539 label.style = "width: " + this.labelWidth + 'px';
33542 if(this.labelWidth < 13 && this.labelmd == 0){
33543 this.labelmd = this.labelWidth;
33546 if(this.labellg > 0){
33547 labelCls = ' col-lg-' + this.labellg;
33548 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33551 if(this.labelmd > 0){
33552 labelCls = ' col-md-' + this.labelmd;
33553 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33556 if(this.labelsm > 0){
33557 labelCls = ' col-sm-' + this.labelsm;
33558 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33561 if(this.labelxs > 0){
33562 labelCls = ' col-xs-' + this.labelxs;
33563 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33567 label.cls += ' ' + labelCls;
33569 cfg.cn.push(label);
33572 Roo.each(['day', 'month', 'year'], function(t){
33575 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33582 inputEl: function ()
33584 return this.el.select('.roo-date-split-field-group-value', true).first();
33587 onRender : function(ct, position)
33591 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33593 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33595 this.dayField = new Roo.bootstrap.ComboBox({
33596 allowBlank : this.dayAllowBlank,
33597 alwaysQuery : true,
33598 displayField : 'value',
33601 forceSelection : true,
33603 placeholder : this.dayPlaceholder,
33604 selectOnFocus : true,
33605 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33606 triggerAction : 'all',
33608 valueField : 'value',
33609 store : new Roo.data.SimpleStore({
33610 data : (function() {
33612 _this.fireEvent('days', _this, days);
33615 fields : [ 'value' ]
33618 select : function (_self, record, index)
33620 _this.setValue(_this.getValue());
33625 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33627 this.monthField = new Roo.bootstrap.MonthField({
33628 after : '<i class=\"fa fa-calendar\"></i>',
33629 allowBlank : this.monthAllowBlank,
33630 placeholder : this.monthPlaceholder,
33633 render : function (_self)
33635 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33636 e.preventDefault();
33640 select : function (_self, oldvalue, newvalue)
33642 _this.setValue(_this.getValue());
33647 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33649 this.yearField = new Roo.bootstrap.ComboBox({
33650 allowBlank : this.yearAllowBlank,
33651 alwaysQuery : true,
33652 displayField : 'value',
33655 forceSelection : true,
33657 placeholder : this.yearPlaceholder,
33658 selectOnFocus : true,
33659 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33660 triggerAction : 'all',
33662 valueField : 'value',
33663 store : new Roo.data.SimpleStore({
33664 data : (function() {
33666 _this.fireEvent('years', _this, years);
33669 fields : [ 'value' ]
33672 select : function (_self, record, index)
33674 _this.setValue(_this.getValue());
33679 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33682 setValue : function(v, format)
33684 this.inputEl.dom.value = v;
33686 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33688 var d = Date.parseDate(v, f);
33695 this.setDay(d.format(this.dayFormat));
33696 this.setMonth(d.format(this.monthFormat));
33697 this.setYear(d.format(this.yearFormat));
33704 setDay : function(v)
33706 this.dayField.setValue(v);
33707 this.inputEl.dom.value = this.getValue();
33712 setMonth : function(v)
33714 this.monthField.setValue(v, true);
33715 this.inputEl.dom.value = this.getValue();
33720 setYear : function(v)
33722 this.yearField.setValue(v);
33723 this.inputEl.dom.value = this.getValue();
33728 getDay : function()
33730 return this.dayField.getValue();
33733 getMonth : function()
33735 return this.monthField.getValue();
33738 getYear : function()
33740 return this.yearField.getValue();
33743 getValue : function()
33745 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33747 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33757 this.inputEl.dom.value = '';
33762 validate : function()
33764 var d = this.dayField.validate();
33765 var m = this.monthField.validate();
33766 var y = this.yearField.validate();
33771 (!this.dayAllowBlank && !d) ||
33772 (!this.monthAllowBlank && !m) ||
33773 (!this.yearAllowBlank && !y)
33778 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33787 this.markInvalid();
33792 markValid : function()
33795 var label = this.el.select('label', true).first();
33796 var icon = this.el.select('i.fa-star', true).first();
33802 this.fireEvent('valid', this);
33806 * Mark this field as invalid
33807 * @param {String} msg The validation message
33809 markInvalid : function(msg)
33812 var label = this.el.select('label', true).first();
33813 var icon = this.el.select('i.fa-star', true).first();
33815 if(label && !icon){
33816 this.el.select('.roo-date-split-field-label', true).createChild({
33818 cls : 'text-danger fa fa-lg fa-star',
33819 tooltip : 'This field is required',
33820 style : 'margin-right:5px;'
33824 this.fireEvent('invalid', this, msg);
33827 clearInvalid : function()
33829 var label = this.el.select('label', true).first();
33830 var icon = this.el.select('i.fa-star', true).first();
33836 this.fireEvent('valid', this);
33839 getName: function()
33849 * http://masonry.desandro.com
33851 * The idea is to render all the bricks based on vertical width...
33853 * The original code extends 'outlayer' - we might need to use that....
33859 * @class Roo.bootstrap.LayoutMasonry
33860 * @extends Roo.bootstrap.Component
33861 * Bootstrap Layout Masonry class
33864 * Create a new Element
33865 * @param {Object} config The config object
33868 Roo.bootstrap.LayoutMasonry = function(config){
33870 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33874 Roo.bootstrap.LayoutMasonry.register(this);
33880 * Fire after layout the items
33881 * @param {Roo.bootstrap.LayoutMasonry} this
33882 * @param {Roo.EventObject} e
33889 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33892 * @cfg {Boolean} isLayoutInstant = no animation?
33894 isLayoutInstant : false, // needed?
33897 * @cfg {Number} boxWidth width of the columns
33902 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33907 * @cfg {Number} padWidth padding below box..
33912 * @cfg {Number} gutter gutter width..
33917 * @cfg {Number} maxCols maximum number of columns
33923 * @cfg {Boolean} isAutoInitial defalut true
33925 isAutoInitial : true,
33930 * @cfg {Boolean} isHorizontal defalut false
33932 isHorizontal : false,
33934 currentSize : null,
33940 bricks: null, //CompositeElement
33944 _isLayoutInited : false,
33946 // isAlternative : false, // only use for vertical layout...
33949 * @cfg {Number} alternativePadWidth padding below box..
33951 alternativePadWidth : 50,
33953 selectedBrick : [],
33955 getAutoCreate : function(){
33957 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33961 cls: 'blog-masonary-wrapper ' + this.cls,
33963 cls : 'mas-boxes masonary'
33970 getChildContainer: function( )
33972 if (this.boxesEl) {
33973 return this.boxesEl;
33976 this.boxesEl = this.el.select('.mas-boxes').first();
33978 return this.boxesEl;
33982 initEvents : function()
33986 if(this.isAutoInitial){
33987 Roo.log('hook children rendered');
33988 this.on('childrenrendered', function() {
33989 Roo.log('children rendered');
33995 initial : function()
33997 this.selectedBrick = [];
33999 this.currentSize = this.el.getBox(true);
34001 Roo.EventManager.onWindowResize(this.resize, this);
34003 if(!this.isAutoInitial){
34011 //this.layout.defer(500,this);
34015 resize : function()
34017 var cs = this.el.getBox(true);
34020 this.currentSize.width == cs.width &&
34021 this.currentSize.x == cs.x &&
34022 this.currentSize.height == cs.height &&
34023 this.currentSize.y == cs.y
34025 Roo.log("no change in with or X or Y");
34029 this.currentSize = cs;
34035 layout : function()
34037 this._resetLayout();
34039 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34041 this.layoutItems( isInstant );
34043 this._isLayoutInited = true;
34045 this.fireEvent('layout', this);
34049 _resetLayout : function()
34051 if(this.isHorizontal){
34052 this.horizontalMeasureColumns();
34056 this.verticalMeasureColumns();
34060 verticalMeasureColumns : function()
34062 this.getContainerWidth();
34064 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34065 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34069 var boxWidth = this.boxWidth + this.padWidth;
34071 if(this.containerWidth < this.boxWidth){
34072 boxWidth = this.containerWidth
34075 var containerWidth = this.containerWidth;
34077 var cols = Math.floor(containerWidth / boxWidth);
34079 this.cols = Math.max( cols, 1 );
34081 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34083 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34085 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34087 this.colWidth = boxWidth + avail - this.padWidth;
34089 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34090 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34093 horizontalMeasureColumns : function()
34095 this.getContainerWidth();
34097 var boxWidth = this.boxWidth;
34099 if(this.containerWidth < boxWidth){
34100 boxWidth = this.containerWidth;
34103 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34105 this.el.setHeight(boxWidth);
34109 getContainerWidth : function()
34111 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34114 layoutItems : function( isInstant )
34116 Roo.log(this.bricks);
34118 var items = Roo.apply([], this.bricks);
34120 if(this.isHorizontal){
34121 this._horizontalLayoutItems( items , isInstant );
34125 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34126 // this._verticalAlternativeLayoutItems( items , isInstant );
34130 this._verticalLayoutItems( items , isInstant );
34134 _verticalLayoutItems : function ( items , isInstant)
34136 if ( !items || !items.length ) {
34141 ['xs', 'xs', 'xs', 'tall'],
34142 ['xs', 'xs', 'tall'],
34143 ['xs', 'xs', 'sm'],
34144 ['xs', 'xs', 'xs'],
34150 ['sm', 'xs', 'xs'],
34154 ['tall', 'xs', 'xs', 'xs'],
34155 ['tall', 'xs', 'xs'],
34167 Roo.each(items, function(item, k){
34169 switch (item.size) {
34170 // these layouts take up a full box,
34181 boxes.push([item]);
34204 var filterPattern = function(box, length)
34212 var pattern = box.slice(0, length);
34216 Roo.each(pattern, function(i){
34217 format.push(i.size);
34220 Roo.each(standard, function(s){
34222 if(String(s) != String(format)){
34231 if(!match && length == 1){
34236 filterPattern(box, length - 1);
34240 queue.push(pattern);
34242 box = box.slice(length, box.length);
34244 filterPattern(box, 4);
34250 Roo.each(boxes, function(box, k){
34256 if(box.length == 1){
34261 filterPattern(box, 4);
34265 this._processVerticalLayoutQueue( queue, isInstant );
34269 // _verticalAlternativeLayoutItems : function( items , isInstant )
34271 // if ( !items || !items.length ) {
34275 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34279 _horizontalLayoutItems : function ( items , isInstant)
34281 if ( !items || !items.length || items.length < 3) {
34287 var eItems = items.slice(0, 3);
34289 items = items.slice(3, items.length);
34292 ['xs', 'xs', 'xs', 'wide'],
34293 ['xs', 'xs', 'wide'],
34294 ['xs', 'xs', 'sm'],
34295 ['xs', 'xs', 'xs'],
34301 ['sm', 'xs', 'xs'],
34305 ['wide', 'xs', 'xs', 'xs'],
34306 ['wide', 'xs', 'xs'],
34319 Roo.each(items, function(item, k){
34321 switch (item.size) {
34332 boxes.push([item]);
34356 var filterPattern = function(box, length)
34364 var pattern = box.slice(0, length);
34368 Roo.each(pattern, function(i){
34369 format.push(i.size);
34372 Roo.each(standard, function(s){
34374 if(String(s) != String(format)){
34383 if(!match && length == 1){
34388 filterPattern(box, length - 1);
34392 queue.push(pattern);
34394 box = box.slice(length, box.length);
34396 filterPattern(box, 4);
34402 Roo.each(boxes, function(box, k){
34408 if(box.length == 1){
34413 filterPattern(box, 4);
34420 var pos = this.el.getBox(true);
34424 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34426 var hit_end = false;
34428 Roo.each(queue, function(box){
34432 Roo.each(box, function(b){
34434 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34444 Roo.each(box, function(b){
34446 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34449 mx = Math.max(mx, b.x);
34453 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34457 Roo.each(box, function(b){
34459 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34473 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34476 /** Sets position of item in DOM
34477 * @param {Element} item
34478 * @param {Number} x - horizontal position
34479 * @param {Number} y - vertical position
34480 * @param {Boolean} isInstant - disables transitions
34482 _processVerticalLayoutQueue : function( queue, isInstant )
34484 var pos = this.el.getBox(true);
34489 for (var i = 0; i < this.cols; i++){
34493 Roo.each(queue, function(box, k){
34495 var col = k % this.cols;
34497 Roo.each(box, function(b,kk){
34499 b.el.position('absolute');
34501 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34502 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34504 if(b.size == 'md-left' || b.size == 'md-right'){
34505 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34506 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34509 b.el.setWidth(width);
34510 b.el.setHeight(height);
34512 b.el.select('iframe',true).setSize(width,height);
34516 for (var i = 0; i < this.cols; i++){
34518 if(maxY[i] < maxY[col]){
34523 col = Math.min(col, i);
34527 x = pos.x + col * (this.colWidth + this.padWidth);
34531 var positions = [];
34533 switch (box.length){
34535 positions = this.getVerticalOneBoxColPositions(x, y, box);
34538 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34541 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34544 positions = this.getVerticalFourBoxColPositions(x, y, box);
34550 Roo.each(box, function(b,kk){
34552 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34554 var sz = b.el.getSize();
34556 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34564 for (var i = 0; i < this.cols; i++){
34565 mY = Math.max(mY, maxY[i]);
34568 this.el.setHeight(mY - pos.y);
34572 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34574 // var pos = this.el.getBox(true);
34577 // var maxX = pos.right;
34579 // var maxHeight = 0;
34581 // Roo.each(items, function(item, k){
34585 // item.el.position('absolute');
34587 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34589 // item.el.setWidth(width);
34591 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34593 // item.el.setHeight(height);
34596 // item.el.setXY([x, y], isInstant ? false : true);
34598 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34601 // y = y + height + this.alternativePadWidth;
34603 // maxHeight = maxHeight + height + this.alternativePadWidth;
34607 // this.el.setHeight(maxHeight);
34611 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34613 var pos = this.el.getBox(true);
34618 var maxX = pos.right;
34620 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34622 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34624 Roo.each(queue, function(box, k){
34626 Roo.each(box, function(b, kk){
34628 b.el.position('absolute');
34630 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34631 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34633 if(b.size == 'md-left' || b.size == 'md-right'){
34634 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34635 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34638 b.el.setWidth(width);
34639 b.el.setHeight(height);
34647 var positions = [];
34649 switch (box.length){
34651 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34654 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34657 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34660 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34666 Roo.each(box, function(b,kk){
34668 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34670 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34678 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34680 Roo.each(eItems, function(b,k){
34682 b.size = (k == 0) ? 'sm' : 'xs';
34683 b.x = (k == 0) ? 2 : 1;
34684 b.y = (k == 0) ? 2 : 1;
34686 b.el.position('absolute');
34688 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34690 b.el.setWidth(width);
34692 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34694 b.el.setHeight(height);
34698 var positions = [];
34701 x : maxX - this.unitWidth * 2 - this.gutter,
34706 x : maxX - this.unitWidth,
34707 y : minY + (this.unitWidth + this.gutter) * 2
34711 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34715 Roo.each(eItems, function(b,k){
34717 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34723 getVerticalOneBoxColPositions : function(x, y, box)
34727 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34729 if(box[0].size == 'md-left'){
34733 if(box[0].size == 'md-right'){
34738 x : x + (this.unitWidth + this.gutter) * rand,
34745 getVerticalTwoBoxColPositions : function(x, y, box)
34749 if(box[0].size == 'xs'){
34753 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34757 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34771 x : x + (this.unitWidth + this.gutter) * 2,
34772 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34779 getVerticalThreeBoxColPositions : function(x, y, box)
34783 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34791 x : x + (this.unitWidth + this.gutter) * 1,
34796 x : x + (this.unitWidth + this.gutter) * 2,
34804 if(box[0].size == 'xs' && box[1].size == 'xs'){
34813 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34817 x : x + (this.unitWidth + this.gutter) * 1,
34831 x : x + (this.unitWidth + this.gutter) * 2,
34836 x : x + (this.unitWidth + this.gutter) * 2,
34837 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34844 getVerticalFourBoxColPositions : function(x, y, box)
34848 if(box[0].size == 'xs'){
34857 y : y + (this.unitHeight + this.gutter) * 1
34862 y : y + (this.unitHeight + this.gutter) * 2
34866 x : x + (this.unitWidth + this.gutter) * 1,
34880 x : x + (this.unitWidth + this.gutter) * 2,
34885 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34886 y : y + (this.unitHeight + this.gutter) * 1
34890 x : x + (this.unitWidth + this.gutter) * 2,
34891 y : y + (this.unitWidth + this.gutter) * 2
34898 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34902 if(box[0].size == 'md-left'){
34904 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34911 if(box[0].size == 'md-right'){
34913 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34914 y : minY + (this.unitWidth + this.gutter) * 1
34920 var rand = Math.floor(Math.random() * (4 - box[0].y));
34923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34924 y : minY + (this.unitWidth + this.gutter) * rand
34931 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34935 if(box[0].size == 'xs'){
34938 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34943 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34944 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34952 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34958 y : minY + (this.unitWidth + this.gutter) * 2
34965 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34969 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34977 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34978 y : minY + (this.unitWidth + this.gutter) * 1
34982 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34983 y : minY + (this.unitWidth + this.gutter) * 2
34990 if(box[0].size == 'xs' && box[1].size == 'xs'){
34993 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35003 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35004 y : minY + (this.unitWidth + this.gutter) * 1
35012 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35018 y : minY + (this.unitWidth + this.gutter) * 2
35022 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35023 y : minY + (this.unitWidth + this.gutter) * 2
35030 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35034 if(box[0].size == 'xs'){
35037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
35047 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35052 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35053 y : minY + (this.unitWidth + this.gutter) * 1
35061 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35066 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
35072 y : minY + (this.unitWidth + this.gutter) * 2
35076 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35077 y : minY + (this.unitWidth + this.gutter) * 2
35085 * remove a Masonry Brick
35086 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35088 removeBrick : function(brick_id)
35094 for (var i = 0; i<this.bricks.length; i++) {
35095 if (this.bricks[i].id == brick_id) {
35096 this.bricks.splice(i,1);
35097 this.el.dom.removeChild(Roo.get(brick_id).dom);
35104 * adds a Masonry Brick
35105 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35107 addBrick : function(cfg)
35109 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35110 //this.register(cn);
35111 cn.parentId = this.id;
35112 cn.render(this.el);
35117 * register a Masonry Brick
35118 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35121 register : function(brick)
35123 this.bricks.push(brick);
35124 brick.masonryId = this.id;
35128 * clear all the Masonry Brick
35130 clearAll : function()
35133 //this.getChildContainer().dom.innerHTML = "";
35134 this.el.dom.innerHTML = '';
35137 getSelected : function()
35139 if (!this.selectedBrick) {
35143 return this.selectedBrick;
35147 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35151 * register a Masonry Layout
35152 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35155 register : function(layout)
35157 this.groups[layout.id] = layout;
35160 * fetch a Masonry Layout based on the masonry layout ID
35161 * @param {string} the masonry layout to add
35162 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35165 get: function(layout_id) {
35166 if (typeof(this.groups[layout_id]) == 'undefined') {
35169 return this.groups[layout_id] ;
35181 * http://masonry.desandro.com
35183 * The idea is to render all the bricks based on vertical width...
35185 * The original code extends 'outlayer' - we might need to use that....
35191 * @class Roo.bootstrap.LayoutMasonryAuto
35192 * @extends Roo.bootstrap.Component
35193 * Bootstrap Layout Masonry class
35196 * Create a new Element
35197 * @param {Object} config The config object
35200 Roo.bootstrap.LayoutMasonryAuto = function(config){
35201 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35204 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35207 * @cfg {Boolean} isFitWidth - resize the width..
35209 isFitWidth : false, // options..
35211 * @cfg {Boolean} isOriginLeft = left align?
35213 isOriginLeft : true,
35215 * @cfg {Boolean} isOriginTop = top align?
35217 isOriginTop : false,
35219 * @cfg {Boolean} isLayoutInstant = no animation?
35221 isLayoutInstant : false, // needed?
35223 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35225 isResizingContainer : true,
35227 * @cfg {Number} columnWidth width of the columns
35233 * @cfg {Number} maxCols maximum number of columns
35238 * @cfg {Number} padHeight padding below box..
35244 * @cfg {Boolean} isAutoInitial defalut true
35247 isAutoInitial : true,
35253 initialColumnWidth : 0,
35254 currentSize : null,
35256 colYs : null, // array.
35263 bricks: null, //CompositeElement
35264 cols : 0, // array?
35265 // element : null, // wrapped now this.el
35266 _isLayoutInited : null,
35269 getAutoCreate : function(){
35273 cls: 'blog-masonary-wrapper ' + this.cls,
35275 cls : 'mas-boxes masonary'
35282 getChildContainer: function( )
35284 if (this.boxesEl) {
35285 return this.boxesEl;
35288 this.boxesEl = this.el.select('.mas-boxes').first();
35290 return this.boxesEl;
35294 initEvents : function()
35298 if(this.isAutoInitial){
35299 Roo.log('hook children rendered');
35300 this.on('childrenrendered', function() {
35301 Roo.log('children rendered');
35308 initial : function()
35310 this.reloadItems();
35312 this.currentSize = this.el.getBox(true);
35314 /// was window resize... - let's see if this works..
35315 Roo.EventManager.onWindowResize(this.resize, this);
35317 if(!this.isAutoInitial){
35322 this.layout.defer(500,this);
35325 reloadItems: function()
35327 this.bricks = this.el.select('.masonry-brick', true);
35329 this.bricks.each(function(b) {
35330 //Roo.log(b.getSize());
35331 if (!b.attr('originalwidth')) {
35332 b.attr('originalwidth', b.getSize().width);
35337 Roo.log(this.bricks.elements.length);
35340 resize : function()
35343 var cs = this.el.getBox(true);
35345 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35346 Roo.log("no change in with or X");
35349 this.currentSize = cs;
35353 layout : function()
35356 this._resetLayout();
35357 //this._manageStamps();
35359 // don't animate first layout
35360 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35361 this.layoutItems( isInstant );
35363 // flag for initalized
35364 this._isLayoutInited = true;
35367 layoutItems : function( isInstant )
35369 //var items = this._getItemsForLayout( this.items );
35370 // original code supports filtering layout items.. we just ignore it..
35372 this._layoutItems( this.bricks , isInstant );
35374 this._postLayout();
35376 _layoutItems : function ( items , isInstant)
35378 //this.fireEvent( 'layout', this, items );
35381 if ( !items || !items.elements.length ) {
35382 // no items, emit event with empty array
35387 items.each(function(item) {
35388 Roo.log("layout item");
35390 // get x/y object from method
35391 var position = this._getItemLayoutPosition( item );
35393 position.item = item;
35394 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35395 queue.push( position );
35398 this._processLayoutQueue( queue );
35400 /** Sets position of item in DOM
35401 * @param {Element} item
35402 * @param {Number} x - horizontal position
35403 * @param {Number} y - vertical position
35404 * @param {Boolean} isInstant - disables transitions
35406 _processLayoutQueue : function( queue )
35408 for ( var i=0, len = queue.length; i < len; i++ ) {
35409 var obj = queue[i];
35410 obj.item.position('absolute');
35411 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35417 * Any logic you want to do after each layout,
35418 * i.e. size the container
35420 _postLayout : function()
35422 this.resizeContainer();
35425 resizeContainer : function()
35427 if ( !this.isResizingContainer ) {
35430 var size = this._getContainerSize();
35432 this.el.setSize(size.width,size.height);
35433 this.boxesEl.setSize(size.width,size.height);
35439 _resetLayout : function()
35441 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35442 this.colWidth = this.el.getWidth();
35443 //this.gutter = this.el.getWidth();
35445 this.measureColumns();
35451 this.colYs.push( 0 );
35457 measureColumns : function()
35459 this.getContainerWidth();
35460 // if columnWidth is 0, default to outerWidth of first item
35461 if ( !this.columnWidth ) {
35462 var firstItem = this.bricks.first();
35463 Roo.log(firstItem);
35464 this.columnWidth = this.containerWidth;
35465 if (firstItem && firstItem.attr('originalwidth') ) {
35466 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35468 // columnWidth fall back to item of first element
35469 Roo.log("set column width?");
35470 this.initialColumnWidth = this.columnWidth ;
35472 // if first elem has no width, default to size of container
35477 if (this.initialColumnWidth) {
35478 this.columnWidth = this.initialColumnWidth;
35483 // column width is fixed at the top - however if container width get's smaller we should
35486 // this bit calcs how man columns..
35488 var columnWidth = this.columnWidth += this.gutter;
35490 // calculate columns
35491 var containerWidth = this.containerWidth + this.gutter;
35493 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35494 // fix rounding errors, typically with gutters
35495 var excess = columnWidth - containerWidth % columnWidth;
35498 // if overshoot is less than a pixel, round up, otherwise floor it
35499 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35500 cols = Math[ mathMethod ]( cols );
35501 this.cols = Math.max( cols, 1 );
35502 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35504 // padding positioning..
35505 var totalColWidth = this.cols * this.columnWidth;
35506 var padavail = this.containerWidth - totalColWidth;
35507 // so for 2 columns - we need 3 'pads'
35509 var padNeeded = (1+this.cols) * this.padWidth;
35511 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35513 this.columnWidth += padExtra
35514 //this.padWidth = Math.floor(padavail / ( this.cols));
35516 // adjust colum width so that padding is fixed??
35518 // we have 3 columns ... total = width * 3
35519 // we have X left over... that should be used by
35521 //if (this.expandC) {
35529 getContainerWidth : function()
35531 /* // container is parent if fit width
35532 var container = this.isFitWidth ? this.element.parentNode : this.element;
35533 // check that this.size and size are there
35534 // IE8 triggers resize on body size change, so they might not be
35536 var size = getSize( container ); //FIXME
35537 this.containerWidth = size && size.innerWidth; //FIXME
35540 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35544 _getItemLayoutPosition : function( item ) // what is item?
35546 // we resize the item to our columnWidth..
35548 item.setWidth(this.columnWidth);
35549 item.autoBoxAdjust = false;
35551 var sz = item.getSize();
35553 // how many columns does this brick span
35554 var remainder = this.containerWidth % this.columnWidth;
35556 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35557 // round if off by 1 pixel, otherwise use ceil
35558 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35559 colSpan = Math.min( colSpan, this.cols );
35561 // normally this should be '1' as we dont' currently allow multi width columns..
35563 var colGroup = this._getColGroup( colSpan );
35564 // get the minimum Y value from the columns
35565 var minimumY = Math.min.apply( Math, colGroup );
35566 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35568 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35570 // position the brick
35572 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35573 y: this.currentSize.y + minimumY + this.padHeight
35577 // apply setHeight to necessary columns
35578 var setHeight = minimumY + sz.height + this.padHeight;
35579 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35581 var setSpan = this.cols + 1 - colGroup.length;
35582 for ( var i = 0; i < setSpan; i++ ) {
35583 this.colYs[ shortColIndex + i ] = setHeight ;
35590 * @param {Number} colSpan - number of columns the element spans
35591 * @returns {Array} colGroup
35593 _getColGroup : function( colSpan )
35595 if ( colSpan < 2 ) {
35596 // if brick spans only one column, use all the column Ys
35601 // how many different places could this brick fit horizontally
35602 var groupCount = this.cols + 1 - colSpan;
35603 // for each group potential horizontal position
35604 for ( var i = 0; i < groupCount; i++ ) {
35605 // make an array of colY values for that one group
35606 var groupColYs = this.colYs.slice( i, i + colSpan );
35607 // and get the max value of the array
35608 colGroup[i] = Math.max.apply( Math, groupColYs );
35613 _manageStamp : function( stamp )
35615 var stampSize = stamp.getSize();
35616 var offset = stamp.getBox();
35617 // get the columns that this stamp affects
35618 var firstX = this.isOriginLeft ? offset.x : offset.right;
35619 var lastX = firstX + stampSize.width;
35620 var firstCol = Math.floor( firstX / this.columnWidth );
35621 firstCol = Math.max( 0, firstCol );
35623 var lastCol = Math.floor( lastX / this.columnWidth );
35624 // lastCol should not go over if multiple of columnWidth #425
35625 lastCol -= lastX % this.columnWidth ? 0 : 1;
35626 lastCol = Math.min( this.cols - 1, lastCol );
35628 // set colYs to bottom of the stamp
35629 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35632 for ( var i = firstCol; i <= lastCol; i++ ) {
35633 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35638 _getContainerSize : function()
35640 this.maxY = Math.max.apply( Math, this.colYs );
35645 if ( this.isFitWidth ) {
35646 size.width = this._getContainerFitWidth();
35652 _getContainerFitWidth : function()
35654 var unusedCols = 0;
35655 // count unused columns
35658 if ( this.colYs[i] !== 0 ) {
35663 // fit container to columns that have been used
35664 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35667 needsResizeLayout : function()
35669 var previousWidth = this.containerWidth;
35670 this.getContainerWidth();
35671 return previousWidth !== this.containerWidth;
35686 * @class Roo.bootstrap.MasonryBrick
35687 * @extends Roo.bootstrap.Component
35688 * Bootstrap MasonryBrick class
35691 * Create a new MasonryBrick
35692 * @param {Object} config The config object
35695 Roo.bootstrap.MasonryBrick = function(config){
35697 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35699 Roo.bootstrap.MasonryBrick.register(this);
35705 * When a MasonryBrick is clcik
35706 * @param {Roo.bootstrap.MasonryBrick} this
35707 * @param {Roo.EventObject} e
35713 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35716 * @cfg {String} title
35720 * @cfg {String} html
35724 * @cfg {String} bgimage
35728 * @cfg {String} videourl
35732 * @cfg {String} cls
35736 * @cfg {String} href
35740 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35745 * @cfg {String} placetitle (center|bottom)
35750 * @cfg {Boolean} isFitContainer defalut true
35752 isFitContainer : true,
35755 * @cfg {Boolean} preventDefault defalut false
35757 preventDefault : false,
35760 * @cfg {Boolean} inverse defalut false
35762 maskInverse : false,
35764 getAutoCreate : function()
35766 if(!this.isFitContainer){
35767 return this.getSplitAutoCreate();
35770 var cls = 'masonry-brick masonry-brick-full';
35772 if(this.href.length){
35773 cls += ' masonry-brick-link';
35776 if(this.bgimage.length){
35777 cls += ' masonry-brick-image';
35780 if(this.maskInverse){
35781 cls += ' mask-inverse';
35784 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35785 cls += ' enable-mask';
35789 cls += ' masonry-' + this.size + '-brick';
35792 if(this.placetitle.length){
35794 switch (this.placetitle) {
35796 cls += ' masonry-center-title';
35799 cls += ' masonry-bottom-title';
35806 if(!this.html.length && !this.bgimage.length){
35807 cls += ' masonry-center-title';
35810 if(!this.html.length && this.bgimage.length){
35811 cls += ' masonry-bottom-title';
35816 cls += ' ' + this.cls;
35820 tag: (this.href.length) ? 'a' : 'div',
35825 cls: 'masonry-brick-mask'
35829 cls: 'masonry-brick-paragraph',
35835 if(this.href.length){
35836 cfg.href = this.href;
35839 var cn = cfg.cn[1].cn;
35841 if(this.title.length){
35844 cls: 'masonry-brick-title',
35849 if(this.html.length){
35852 cls: 'masonry-brick-text',
35857 if (!this.title.length && !this.html.length) {
35858 cfg.cn[1].cls += ' hide';
35861 if(this.bgimage.length){
35864 cls: 'masonry-brick-image-view',
35869 if(this.videourl.length){
35870 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35871 // youtube support only?
35874 cls: 'masonry-brick-image-view',
35877 allowfullscreen : true
35885 getSplitAutoCreate : function()
35887 var cls = 'masonry-brick masonry-brick-split';
35889 if(this.href.length){
35890 cls += ' masonry-brick-link';
35893 if(this.bgimage.length){
35894 cls += ' masonry-brick-image';
35898 cls += ' masonry-' + this.size + '-brick';
35901 switch (this.placetitle) {
35903 cls += ' masonry-center-title';
35906 cls += ' masonry-bottom-title';
35909 if(!this.bgimage.length){
35910 cls += ' masonry-center-title';
35913 if(this.bgimage.length){
35914 cls += ' masonry-bottom-title';
35920 cls += ' ' + this.cls;
35924 tag: (this.href.length) ? 'a' : 'div',
35929 cls: 'masonry-brick-split-head',
35933 cls: 'masonry-brick-paragraph',
35940 cls: 'masonry-brick-split-body',
35946 if(this.href.length){
35947 cfg.href = this.href;
35950 if(this.title.length){
35951 cfg.cn[0].cn[0].cn.push({
35953 cls: 'masonry-brick-title',
35958 if(this.html.length){
35959 cfg.cn[1].cn.push({
35961 cls: 'masonry-brick-text',
35966 if(this.bgimage.length){
35967 cfg.cn[0].cn.push({
35969 cls: 'masonry-brick-image-view',
35974 if(this.videourl.length){
35975 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35976 // youtube support only?
35977 cfg.cn[0].cn.cn.push({
35979 cls: 'masonry-brick-image-view',
35982 allowfullscreen : true
35989 initEvents: function()
35991 switch (this.size) {
36024 this.el.on('touchstart', this.onTouchStart, this);
36025 this.el.on('touchmove', this.onTouchMove, this);
36026 this.el.on('touchend', this.onTouchEnd, this);
36027 this.el.on('contextmenu', this.onContextMenu, this);
36029 this.el.on('mouseenter' ,this.enter, this);
36030 this.el.on('mouseleave', this.leave, this);
36031 this.el.on('click', this.onClick, this);
36034 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36035 this.parent().bricks.push(this);
36040 onClick: function(e, el)
36042 var time = this.endTimer - this.startTimer;
36043 // Roo.log(e.preventDefault());
36046 e.preventDefault();
36051 if(!this.preventDefault){
36055 e.preventDefault();
36057 if (this.activeClass != '') {
36058 this.selectBrick();
36061 this.fireEvent('click', this, e);
36064 enter: function(e, el)
36066 e.preventDefault();
36068 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36072 if(this.bgimage.length && this.html.length){
36073 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36077 leave: function(e, el)
36079 e.preventDefault();
36081 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36085 if(this.bgimage.length && this.html.length){
36086 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36090 onTouchStart: function(e, el)
36092 // e.preventDefault();
36094 this.touchmoved = false;
36096 if(!this.isFitContainer){
36100 if(!this.bgimage.length || !this.html.length){
36104 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36106 this.timer = new Date().getTime();
36110 onTouchMove: function(e, el)
36112 this.touchmoved = true;
36115 onContextMenu : function(e,el)
36117 e.preventDefault();
36118 e.stopPropagation();
36122 onTouchEnd: function(e, el)
36124 // e.preventDefault();
36126 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36133 if(!this.bgimage.length || !this.html.length){
36135 if(this.href.length){
36136 window.location.href = this.href;
36142 if(!this.isFitContainer){
36146 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36148 window.location.href = this.href;
36151 //selection on single brick only
36152 selectBrick : function() {
36154 if (!this.parentId) {
36158 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36159 var index = m.selectedBrick.indexOf(this.id);
36162 m.selectedBrick.splice(index,1);
36163 this.el.removeClass(this.activeClass);
36167 for(var i = 0; i < m.selectedBrick.length; i++) {
36168 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36169 b.el.removeClass(b.activeClass);
36172 m.selectedBrick = [];
36174 m.selectedBrick.push(this.id);
36175 this.el.addClass(this.activeClass);
36179 isSelected : function(){
36180 return this.el.hasClass(this.activeClass);
36185 Roo.apply(Roo.bootstrap.MasonryBrick, {
36188 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36190 * register a Masonry Brick
36191 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36194 register : function(brick)
36196 //this.groups[brick.id] = brick;
36197 this.groups.add(brick.id, brick);
36200 * fetch a masonry brick based on the masonry brick ID
36201 * @param {string} the masonry brick to add
36202 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36205 get: function(brick_id)
36207 // if (typeof(this.groups[brick_id]) == 'undefined') {
36210 // return this.groups[brick_id] ;
36212 if(this.groups.key(brick_id)) {
36213 return this.groups.key(brick_id);
36231 * @class Roo.bootstrap.Brick
36232 * @extends Roo.bootstrap.Component
36233 * Bootstrap Brick class
36236 * Create a new Brick
36237 * @param {Object} config The config object
36240 Roo.bootstrap.Brick = function(config){
36241 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36247 * When a Brick is click
36248 * @param {Roo.bootstrap.Brick} this
36249 * @param {Roo.EventObject} e
36255 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36258 * @cfg {String} title
36262 * @cfg {String} html
36266 * @cfg {String} bgimage
36270 * @cfg {String} cls
36274 * @cfg {String} href
36278 * @cfg {String} video
36282 * @cfg {Boolean} square
36286 getAutoCreate : function()
36288 var cls = 'roo-brick';
36290 if(this.href.length){
36291 cls += ' roo-brick-link';
36294 if(this.bgimage.length){
36295 cls += ' roo-brick-image';
36298 if(!this.html.length && !this.bgimage.length){
36299 cls += ' roo-brick-center-title';
36302 if(!this.html.length && this.bgimage.length){
36303 cls += ' roo-brick-bottom-title';
36307 cls += ' ' + this.cls;
36311 tag: (this.href.length) ? 'a' : 'div',
36316 cls: 'roo-brick-paragraph',
36322 if(this.href.length){
36323 cfg.href = this.href;
36326 var cn = cfg.cn[0].cn;
36328 if(this.title.length){
36331 cls: 'roo-brick-title',
36336 if(this.html.length){
36339 cls: 'roo-brick-text',
36346 if(this.bgimage.length){
36349 cls: 'roo-brick-image-view',
36357 initEvents: function()
36359 if(this.title.length || this.html.length){
36360 this.el.on('mouseenter' ,this.enter, this);
36361 this.el.on('mouseleave', this.leave, this);
36364 Roo.EventManager.onWindowResize(this.resize, this);
36366 if(this.bgimage.length){
36367 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36368 this.imageEl.on('load', this.onImageLoad, this);
36375 onImageLoad : function()
36380 resize : function()
36382 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36384 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36386 if(this.bgimage.length){
36387 var image = this.el.select('.roo-brick-image-view', true).first();
36389 image.setWidth(paragraph.getWidth());
36392 image.setHeight(paragraph.getWidth());
36395 this.el.setHeight(image.getHeight());
36396 paragraph.setHeight(image.getHeight());
36402 enter: function(e, el)
36404 e.preventDefault();
36406 if(this.bgimage.length){
36407 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36408 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36412 leave: function(e, el)
36414 e.preventDefault();
36416 if(this.bgimage.length){
36417 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36418 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36433 * @class Roo.bootstrap.NumberField
36434 * @extends Roo.bootstrap.Input
36435 * Bootstrap NumberField class
36441 * Create a new NumberField
36442 * @param {Object} config The config object
36445 Roo.bootstrap.NumberField = function(config){
36446 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36449 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36452 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36454 allowDecimals : true,
36456 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36458 decimalSeparator : ".",
36460 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36462 decimalPrecision : 2,
36464 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36466 allowNegative : true,
36469 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36473 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36475 minValue : Number.NEGATIVE_INFINITY,
36477 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36479 maxValue : Number.MAX_VALUE,
36481 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36483 minText : "The minimum value for this field is {0}",
36485 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36487 maxText : "The maximum value for this field is {0}",
36489 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36490 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36492 nanText : "{0} is not a valid number",
36494 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36496 thousandsDelimiter : false,
36498 * @cfg {String} valueAlign alignment of value
36500 valueAlign : "left",
36502 getAutoCreate : function()
36504 var hiddenInput = {
36508 cls: 'hidden-number-input'
36512 hiddenInput.name = this.name;
36517 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36519 this.name = hiddenInput.name;
36521 if(cfg.cn.length > 0) {
36522 cfg.cn.push(hiddenInput);
36529 initEvents : function()
36531 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36533 var allowed = "0123456789";
36535 if(this.allowDecimals){
36536 allowed += this.decimalSeparator;
36539 if(this.allowNegative){
36543 if(this.thousandsDelimiter) {
36547 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36549 var keyPress = function(e){
36551 var k = e.getKey();
36553 var c = e.getCharCode();
36556 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36557 allowed.indexOf(String.fromCharCode(c)) === -1
36563 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36567 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36572 this.el.on("keypress", keyPress, this);
36575 validateValue : function(value)
36578 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36582 var num = this.parseValue(value);
36585 this.markInvalid(String.format(this.nanText, value));
36589 if(num < this.minValue){
36590 this.markInvalid(String.format(this.minText, this.minValue));
36594 if(num > this.maxValue){
36595 this.markInvalid(String.format(this.maxText, this.maxValue));
36602 getValue : function()
36604 var v = this.hiddenEl().getValue();
36606 return this.fixPrecision(this.parseValue(v));
36609 parseValue : function(value)
36611 if(this.thousandsDelimiter) {
36613 r = new RegExp(",", "g");
36614 value = value.replace(r, "");
36617 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36618 return isNaN(value) ? '' : value;
36621 fixPrecision : function(value)
36623 if(this.thousandsDelimiter) {
36625 r = new RegExp(",", "g");
36626 value = value.replace(r, "");
36629 var nan = isNaN(value);
36631 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36632 return nan ? '' : value;
36634 return parseFloat(value).toFixed(this.decimalPrecision);
36637 setValue : function(v)
36639 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36645 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36647 this.inputEl().dom.value = (v == '') ? '' :
36648 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36650 if(!this.allowZero && v === '0') {
36651 this.hiddenEl().dom.value = '';
36652 this.inputEl().dom.value = '';
36659 decimalPrecisionFcn : function(v)
36661 return Math.floor(v);
36664 beforeBlur : function()
36666 var v = this.parseValue(this.getRawValue());
36668 if(v || v === 0 || v === ''){
36673 hiddenEl : function()
36675 return this.el.select('input.hidden-number-input',true).first();
36687 * @class Roo.bootstrap.DocumentSlider
36688 * @extends Roo.bootstrap.Component
36689 * Bootstrap DocumentSlider class
36692 * Create a new DocumentViewer
36693 * @param {Object} config The config object
36696 Roo.bootstrap.DocumentSlider = function(config){
36697 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36704 * Fire after initEvent
36705 * @param {Roo.bootstrap.DocumentSlider} this
36710 * Fire after update
36711 * @param {Roo.bootstrap.DocumentSlider} this
36717 * @param {Roo.bootstrap.DocumentSlider} this
36723 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36729 getAutoCreate : function()
36733 cls : 'roo-document-slider',
36737 cls : 'roo-document-slider-header',
36741 cls : 'roo-document-slider-header-title'
36747 cls : 'roo-document-slider-body',
36751 cls : 'roo-document-slider-prev',
36755 cls : 'fa fa-chevron-left'
36761 cls : 'roo-document-slider-thumb',
36765 cls : 'roo-document-slider-image'
36771 cls : 'roo-document-slider-next',
36775 cls : 'fa fa-chevron-right'
36787 initEvents : function()
36789 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36790 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36792 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36793 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36795 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36796 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36798 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36799 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36801 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36802 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36804 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36805 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36807 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36808 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36810 this.thumbEl.on('click', this.onClick, this);
36812 this.prevIndicator.on('click', this.prev, this);
36814 this.nextIndicator.on('click', this.next, this);
36818 initial : function()
36820 if(this.files.length){
36821 this.indicator = 1;
36825 this.fireEvent('initial', this);
36828 update : function()
36830 this.imageEl.attr('src', this.files[this.indicator - 1]);
36832 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36834 this.prevIndicator.show();
36836 if(this.indicator == 1){
36837 this.prevIndicator.hide();
36840 this.nextIndicator.show();
36842 if(this.indicator == this.files.length){
36843 this.nextIndicator.hide();
36846 this.thumbEl.scrollTo('top');
36848 this.fireEvent('update', this);
36851 onClick : function(e)
36853 e.preventDefault();
36855 this.fireEvent('click', this);
36860 e.preventDefault();
36862 this.indicator = Math.max(1, this.indicator - 1);
36869 e.preventDefault();
36871 this.indicator = Math.min(this.files.length, this.indicator + 1);
36885 * @class Roo.bootstrap.RadioSet
36886 * @extends Roo.bootstrap.Input
36887 * Bootstrap RadioSet class
36888 * @cfg {String} indicatorpos (left|right) default left
36889 * @cfg {Boolean} inline (true|false) inline the element (default true)
36890 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36892 * Create a new RadioSet
36893 * @param {Object} config The config object
36896 Roo.bootstrap.RadioSet = function(config){
36898 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36902 Roo.bootstrap.RadioSet.register(this);
36907 * Fires when the element is checked or unchecked.
36908 * @param {Roo.bootstrap.RadioSet} this This radio
36909 * @param {Roo.bootstrap.Radio} item The checked item
36914 * Fires when the element is click.
36915 * @param {Roo.bootstrap.RadioSet} this This radio set
36916 * @param {Roo.bootstrap.Radio} item The checked item
36917 * @param {Roo.EventObject} e The event object
36924 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36932 indicatorpos : 'left',
36934 getAutoCreate : function()
36938 cls : 'roo-radio-set-label',
36942 html : this.fieldLabel
36946 if (Roo.bootstrap.version == 3) {
36949 if(this.indicatorpos == 'left'){
36952 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36953 tooltip : 'This field is required'
36958 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36959 tooltip : 'This field is required'
36965 cls : 'roo-radio-set-items'
36968 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36970 if (align === 'left' && this.fieldLabel.length) {
36973 cls : "roo-radio-set-right",
36979 if(this.labelWidth > 12){
36980 label.style = "width: " + this.labelWidth + 'px';
36983 if(this.labelWidth < 13 && this.labelmd == 0){
36984 this.labelmd = this.labelWidth;
36987 if(this.labellg > 0){
36988 label.cls += ' col-lg-' + this.labellg;
36989 items.cls += ' col-lg-' + (12 - this.labellg);
36992 if(this.labelmd > 0){
36993 label.cls += ' col-md-' + this.labelmd;
36994 items.cls += ' col-md-' + (12 - this.labelmd);
36997 if(this.labelsm > 0){
36998 label.cls += ' col-sm-' + this.labelsm;
36999 items.cls += ' col-sm-' + (12 - this.labelsm);
37002 if(this.labelxs > 0){
37003 label.cls += ' col-xs-' + this.labelxs;
37004 items.cls += ' col-xs-' + (12 - this.labelxs);
37010 cls : 'roo-radio-set',
37014 cls : 'roo-radio-set-input',
37017 value : this.value ? this.value : ''
37024 if(this.weight.length){
37025 cfg.cls += ' roo-radio-' + this.weight;
37029 cfg.cls += ' roo-radio-set-inline';
37033 ['xs','sm','md','lg'].map(function(size){
37034 if (settings[size]) {
37035 cfg.cls += ' col-' + size + '-' + settings[size];
37043 initEvents : function()
37045 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37046 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37048 if(!this.fieldLabel.length){
37049 this.labelEl.hide();
37052 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37053 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37055 this.indicator = this.indicatorEl();
37057 if(this.indicator){
37058 this.indicator.addClass('invisible');
37061 this.originalValue = this.getValue();
37065 inputEl: function ()
37067 return this.el.select('.roo-radio-set-input', true).first();
37070 getChildContainer : function()
37072 return this.itemsEl;
37075 register : function(item)
37077 this.radioes.push(item);
37081 validate : function()
37083 if(this.getVisibilityEl().hasClass('hidden')){
37089 Roo.each(this.radioes, function(i){
37098 if(this.allowBlank) {
37102 if(this.disabled || valid){
37107 this.markInvalid();
37112 markValid : function()
37114 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37115 this.indicatorEl().removeClass('visible');
37116 this.indicatorEl().addClass('invisible');
37120 if (Roo.bootstrap.version == 3) {
37121 this.el.removeClass([this.invalidClass, this.validClass]);
37122 this.el.addClass(this.validClass);
37124 this.el.removeClass(['is-invalid','is-valid']);
37125 this.el.addClass(['is-valid']);
37127 this.fireEvent('valid', this);
37130 markInvalid : function(msg)
37132 if(this.allowBlank || this.disabled){
37136 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37137 this.indicatorEl().removeClass('invisible');
37138 this.indicatorEl().addClass('visible');
37140 if (Roo.bootstrap.version == 3) {
37141 this.el.removeClass([this.invalidClass, this.validClass]);
37142 this.el.addClass(this.invalidClass);
37144 this.el.removeClass(['is-invalid','is-valid']);
37145 this.el.addClass(['is-invalid']);
37148 this.fireEvent('invalid', this, msg);
37152 setValue : function(v, suppressEvent)
37154 if(this.value === v){
37161 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37164 Roo.each(this.radioes, function(i){
37166 i.el.removeClass('checked');
37169 Roo.each(this.radioes, function(i){
37171 if(i.value === v || i.value.toString() === v.toString()){
37173 i.el.addClass('checked');
37175 if(suppressEvent !== true){
37176 this.fireEvent('check', this, i);
37187 clearInvalid : function(){
37189 if(!this.el || this.preventMark){
37193 this.el.removeClass([this.invalidClass]);
37195 this.fireEvent('valid', this);
37200 Roo.apply(Roo.bootstrap.RadioSet, {
37204 register : function(set)
37206 this.groups[set.name] = set;
37209 get: function(name)
37211 if (typeof(this.groups[name]) == 'undefined') {
37215 return this.groups[name] ;
37221 * Ext JS Library 1.1.1
37222 * Copyright(c) 2006-2007, Ext JS, LLC.
37224 * Originally Released Under LGPL - original licence link has changed is not relivant.
37227 * <script type="text/javascript">
37232 * @class Roo.bootstrap.SplitBar
37233 * @extends Roo.util.Observable
37234 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37238 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37239 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37240 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37241 split.minSize = 100;
37242 split.maxSize = 600;
37243 split.animate = true;
37244 split.on('moved', splitterMoved);
37247 * Create a new SplitBar
37248 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37249 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37250 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37251 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37252 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37253 position of the SplitBar).
37255 Roo.bootstrap.SplitBar = function(cfg){
37260 // dragElement : elm
37261 // resizingElement: el,
37263 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37264 // placement : Roo.bootstrap.SplitBar.LEFT ,
37265 // existingProxy ???
37268 this.el = Roo.get(cfg.dragElement, true);
37269 this.el.dom.unselectable = "on";
37271 this.resizingEl = Roo.get(cfg.resizingElement, true);
37275 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37276 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37279 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37282 * The minimum size of the resizing element. (Defaults to 0)
37288 * The maximum size of the resizing element. (Defaults to 2000)
37291 this.maxSize = 2000;
37294 * Whether to animate the transition to the new size
37297 this.animate = false;
37300 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37303 this.useShim = false;
37308 if(!cfg.existingProxy){
37310 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37312 this.proxy = Roo.get(cfg.existingProxy).dom;
37315 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37318 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37321 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37324 this.dragSpecs = {};
37327 * @private The adapter to use to positon and resize elements
37329 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37330 this.adapter.init(this);
37332 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37334 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37335 this.el.addClass("roo-splitbar-h");
37338 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37339 this.el.addClass("roo-splitbar-v");
37345 * Fires when the splitter is moved (alias for {@link #event-moved})
37346 * @param {Roo.bootstrap.SplitBar} this
37347 * @param {Number} newSize the new width or height
37352 * Fires when the splitter is moved
37353 * @param {Roo.bootstrap.SplitBar} this
37354 * @param {Number} newSize the new width or height
37358 * @event beforeresize
37359 * Fires before the splitter is dragged
37360 * @param {Roo.bootstrap.SplitBar} this
37362 "beforeresize" : true,
37364 "beforeapply" : true
37367 Roo.util.Observable.call(this);
37370 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37371 onStartProxyDrag : function(x, y){
37372 this.fireEvent("beforeresize", this);
37374 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37376 o.enableDisplayMode("block");
37377 // all splitbars share the same overlay
37378 Roo.bootstrap.SplitBar.prototype.overlay = o;
37380 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37381 this.overlay.show();
37382 Roo.get(this.proxy).setDisplayed("block");
37383 var size = this.adapter.getElementSize(this);
37384 this.activeMinSize = this.getMinimumSize();;
37385 this.activeMaxSize = this.getMaximumSize();;
37386 var c1 = size - this.activeMinSize;
37387 var c2 = Math.max(this.activeMaxSize - size, 0);
37388 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37389 this.dd.resetConstraints();
37390 this.dd.setXConstraint(
37391 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37392 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37394 this.dd.setYConstraint(0, 0);
37396 this.dd.resetConstraints();
37397 this.dd.setXConstraint(0, 0);
37398 this.dd.setYConstraint(
37399 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37400 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37403 this.dragSpecs.startSize = size;
37404 this.dragSpecs.startPoint = [x, y];
37405 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37409 * @private Called after the drag operation by the DDProxy
37411 onEndProxyDrag : function(e){
37412 Roo.get(this.proxy).setDisplayed(false);
37413 var endPoint = Roo.lib.Event.getXY(e);
37415 this.overlay.hide();
37418 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37419 newSize = this.dragSpecs.startSize +
37420 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37421 endPoint[0] - this.dragSpecs.startPoint[0] :
37422 this.dragSpecs.startPoint[0] - endPoint[0]
37425 newSize = this.dragSpecs.startSize +
37426 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37427 endPoint[1] - this.dragSpecs.startPoint[1] :
37428 this.dragSpecs.startPoint[1] - endPoint[1]
37431 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37432 if(newSize != this.dragSpecs.startSize){
37433 if(this.fireEvent('beforeapply', this, newSize) !== false){
37434 this.adapter.setElementSize(this, newSize);
37435 this.fireEvent("moved", this, newSize);
37436 this.fireEvent("resize", this, newSize);
37442 * Get the adapter this SplitBar uses
37443 * @return The adapter object
37445 getAdapter : function(){
37446 return this.adapter;
37450 * Set the adapter this SplitBar uses
37451 * @param {Object} adapter A SplitBar adapter object
37453 setAdapter : function(adapter){
37454 this.adapter = adapter;
37455 this.adapter.init(this);
37459 * Gets the minimum size for the resizing element
37460 * @return {Number} The minimum size
37462 getMinimumSize : function(){
37463 return this.minSize;
37467 * Sets the minimum size for the resizing element
37468 * @param {Number} minSize The minimum size
37470 setMinimumSize : function(minSize){
37471 this.minSize = minSize;
37475 * Gets the maximum size for the resizing element
37476 * @return {Number} The maximum size
37478 getMaximumSize : function(){
37479 return this.maxSize;
37483 * Sets the maximum size for the resizing element
37484 * @param {Number} maxSize The maximum size
37486 setMaximumSize : function(maxSize){
37487 this.maxSize = maxSize;
37491 * Sets the initialize size for the resizing element
37492 * @param {Number} size The initial size
37494 setCurrentSize : function(size){
37495 var oldAnimate = this.animate;
37496 this.animate = false;
37497 this.adapter.setElementSize(this, size);
37498 this.animate = oldAnimate;
37502 * Destroy this splitbar.
37503 * @param {Boolean} removeEl True to remove the element
37505 destroy : function(removeEl){
37507 this.shim.remove();
37510 this.proxy.parentNode.removeChild(this.proxy);
37518 * @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.
37520 Roo.bootstrap.SplitBar.createProxy = function(dir){
37521 var proxy = new Roo.Element(document.createElement("div"));
37522 proxy.unselectable();
37523 var cls = 'roo-splitbar-proxy';
37524 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37525 document.body.appendChild(proxy.dom);
37530 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37531 * Default Adapter. It assumes the splitter and resizing element are not positioned
37532 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37534 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37537 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37538 // do nothing for now
37539 init : function(s){
37543 * Called before drag operations to get the current size of the resizing element.
37544 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37546 getElementSize : function(s){
37547 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37548 return s.resizingEl.getWidth();
37550 return s.resizingEl.getHeight();
37555 * Called after drag operations to set the size of the resizing element.
37556 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37557 * @param {Number} newSize The new size to set
37558 * @param {Function} onComplete A function to be invoked when resizing is complete
37560 setElementSize : function(s, newSize, onComplete){
37561 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37563 s.resizingEl.setWidth(newSize);
37565 onComplete(s, newSize);
37568 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37573 s.resizingEl.setHeight(newSize);
37575 onComplete(s, newSize);
37578 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37585 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37586 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37587 * Adapter that moves the splitter element to align with the resized sizing element.
37588 * Used with an absolute positioned SplitBar.
37589 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37590 * document.body, make sure you assign an id to the body element.
37592 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37593 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37594 this.container = Roo.get(container);
37597 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37598 init : function(s){
37599 this.basic.init(s);
37602 getElementSize : function(s){
37603 return this.basic.getElementSize(s);
37606 setElementSize : function(s, newSize, onComplete){
37607 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37610 moveSplitter : function(s){
37611 var yes = Roo.bootstrap.SplitBar;
37612 switch(s.placement){
37614 s.el.setX(s.resizingEl.getRight());
37617 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37620 s.el.setY(s.resizingEl.getBottom());
37623 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37630 * Orientation constant - Create a vertical SplitBar
37634 Roo.bootstrap.SplitBar.VERTICAL = 1;
37637 * Orientation constant - Create a horizontal SplitBar
37641 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37644 * Placement constant - The resizing element is to the left of the splitter element
37648 Roo.bootstrap.SplitBar.LEFT = 1;
37651 * Placement constant - The resizing element is to the right of the splitter element
37655 Roo.bootstrap.SplitBar.RIGHT = 2;
37658 * Placement constant - The resizing element is positioned above the splitter element
37662 Roo.bootstrap.SplitBar.TOP = 3;
37665 * Placement constant - The resizing element is positioned under splitter element
37669 Roo.bootstrap.SplitBar.BOTTOM = 4;
37670 Roo.namespace("Roo.bootstrap.layout");/*
37672 * Ext JS Library 1.1.1
37673 * Copyright(c) 2006-2007, Ext JS, LLC.
37675 * Originally Released Under LGPL - original licence link has changed is not relivant.
37678 * <script type="text/javascript">
37682 * @class Roo.bootstrap.layout.Manager
37683 * @extends Roo.bootstrap.Component
37684 * Base class for layout managers.
37686 Roo.bootstrap.layout.Manager = function(config)
37688 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37694 /** false to disable window resize monitoring @type Boolean */
37695 this.monitorWindowResize = true;
37700 * Fires when a layout is performed.
37701 * @param {Roo.LayoutManager} this
37705 * @event regionresized
37706 * Fires when the user resizes a region.
37707 * @param {Roo.LayoutRegion} region The resized region
37708 * @param {Number} newSize The new size (width for east/west, height for north/south)
37710 "regionresized" : true,
37712 * @event regioncollapsed
37713 * Fires when a region is collapsed.
37714 * @param {Roo.LayoutRegion} region The collapsed region
37716 "regioncollapsed" : true,
37718 * @event regionexpanded
37719 * Fires when a region is expanded.
37720 * @param {Roo.LayoutRegion} region The expanded region
37722 "regionexpanded" : true
37724 this.updating = false;
37727 this.el = Roo.get(config.el);
37733 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37738 monitorWindowResize : true,
37744 onRender : function(ct, position)
37747 this.el = Roo.get(ct);
37750 //this.fireEvent('render',this);
37754 initEvents: function()
37758 // ie scrollbar fix
37759 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37760 document.body.scroll = "no";
37761 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37762 this.el.position('relative');
37764 this.id = this.el.id;
37765 this.el.addClass("roo-layout-container");
37766 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37767 if(this.el.dom != document.body ) {
37768 this.el.on('resize', this.layout,this);
37769 this.el.on('show', this.layout,this);
37775 * Returns true if this layout is currently being updated
37776 * @return {Boolean}
37778 isUpdating : function(){
37779 return this.updating;
37783 * Suspend the LayoutManager from doing auto-layouts while
37784 * making multiple add or remove calls
37786 beginUpdate : function(){
37787 this.updating = true;
37791 * Restore auto-layouts and optionally disable the manager from performing a layout
37792 * @param {Boolean} noLayout true to disable a layout update
37794 endUpdate : function(noLayout){
37795 this.updating = false;
37801 layout: function(){
37805 onRegionResized : function(region, newSize){
37806 this.fireEvent("regionresized", region, newSize);
37810 onRegionCollapsed : function(region){
37811 this.fireEvent("regioncollapsed", region);
37814 onRegionExpanded : function(region){
37815 this.fireEvent("regionexpanded", region);
37819 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37820 * performs box-model adjustments.
37821 * @return {Object} The size as an object {width: (the width), height: (the height)}
37823 getViewSize : function()
37826 if(this.el.dom != document.body){
37827 size = this.el.getSize();
37829 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37831 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37832 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37837 * Returns the Element this layout is bound to.
37838 * @return {Roo.Element}
37840 getEl : function(){
37845 * Returns the specified region.
37846 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37847 * @return {Roo.LayoutRegion}
37849 getRegion : function(target){
37850 return this.regions[target.toLowerCase()];
37853 onWindowResize : function(){
37854 if(this.monitorWindowResize){
37861 * Ext JS Library 1.1.1
37862 * Copyright(c) 2006-2007, Ext JS, LLC.
37864 * Originally Released Under LGPL - original licence link has changed is not relivant.
37867 * <script type="text/javascript">
37870 * @class Roo.bootstrap.layout.Border
37871 * @extends Roo.bootstrap.layout.Manager
37872 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37873 * please see: examples/bootstrap/nested.html<br><br>
37875 <b>The container the layout is rendered into can be either the body element or any other element.
37876 If it is not the body element, the container needs to either be an absolute positioned element,
37877 or you will need to add "position:relative" to the css of the container. You will also need to specify
37878 the container size if it is not the body element.</b>
37881 * Create a new Border
37882 * @param {Object} config Configuration options
37884 Roo.bootstrap.layout.Border = function(config){
37885 config = config || {};
37886 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37890 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37891 if(config[region]){
37892 config[region].region = region;
37893 this.addRegion(config[region]);
37899 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37901 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37903 parent : false, // this might point to a 'nest' or a ???
37906 * Creates and adds a new region if it doesn't already exist.
37907 * @param {String} target The target region key (north, south, east, west or center).
37908 * @param {Object} config The regions config object
37909 * @return {BorderLayoutRegion} The new region
37911 addRegion : function(config)
37913 if(!this.regions[config.region]){
37914 var r = this.factory(config);
37915 this.bindRegion(r);
37917 return this.regions[config.region];
37921 bindRegion : function(r){
37922 this.regions[r.config.region] = r;
37924 r.on("visibilitychange", this.layout, this);
37925 r.on("paneladded", this.layout, this);
37926 r.on("panelremoved", this.layout, this);
37927 r.on("invalidated", this.layout, this);
37928 r.on("resized", this.onRegionResized, this);
37929 r.on("collapsed", this.onRegionCollapsed, this);
37930 r.on("expanded", this.onRegionExpanded, this);
37934 * Performs a layout update.
37936 layout : function()
37938 if(this.updating) {
37942 // render all the rebions if they have not been done alreayd?
37943 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37944 if(this.regions[region] && !this.regions[region].bodyEl){
37945 this.regions[region].onRender(this.el)
37949 var size = this.getViewSize();
37950 var w = size.width;
37951 var h = size.height;
37956 //var x = 0, y = 0;
37958 var rs = this.regions;
37959 var north = rs["north"];
37960 var south = rs["south"];
37961 var west = rs["west"];
37962 var east = rs["east"];
37963 var center = rs["center"];
37964 //if(this.hideOnLayout){ // not supported anymore
37965 //c.el.setStyle("display", "none");
37967 if(north && north.isVisible()){
37968 var b = north.getBox();
37969 var m = north.getMargins();
37970 b.width = w - (m.left+m.right);
37973 centerY = b.height + b.y + m.bottom;
37974 centerH -= centerY;
37975 north.updateBox(this.safeBox(b));
37977 if(south && south.isVisible()){
37978 var b = south.getBox();
37979 var m = south.getMargins();
37980 b.width = w - (m.left+m.right);
37982 var totalHeight = (b.height + m.top + m.bottom);
37983 b.y = h - totalHeight + m.top;
37984 centerH -= totalHeight;
37985 south.updateBox(this.safeBox(b));
37987 if(west && west.isVisible()){
37988 var b = west.getBox();
37989 var m = west.getMargins();
37990 b.height = centerH - (m.top+m.bottom);
37992 b.y = centerY + m.top;
37993 var totalWidth = (b.width + m.left + m.right);
37994 centerX += totalWidth;
37995 centerW -= totalWidth;
37996 west.updateBox(this.safeBox(b));
37998 if(east && east.isVisible()){
37999 var b = east.getBox();
38000 var m = east.getMargins();
38001 b.height = centerH - (m.top+m.bottom);
38002 var totalWidth = (b.width + m.left + m.right);
38003 b.x = w - totalWidth + m.left;
38004 b.y = centerY + m.top;
38005 centerW -= totalWidth;
38006 east.updateBox(this.safeBox(b));
38009 var m = center.getMargins();
38011 x: centerX + m.left,
38012 y: centerY + m.top,
38013 width: centerW - (m.left+m.right),
38014 height: centerH - (m.top+m.bottom)
38016 //if(this.hideOnLayout){
38017 //center.el.setStyle("display", "block");
38019 center.updateBox(this.safeBox(centerBox));
38022 this.fireEvent("layout", this);
38026 safeBox : function(box){
38027 box.width = Math.max(0, box.width);
38028 box.height = Math.max(0, box.height);
38033 * Adds a ContentPanel (or subclass) to this layout.
38034 * @param {String} target The target region key (north, south, east, west or center).
38035 * @param {Roo.ContentPanel} panel The panel to add
38036 * @return {Roo.ContentPanel} The added panel
38038 add : function(target, panel){
38040 target = target.toLowerCase();
38041 return this.regions[target].add(panel);
38045 * Remove a ContentPanel (or subclass) to this layout.
38046 * @param {String} target The target region key (north, south, east, west or center).
38047 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38048 * @return {Roo.ContentPanel} The removed panel
38050 remove : function(target, panel){
38051 target = target.toLowerCase();
38052 return this.regions[target].remove(panel);
38056 * Searches all regions for a panel with the specified id
38057 * @param {String} panelId
38058 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38060 findPanel : function(panelId){
38061 var rs = this.regions;
38062 for(var target in rs){
38063 if(typeof rs[target] != "function"){
38064 var p = rs[target].getPanel(panelId);
38074 * Searches all regions for a panel with the specified id and activates (shows) it.
38075 * @param {String/ContentPanel} panelId The panels id or the panel itself
38076 * @return {Roo.ContentPanel} The shown panel or null
38078 showPanel : function(panelId) {
38079 var rs = this.regions;
38080 for(var target in rs){
38081 var r = rs[target];
38082 if(typeof r != "function"){
38083 if(r.hasPanel(panelId)){
38084 return r.showPanel(panelId);
38092 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38093 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38096 restoreState : function(provider){
38098 provider = Roo.state.Manager;
38100 var sm = new Roo.LayoutStateManager();
38101 sm.init(this, provider);
38107 * Adds a xtype elements to the layout.
38111 xtype : 'ContentPanel',
38118 xtype : 'NestedLayoutPanel',
38124 items : [ ... list of content panels or nested layout panels.. ]
38128 * @param {Object} cfg Xtype definition of item to add.
38130 addxtype : function(cfg)
38132 // basically accepts a pannel...
38133 // can accept a layout region..!?!?
38134 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38137 // theory? children can only be panels??
38139 //if (!cfg.xtype.match(/Panel$/)) {
38144 if (typeof(cfg.region) == 'undefined') {
38145 Roo.log("Failed to add Panel, region was not set");
38149 var region = cfg.region;
38155 xitems = cfg.items;
38160 if ( region == 'center') {
38161 Roo.log("Center: " + cfg.title);
38167 case 'Content': // ContentPanel (el, cfg)
38168 case 'Scroll': // ContentPanel (el, cfg)
38170 cfg.autoCreate = cfg.autoCreate || true;
38171 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38173 // var el = this.el.createChild();
38174 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38177 this.add(region, ret);
38181 case 'TreePanel': // our new panel!
38182 cfg.el = this.el.createChild();
38183 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38184 this.add(region, ret);
38189 // create a new Layout (which is a Border Layout...
38191 var clayout = cfg.layout;
38192 clayout.el = this.el.createChild();
38193 clayout.items = clayout.items || [];
38197 // replace this exitems with the clayout ones..
38198 xitems = clayout.items;
38200 // force background off if it's in center...
38201 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38202 cfg.background = false;
38204 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38207 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38208 //console.log('adding nested layout panel ' + cfg.toSource());
38209 this.add(region, ret);
38210 nb = {}; /// find first...
38215 // needs grid and region
38217 //var el = this.getRegion(region).el.createChild();
38219 *var el = this.el.createChild();
38220 // create the grid first...
38221 cfg.grid.container = el;
38222 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38225 if (region == 'center' && this.active ) {
38226 cfg.background = false;
38229 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38231 this.add(region, ret);
38233 if (cfg.background) {
38234 // render grid on panel activation (if panel background)
38235 ret.on('activate', function(gp) {
38236 if (!gp.grid.rendered) {
38237 // gp.grid.render(el);
38241 // cfg.grid.render(el);
38247 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38248 // it was the old xcomponent building that caused this before.
38249 // espeically if border is the top element in the tree.
38259 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38261 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38262 this.add(region, ret);
38266 throw "Can not add '" + cfg.xtype + "' to Border";
38272 this.beginUpdate();
38276 Roo.each(xitems, function(i) {
38277 region = nb && i.region ? i.region : false;
38279 var add = ret.addxtype(i);
38282 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38283 if (!i.background) {
38284 abn[region] = nb[region] ;
38291 // make the last non-background panel active..
38292 //if (nb) { Roo.log(abn); }
38295 for(var r in abn) {
38296 region = this.getRegion(r);
38298 // tried using nb[r], but it does not work..
38300 region.showPanel(abn[r]);
38311 factory : function(cfg)
38314 var validRegions = Roo.bootstrap.layout.Border.regions;
38316 var target = cfg.region;
38319 var r = Roo.bootstrap.layout;
38323 return new r.North(cfg);
38325 return new r.South(cfg);
38327 return new r.East(cfg);
38329 return new r.West(cfg);
38331 return new r.Center(cfg);
38333 throw 'Layout region "'+target+'" not supported.';
38340 * Ext JS Library 1.1.1
38341 * Copyright(c) 2006-2007, Ext JS, LLC.
38343 * Originally Released Under LGPL - original licence link has changed is not relivant.
38346 * <script type="text/javascript">
38350 * @class Roo.bootstrap.layout.Basic
38351 * @extends Roo.util.Observable
38352 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38353 * and does not have a titlebar, tabs or any other features. All it does is size and position
38354 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38355 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38356 * @cfg {string} region the region that it inhabits..
38357 * @cfg {bool} skipConfig skip config?
38361 Roo.bootstrap.layout.Basic = function(config){
38363 this.mgr = config.mgr;
38365 this.position = config.region;
38367 var skipConfig = config.skipConfig;
38371 * @scope Roo.BasicLayoutRegion
38375 * @event beforeremove
38376 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38377 * @param {Roo.LayoutRegion} this
38378 * @param {Roo.ContentPanel} panel The panel
38379 * @param {Object} e The cancel event object
38381 "beforeremove" : true,
38383 * @event invalidated
38384 * Fires when the layout for this region is changed.
38385 * @param {Roo.LayoutRegion} this
38387 "invalidated" : true,
38389 * @event visibilitychange
38390 * Fires when this region is shown or hidden
38391 * @param {Roo.LayoutRegion} this
38392 * @param {Boolean} visibility true or false
38394 "visibilitychange" : true,
38396 * @event paneladded
38397 * Fires when a panel is added.
38398 * @param {Roo.LayoutRegion} this
38399 * @param {Roo.ContentPanel} panel The panel
38401 "paneladded" : true,
38403 * @event panelremoved
38404 * Fires when a panel is removed.
38405 * @param {Roo.LayoutRegion} this
38406 * @param {Roo.ContentPanel} panel The panel
38408 "panelremoved" : true,
38410 * @event beforecollapse
38411 * Fires when this region before collapse.
38412 * @param {Roo.LayoutRegion} this
38414 "beforecollapse" : true,
38417 * Fires when this region is collapsed.
38418 * @param {Roo.LayoutRegion} this
38420 "collapsed" : true,
38423 * Fires when this region is expanded.
38424 * @param {Roo.LayoutRegion} this
38429 * Fires when this region is slid into view.
38430 * @param {Roo.LayoutRegion} this
38432 "slideshow" : true,
38435 * Fires when this region slides out of view.
38436 * @param {Roo.LayoutRegion} this
38438 "slidehide" : true,
38440 * @event panelactivated
38441 * Fires when a panel is activated.
38442 * @param {Roo.LayoutRegion} this
38443 * @param {Roo.ContentPanel} panel The activated panel
38445 "panelactivated" : true,
38448 * Fires when the user resizes this region.
38449 * @param {Roo.LayoutRegion} this
38450 * @param {Number} newSize The new size (width for east/west, height for north/south)
38454 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38455 this.panels = new Roo.util.MixedCollection();
38456 this.panels.getKey = this.getPanelId.createDelegate(this);
38458 this.activePanel = null;
38459 // ensure listeners are added...
38461 if (config.listeners || config.events) {
38462 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38463 listeners : config.listeners || {},
38464 events : config.events || {}
38468 if(skipConfig !== true){
38469 this.applyConfig(config);
38473 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38475 getPanelId : function(p){
38479 applyConfig : function(config){
38480 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38481 this.config = config;
38486 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38487 * the width, for horizontal (north, south) the height.
38488 * @param {Number} newSize The new width or height
38490 resizeTo : function(newSize){
38491 var el = this.el ? this.el :
38492 (this.activePanel ? this.activePanel.getEl() : null);
38494 switch(this.position){
38497 el.setWidth(newSize);
38498 this.fireEvent("resized", this, newSize);
38502 el.setHeight(newSize);
38503 this.fireEvent("resized", this, newSize);
38509 getBox : function(){
38510 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38513 getMargins : function(){
38514 return this.margins;
38517 updateBox : function(box){
38519 var el = this.activePanel.getEl();
38520 el.dom.style.left = box.x + "px";
38521 el.dom.style.top = box.y + "px";
38522 this.activePanel.setSize(box.width, box.height);
38526 * Returns the container element for this region.
38527 * @return {Roo.Element}
38529 getEl : function(){
38530 return this.activePanel;
38534 * Returns true if this region is currently visible.
38535 * @return {Boolean}
38537 isVisible : function(){
38538 return this.activePanel ? true : false;
38541 setActivePanel : function(panel){
38542 panel = this.getPanel(panel);
38543 if(this.activePanel && this.activePanel != panel){
38544 this.activePanel.setActiveState(false);
38545 this.activePanel.getEl().setLeftTop(-10000,-10000);
38547 this.activePanel = panel;
38548 panel.setActiveState(true);
38550 panel.setSize(this.box.width, this.box.height);
38552 this.fireEvent("panelactivated", this, panel);
38553 this.fireEvent("invalidated");
38557 * Show the specified panel.
38558 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38559 * @return {Roo.ContentPanel} The shown panel or null
38561 showPanel : function(panel){
38562 panel = this.getPanel(panel);
38564 this.setActivePanel(panel);
38570 * Get the active panel for this region.
38571 * @return {Roo.ContentPanel} The active panel or null
38573 getActivePanel : function(){
38574 return this.activePanel;
38578 * Add the passed ContentPanel(s)
38579 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38580 * @return {Roo.ContentPanel} The panel added (if only one was added)
38582 add : function(panel){
38583 if(arguments.length > 1){
38584 for(var i = 0, len = arguments.length; i < len; i++) {
38585 this.add(arguments[i]);
38589 if(this.hasPanel(panel)){
38590 this.showPanel(panel);
38593 var el = panel.getEl();
38594 if(el.dom.parentNode != this.mgr.el.dom){
38595 this.mgr.el.dom.appendChild(el.dom);
38597 if(panel.setRegion){
38598 panel.setRegion(this);
38600 this.panels.add(panel);
38601 el.setStyle("position", "absolute");
38602 if(!panel.background){
38603 this.setActivePanel(panel);
38604 if(this.config.initialSize && this.panels.getCount()==1){
38605 this.resizeTo(this.config.initialSize);
38608 this.fireEvent("paneladded", this, panel);
38613 * Returns true if the panel is in this region.
38614 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38615 * @return {Boolean}
38617 hasPanel : function(panel){
38618 if(typeof panel == "object"){ // must be panel obj
38619 panel = panel.getId();
38621 return this.getPanel(panel) ? true : false;
38625 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38626 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38627 * @param {Boolean} preservePanel Overrides the config preservePanel option
38628 * @return {Roo.ContentPanel} The panel that was removed
38630 remove : function(panel, preservePanel){
38631 panel = this.getPanel(panel);
38636 this.fireEvent("beforeremove", this, panel, e);
38637 if(e.cancel === true){
38640 var panelId = panel.getId();
38641 this.panels.removeKey(panelId);
38646 * Returns the panel specified or null if it's not in this region.
38647 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38648 * @return {Roo.ContentPanel}
38650 getPanel : function(id){
38651 if(typeof id == "object"){ // must be panel obj
38654 return this.panels.get(id);
38658 * Returns this regions position (north/south/east/west/center).
38661 getPosition: function(){
38662 return this.position;
38666 * Ext JS Library 1.1.1
38667 * Copyright(c) 2006-2007, Ext JS, LLC.
38669 * Originally Released Under LGPL - original licence link has changed is not relivant.
38672 * <script type="text/javascript">
38676 * @class Roo.bootstrap.layout.Region
38677 * @extends Roo.bootstrap.layout.Basic
38678 * This class represents a region in a layout manager.
38680 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38681 * @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})
38682 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38683 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38684 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38685 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38686 * @cfg {String} title The title for the region (overrides panel titles)
38687 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38688 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38689 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38690 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38691 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38692 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38693 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38694 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38695 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38696 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38698 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38699 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38700 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38701 * @cfg {Number} width For East/West panels
38702 * @cfg {Number} height For North/South panels
38703 * @cfg {Boolean} split To show the splitter
38704 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38706 * @cfg {string} cls Extra CSS classes to add to region
38708 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38709 * @cfg {string} region the region that it inhabits..
38712 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38713 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38715 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38716 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38717 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38719 Roo.bootstrap.layout.Region = function(config)
38721 this.applyConfig(config);
38723 var mgr = config.mgr;
38724 var pos = config.region;
38725 config.skipConfig = true;
38726 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38729 this.onRender(mgr.el);
38732 this.visible = true;
38733 this.collapsed = false;
38734 this.unrendered_panels = [];
38737 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38739 position: '', // set by wrapper (eg. north/south etc..)
38740 unrendered_panels : null, // unrendered panels.
38742 tabPosition : false,
38744 mgr: false, // points to 'Border'
38747 createBody : function(){
38748 /** This region's body element
38749 * @type Roo.Element */
38750 this.bodyEl = this.el.createChild({
38752 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38756 onRender: function(ctr, pos)
38758 var dh = Roo.DomHelper;
38759 /** This region's container element
38760 * @type Roo.Element */
38761 this.el = dh.append(ctr.dom, {
38763 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38765 /** This region's title element
38766 * @type Roo.Element */
38768 this.titleEl = dh.append(this.el.dom, {
38770 unselectable: "on",
38771 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38773 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38774 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38778 this.titleEl.enableDisplayMode();
38779 /** This region's title text element
38780 * @type HTMLElement */
38781 this.titleTextEl = this.titleEl.dom.firstChild;
38782 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38784 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38785 this.closeBtn.enableDisplayMode();
38786 this.closeBtn.on("click", this.closeClicked, this);
38787 this.closeBtn.hide();
38789 this.createBody(this.config);
38790 if(this.config.hideWhenEmpty){
38792 this.on("paneladded", this.validateVisibility, this);
38793 this.on("panelremoved", this.validateVisibility, this);
38795 if(this.autoScroll){
38796 this.bodyEl.setStyle("overflow", "auto");
38798 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38800 //if(c.titlebar !== false){
38801 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38802 this.titleEl.hide();
38804 this.titleEl.show();
38805 if(this.config.title){
38806 this.titleTextEl.innerHTML = this.config.title;
38810 if(this.config.collapsed){
38811 this.collapse(true);
38813 if(this.config.hidden){
38817 if (this.unrendered_panels && this.unrendered_panels.length) {
38818 for (var i =0;i< this.unrendered_panels.length; i++) {
38819 this.add(this.unrendered_panels[i]);
38821 this.unrendered_panels = null;
38827 applyConfig : function(c)
38830 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38831 var dh = Roo.DomHelper;
38832 if(c.titlebar !== false){
38833 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38834 this.collapseBtn.on("click", this.collapse, this);
38835 this.collapseBtn.enableDisplayMode();
38837 if(c.showPin === true || this.showPin){
38838 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38839 this.stickBtn.enableDisplayMode();
38840 this.stickBtn.on("click", this.expand, this);
38841 this.stickBtn.hide();
38846 /** This region's collapsed element
38847 * @type Roo.Element */
38850 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38851 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38854 if(c.floatable !== false){
38855 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38856 this.collapsedEl.on("click", this.collapseClick, this);
38859 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38860 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38861 id: "message", unselectable: "on", style:{"float":"left"}});
38862 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38864 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38865 this.expandBtn.on("click", this.expand, this);
38869 if(this.collapseBtn){
38870 this.collapseBtn.setVisible(c.collapsible == true);
38873 this.cmargins = c.cmargins || this.cmargins ||
38874 (this.position == "west" || this.position == "east" ?
38875 {top: 0, left: 2, right:2, bottom: 0} :
38876 {top: 2, left: 0, right:0, bottom: 2});
38878 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38881 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38883 this.autoScroll = c.autoScroll || false;
38888 this.duration = c.duration || .30;
38889 this.slideDuration = c.slideDuration || .45;
38894 * Returns true if this region is currently visible.
38895 * @return {Boolean}
38897 isVisible : function(){
38898 return this.visible;
38902 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38903 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38905 //setCollapsedTitle : function(title){
38906 // title = title || " ";
38907 // if(this.collapsedTitleTextEl){
38908 // this.collapsedTitleTextEl.innerHTML = title;
38912 getBox : function(){
38914 // if(!this.collapsed){
38915 b = this.el.getBox(false, true);
38917 // b = this.collapsedEl.getBox(false, true);
38922 getMargins : function(){
38923 return this.margins;
38924 //return this.collapsed ? this.cmargins : this.margins;
38927 highlight : function(){
38928 this.el.addClass("x-layout-panel-dragover");
38931 unhighlight : function(){
38932 this.el.removeClass("x-layout-panel-dragover");
38935 updateBox : function(box)
38937 if (!this.bodyEl) {
38938 return; // not rendered yet..
38942 if(!this.collapsed){
38943 this.el.dom.style.left = box.x + "px";
38944 this.el.dom.style.top = box.y + "px";
38945 this.updateBody(box.width, box.height);
38947 this.collapsedEl.dom.style.left = box.x + "px";
38948 this.collapsedEl.dom.style.top = box.y + "px";
38949 this.collapsedEl.setSize(box.width, box.height);
38952 this.tabs.autoSizeTabs();
38956 updateBody : function(w, h)
38959 this.el.setWidth(w);
38960 w -= this.el.getBorderWidth("rl");
38961 if(this.config.adjustments){
38962 w += this.config.adjustments[0];
38965 if(h !== null && h > 0){
38966 this.el.setHeight(h);
38967 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38968 h -= this.el.getBorderWidth("tb");
38969 if(this.config.adjustments){
38970 h += this.config.adjustments[1];
38972 this.bodyEl.setHeight(h);
38974 h = this.tabs.syncHeight(h);
38977 if(this.panelSize){
38978 w = w !== null ? w : this.panelSize.width;
38979 h = h !== null ? h : this.panelSize.height;
38981 if(this.activePanel){
38982 var el = this.activePanel.getEl();
38983 w = w !== null ? w : el.getWidth();
38984 h = h !== null ? h : el.getHeight();
38985 this.panelSize = {width: w, height: h};
38986 this.activePanel.setSize(w, h);
38988 if(Roo.isIE && this.tabs){
38989 this.tabs.el.repaint();
38994 * Returns the container element for this region.
38995 * @return {Roo.Element}
38997 getEl : function(){
39002 * Hides this region.
39005 //if(!this.collapsed){
39006 this.el.dom.style.left = "-2000px";
39009 // this.collapsedEl.dom.style.left = "-2000px";
39010 // this.collapsedEl.hide();
39012 this.visible = false;
39013 this.fireEvent("visibilitychange", this, false);
39017 * Shows this region if it was previously hidden.
39020 //if(!this.collapsed){
39023 // this.collapsedEl.show();
39025 this.visible = true;
39026 this.fireEvent("visibilitychange", this, true);
39029 closeClicked : function(){
39030 if(this.activePanel){
39031 this.remove(this.activePanel);
39035 collapseClick : function(e){
39037 e.stopPropagation();
39040 e.stopPropagation();
39046 * Collapses this region.
39047 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39050 collapse : function(skipAnim, skipCheck = false){
39051 if(this.collapsed) {
39055 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39057 this.collapsed = true;
39059 this.split.el.hide();
39061 if(this.config.animate && skipAnim !== true){
39062 this.fireEvent("invalidated", this);
39063 this.animateCollapse();
39065 this.el.setLocation(-20000,-20000);
39067 this.collapsedEl.show();
39068 this.fireEvent("collapsed", this);
39069 this.fireEvent("invalidated", this);
39075 animateCollapse : function(){
39080 * Expands this region if it was previously collapsed.
39081 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39082 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39085 expand : function(e, skipAnim){
39087 e.stopPropagation();
39089 if(!this.collapsed || this.el.hasActiveFx()) {
39093 this.afterSlideIn();
39096 this.collapsed = false;
39097 if(this.config.animate && skipAnim !== true){
39098 this.animateExpand();
39102 this.split.el.show();
39104 this.collapsedEl.setLocation(-2000,-2000);
39105 this.collapsedEl.hide();
39106 this.fireEvent("invalidated", this);
39107 this.fireEvent("expanded", this);
39111 animateExpand : function(){
39115 initTabs : function()
39117 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39119 var ts = new Roo.bootstrap.panel.Tabs({
39120 el: this.bodyEl.dom,
39122 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39123 disableTooltips: this.config.disableTabTips,
39124 toolbar : this.config.toolbar
39127 if(this.config.hideTabs){
39128 ts.stripWrap.setDisplayed(false);
39131 ts.resizeTabs = this.config.resizeTabs === true;
39132 ts.minTabWidth = this.config.minTabWidth || 40;
39133 ts.maxTabWidth = this.config.maxTabWidth || 250;
39134 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39135 ts.monitorResize = false;
39136 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39137 ts.bodyEl.addClass('roo-layout-tabs-body');
39138 this.panels.each(this.initPanelAsTab, this);
39141 initPanelAsTab : function(panel){
39142 var ti = this.tabs.addTab(
39146 this.config.closeOnTab && panel.isClosable(),
39149 if(panel.tabTip !== undefined){
39150 ti.setTooltip(panel.tabTip);
39152 ti.on("activate", function(){
39153 this.setActivePanel(panel);
39156 if(this.config.closeOnTab){
39157 ti.on("beforeclose", function(t, e){
39159 this.remove(panel);
39163 panel.tabItem = ti;
39168 updatePanelTitle : function(panel, title)
39170 if(this.activePanel == panel){
39171 this.updateTitle(title);
39174 var ti = this.tabs.getTab(panel.getEl().id);
39176 if(panel.tabTip !== undefined){
39177 ti.setTooltip(panel.tabTip);
39182 updateTitle : function(title){
39183 if(this.titleTextEl && !this.config.title){
39184 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39188 setActivePanel : function(panel)
39190 panel = this.getPanel(panel);
39191 if(this.activePanel && this.activePanel != panel){
39192 if(this.activePanel.setActiveState(false) === false){
39196 this.activePanel = panel;
39197 panel.setActiveState(true);
39198 if(this.panelSize){
39199 panel.setSize(this.panelSize.width, this.panelSize.height);
39202 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39204 this.updateTitle(panel.getTitle());
39206 this.fireEvent("invalidated", this);
39208 this.fireEvent("panelactivated", this, panel);
39212 * Shows the specified panel.
39213 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39214 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39216 showPanel : function(panel)
39218 panel = this.getPanel(panel);
39221 var tab = this.tabs.getTab(panel.getEl().id);
39222 if(tab.isHidden()){
39223 this.tabs.unhideTab(tab.id);
39227 this.setActivePanel(panel);
39234 * Get the active panel for this region.
39235 * @return {Roo.ContentPanel} The active panel or null
39237 getActivePanel : function(){
39238 return this.activePanel;
39241 validateVisibility : function(){
39242 if(this.panels.getCount() < 1){
39243 this.updateTitle(" ");
39244 this.closeBtn.hide();
39247 if(!this.isVisible()){
39254 * Adds the passed ContentPanel(s) to this region.
39255 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39256 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39258 add : function(panel)
39260 if(arguments.length > 1){
39261 for(var i = 0, len = arguments.length; i < len; i++) {
39262 this.add(arguments[i]);
39267 // if we have not been rendered yet, then we can not really do much of this..
39268 if (!this.bodyEl) {
39269 this.unrendered_panels.push(panel);
39276 if(this.hasPanel(panel)){
39277 this.showPanel(panel);
39280 panel.setRegion(this);
39281 this.panels.add(panel);
39282 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39283 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39284 // and hide them... ???
39285 this.bodyEl.dom.appendChild(panel.getEl().dom);
39286 if(panel.background !== true){
39287 this.setActivePanel(panel);
39289 this.fireEvent("paneladded", this, panel);
39296 this.initPanelAsTab(panel);
39300 if(panel.background !== true){
39301 this.tabs.activate(panel.getEl().id);
39303 this.fireEvent("paneladded", this, panel);
39308 * Hides the tab for the specified panel.
39309 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39311 hidePanel : function(panel){
39312 if(this.tabs && (panel = this.getPanel(panel))){
39313 this.tabs.hideTab(panel.getEl().id);
39318 * Unhides the tab for a previously hidden panel.
39319 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39321 unhidePanel : function(panel){
39322 if(this.tabs && (panel = this.getPanel(panel))){
39323 this.tabs.unhideTab(panel.getEl().id);
39327 clearPanels : function(){
39328 while(this.panels.getCount() > 0){
39329 this.remove(this.panels.first());
39334 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39335 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39336 * @param {Boolean} preservePanel Overrides the config preservePanel option
39337 * @return {Roo.ContentPanel} The panel that was removed
39339 remove : function(panel, preservePanel)
39341 panel = this.getPanel(panel);
39346 this.fireEvent("beforeremove", this, panel, e);
39347 if(e.cancel === true){
39350 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39351 var panelId = panel.getId();
39352 this.panels.removeKey(panelId);
39354 document.body.appendChild(panel.getEl().dom);
39357 this.tabs.removeTab(panel.getEl().id);
39358 }else if (!preservePanel){
39359 this.bodyEl.dom.removeChild(panel.getEl().dom);
39361 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39362 var p = this.panels.first();
39363 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39364 tempEl.appendChild(p.getEl().dom);
39365 this.bodyEl.update("");
39366 this.bodyEl.dom.appendChild(p.getEl().dom);
39368 this.updateTitle(p.getTitle());
39370 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39371 this.setActivePanel(p);
39373 panel.setRegion(null);
39374 if(this.activePanel == panel){
39375 this.activePanel = null;
39377 if(this.config.autoDestroy !== false && preservePanel !== true){
39378 try{panel.destroy();}catch(e){}
39380 this.fireEvent("panelremoved", this, panel);
39385 * Returns the TabPanel component used by this region
39386 * @return {Roo.TabPanel}
39388 getTabs : function(){
39392 createTool : function(parentEl, className){
39393 var btn = Roo.DomHelper.append(parentEl, {
39395 cls: "x-layout-tools-button",
39398 cls: "roo-layout-tools-button-inner " + className,
39402 btn.addClassOnOver("roo-layout-tools-button-over");
39407 * Ext JS Library 1.1.1
39408 * Copyright(c) 2006-2007, Ext JS, LLC.
39410 * Originally Released Under LGPL - original licence link has changed is not relivant.
39413 * <script type="text/javascript">
39419 * @class Roo.SplitLayoutRegion
39420 * @extends Roo.LayoutRegion
39421 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39423 Roo.bootstrap.layout.Split = function(config){
39424 this.cursor = config.cursor;
39425 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39428 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39430 splitTip : "Drag to resize.",
39431 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39432 useSplitTips : false,
39434 applyConfig : function(config){
39435 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39438 onRender : function(ctr,pos) {
39440 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39441 if(!this.config.split){
39446 var splitEl = Roo.DomHelper.append(ctr.dom, {
39448 id: this.el.id + "-split",
39449 cls: "roo-layout-split roo-layout-split-"+this.position,
39452 /** The SplitBar for this region
39453 * @type Roo.SplitBar */
39454 // does not exist yet...
39455 Roo.log([this.position, this.orientation]);
39457 this.split = new Roo.bootstrap.SplitBar({
39458 dragElement : splitEl,
39459 resizingElement: this.el,
39460 orientation : this.orientation
39463 this.split.on("moved", this.onSplitMove, this);
39464 this.split.useShim = this.config.useShim === true;
39465 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39466 if(this.useSplitTips){
39467 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39469 //if(config.collapsible){
39470 // this.split.el.on("dblclick", this.collapse, this);
39473 if(typeof this.config.minSize != "undefined"){
39474 this.split.minSize = this.config.minSize;
39476 if(typeof this.config.maxSize != "undefined"){
39477 this.split.maxSize = this.config.maxSize;
39479 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39480 this.hideSplitter();
39485 getHMaxSize : function(){
39486 var cmax = this.config.maxSize || 10000;
39487 var center = this.mgr.getRegion("center");
39488 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39491 getVMaxSize : function(){
39492 var cmax = this.config.maxSize || 10000;
39493 var center = this.mgr.getRegion("center");
39494 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39497 onSplitMove : function(split, newSize){
39498 this.fireEvent("resized", this, newSize);
39502 * Returns the {@link Roo.SplitBar} for this region.
39503 * @return {Roo.SplitBar}
39505 getSplitBar : function(){
39510 this.hideSplitter();
39511 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39514 hideSplitter : function(){
39516 this.split.el.setLocation(-2000,-2000);
39517 this.split.el.hide();
39523 this.split.el.show();
39525 Roo.bootstrap.layout.Split.superclass.show.call(this);
39528 beforeSlide: function(){
39529 if(Roo.isGecko){// firefox overflow auto bug workaround
39530 this.bodyEl.clip();
39532 this.tabs.bodyEl.clip();
39534 if(this.activePanel){
39535 this.activePanel.getEl().clip();
39537 if(this.activePanel.beforeSlide){
39538 this.activePanel.beforeSlide();
39544 afterSlide : function(){
39545 if(Roo.isGecko){// firefox overflow auto bug workaround
39546 this.bodyEl.unclip();
39548 this.tabs.bodyEl.unclip();
39550 if(this.activePanel){
39551 this.activePanel.getEl().unclip();
39552 if(this.activePanel.afterSlide){
39553 this.activePanel.afterSlide();
39559 initAutoHide : function(){
39560 if(this.autoHide !== false){
39561 if(!this.autoHideHd){
39562 var st = new Roo.util.DelayedTask(this.slideIn, this);
39563 this.autoHideHd = {
39564 "mouseout": function(e){
39565 if(!e.within(this.el, true)){
39569 "mouseover" : function(e){
39575 this.el.on(this.autoHideHd);
39579 clearAutoHide : function(){
39580 if(this.autoHide !== false){
39581 this.el.un("mouseout", this.autoHideHd.mouseout);
39582 this.el.un("mouseover", this.autoHideHd.mouseover);
39586 clearMonitor : function(){
39587 Roo.get(document).un("click", this.slideInIf, this);
39590 // these names are backwards but not changed for compat
39591 slideOut : function(){
39592 if(this.isSlid || this.el.hasActiveFx()){
39595 this.isSlid = true;
39596 if(this.collapseBtn){
39597 this.collapseBtn.hide();
39599 this.closeBtnState = this.closeBtn.getStyle('display');
39600 this.closeBtn.hide();
39602 this.stickBtn.show();
39605 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39606 this.beforeSlide();
39607 this.el.setStyle("z-index", 10001);
39608 this.el.slideIn(this.getSlideAnchor(), {
39609 callback: function(){
39611 this.initAutoHide();
39612 Roo.get(document).on("click", this.slideInIf, this);
39613 this.fireEvent("slideshow", this);
39620 afterSlideIn : function(){
39621 this.clearAutoHide();
39622 this.isSlid = false;
39623 this.clearMonitor();
39624 this.el.setStyle("z-index", "");
39625 if(this.collapseBtn){
39626 this.collapseBtn.show();
39628 this.closeBtn.setStyle('display', this.closeBtnState);
39630 this.stickBtn.hide();
39632 this.fireEvent("slidehide", this);
39635 slideIn : function(cb){
39636 if(!this.isSlid || this.el.hasActiveFx()){
39640 this.isSlid = false;
39641 this.beforeSlide();
39642 this.el.slideOut(this.getSlideAnchor(), {
39643 callback: function(){
39644 this.el.setLeftTop(-10000, -10000);
39646 this.afterSlideIn();
39654 slideInIf : function(e){
39655 if(!e.within(this.el)){
39660 animateCollapse : function(){
39661 this.beforeSlide();
39662 this.el.setStyle("z-index", 20000);
39663 var anchor = this.getSlideAnchor();
39664 this.el.slideOut(anchor, {
39665 callback : function(){
39666 this.el.setStyle("z-index", "");
39667 this.collapsedEl.slideIn(anchor, {duration:.3});
39669 this.el.setLocation(-10000,-10000);
39671 this.fireEvent("collapsed", this);
39678 animateExpand : function(){
39679 this.beforeSlide();
39680 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39681 this.el.setStyle("z-index", 20000);
39682 this.collapsedEl.hide({
39685 this.el.slideIn(this.getSlideAnchor(), {
39686 callback : function(){
39687 this.el.setStyle("z-index", "");
39690 this.split.el.show();
39692 this.fireEvent("invalidated", this);
39693 this.fireEvent("expanded", this);
39721 getAnchor : function(){
39722 return this.anchors[this.position];
39725 getCollapseAnchor : function(){
39726 return this.canchors[this.position];
39729 getSlideAnchor : function(){
39730 return this.sanchors[this.position];
39733 getAlignAdj : function(){
39734 var cm = this.cmargins;
39735 switch(this.position){
39751 getExpandAdj : function(){
39752 var c = this.collapsedEl, cm = this.cmargins;
39753 switch(this.position){
39755 return [-(cm.right+c.getWidth()+cm.left), 0];
39758 return [cm.right+c.getWidth()+cm.left, 0];
39761 return [0, -(cm.top+cm.bottom+c.getHeight())];
39764 return [0, cm.top+cm.bottom+c.getHeight()];
39770 * Ext JS Library 1.1.1
39771 * Copyright(c) 2006-2007, Ext JS, LLC.
39773 * Originally Released Under LGPL - original licence link has changed is not relivant.
39776 * <script type="text/javascript">
39779 * These classes are private internal classes
39781 Roo.bootstrap.layout.Center = function(config){
39782 config.region = "center";
39783 Roo.bootstrap.layout.Region.call(this, config);
39784 this.visible = true;
39785 this.minWidth = config.minWidth || 20;
39786 this.minHeight = config.minHeight || 20;
39789 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39791 // center panel can't be hidden
39795 // center panel can't be hidden
39798 getMinWidth: function(){
39799 return this.minWidth;
39802 getMinHeight: function(){
39803 return this.minHeight;
39817 Roo.bootstrap.layout.North = function(config)
39819 config.region = 'north';
39820 config.cursor = 'n-resize';
39822 Roo.bootstrap.layout.Split.call(this, config);
39826 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39827 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39828 this.split.el.addClass("roo-layout-split-v");
39830 //var size = config.initialSize || config.height;
39831 //if(this.el && typeof size != "undefined"){
39832 // this.el.setHeight(size);
39835 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39837 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39840 onRender : function(ctr, pos)
39842 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39843 var size = this.config.initialSize || this.config.height;
39844 if(this.el && typeof size != "undefined"){
39845 this.el.setHeight(size);
39850 getBox : function(){
39851 if(this.collapsed){
39852 return this.collapsedEl.getBox();
39854 var box = this.el.getBox();
39856 box.height += this.split.el.getHeight();
39861 updateBox : function(box){
39862 if(this.split && !this.collapsed){
39863 box.height -= this.split.el.getHeight();
39864 this.split.el.setLeft(box.x);
39865 this.split.el.setTop(box.y+box.height);
39866 this.split.el.setWidth(box.width);
39868 if(this.collapsed){
39869 this.updateBody(box.width, null);
39871 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39879 Roo.bootstrap.layout.South = function(config){
39880 config.region = 'south';
39881 config.cursor = 's-resize';
39882 Roo.bootstrap.layout.Split.call(this, config);
39884 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39885 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39886 this.split.el.addClass("roo-layout-split-v");
39891 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39892 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39894 onRender : function(ctr, pos)
39896 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39897 var size = this.config.initialSize || this.config.height;
39898 if(this.el && typeof size != "undefined"){
39899 this.el.setHeight(size);
39904 getBox : function(){
39905 if(this.collapsed){
39906 return this.collapsedEl.getBox();
39908 var box = this.el.getBox();
39910 var sh = this.split.el.getHeight();
39917 updateBox : function(box){
39918 if(this.split && !this.collapsed){
39919 var sh = this.split.el.getHeight();
39922 this.split.el.setLeft(box.x);
39923 this.split.el.setTop(box.y-sh);
39924 this.split.el.setWidth(box.width);
39926 if(this.collapsed){
39927 this.updateBody(box.width, null);
39929 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39933 Roo.bootstrap.layout.East = function(config){
39934 config.region = "east";
39935 config.cursor = "e-resize";
39936 Roo.bootstrap.layout.Split.call(this, config);
39938 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39939 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39940 this.split.el.addClass("roo-layout-split-h");
39944 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39945 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39947 onRender : function(ctr, pos)
39949 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39950 var size = this.config.initialSize || this.config.width;
39951 if(this.el && typeof size != "undefined"){
39952 this.el.setWidth(size);
39957 getBox : function(){
39958 if(this.collapsed){
39959 return this.collapsedEl.getBox();
39961 var box = this.el.getBox();
39963 var sw = this.split.el.getWidth();
39970 updateBox : function(box){
39971 if(this.split && !this.collapsed){
39972 var sw = this.split.el.getWidth();
39974 this.split.el.setLeft(box.x);
39975 this.split.el.setTop(box.y);
39976 this.split.el.setHeight(box.height);
39979 if(this.collapsed){
39980 this.updateBody(null, box.height);
39982 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39986 Roo.bootstrap.layout.West = function(config){
39987 config.region = "west";
39988 config.cursor = "w-resize";
39990 Roo.bootstrap.layout.Split.call(this, config);
39992 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39993 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39994 this.split.el.addClass("roo-layout-split-h");
39998 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39999 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40001 onRender: function(ctr, pos)
40003 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40004 var size = this.config.initialSize || this.config.width;
40005 if(typeof size != "undefined"){
40006 this.el.setWidth(size);
40010 getBox : function(){
40011 if(this.collapsed){
40012 return this.collapsedEl.getBox();
40014 var box = this.el.getBox();
40015 if (box.width == 0) {
40016 box.width = this.config.width; // kludge?
40019 box.width += this.split.el.getWidth();
40024 updateBox : function(box){
40025 if(this.split && !this.collapsed){
40026 var sw = this.split.el.getWidth();
40028 this.split.el.setLeft(box.x+box.width);
40029 this.split.el.setTop(box.y);
40030 this.split.el.setHeight(box.height);
40032 if(this.collapsed){
40033 this.updateBody(null, box.height);
40035 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40037 });Roo.namespace("Roo.bootstrap.panel");/*
40039 * Ext JS Library 1.1.1
40040 * Copyright(c) 2006-2007, Ext JS, LLC.
40042 * Originally Released Under LGPL - original licence link has changed is not relivant.
40045 * <script type="text/javascript">
40048 * @class Roo.ContentPanel
40049 * @extends Roo.util.Observable
40050 * A basic ContentPanel element.
40051 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40052 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40053 * @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
40054 * @cfg {Boolean} closable True if the panel can be closed/removed
40055 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40056 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40057 * @cfg {Toolbar} toolbar A toolbar for this panel
40058 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40059 * @cfg {String} title The title for this panel
40060 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40061 * @cfg {String} url Calls {@link #setUrl} with this value
40062 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40063 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40064 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40065 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40066 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40067 * @cfg {Boolean} badges render the badges
40068 * @cfg {String} cls extra classes to use
40069 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40072 * Create a new ContentPanel.
40073 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40074 * @param {String/Object} config A string to set only the title or a config object
40075 * @param {String} content (optional) Set the HTML content for this panel
40076 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40078 Roo.bootstrap.panel.Content = function( config){
40080 this.tpl = config.tpl || false;
40082 var el = config.el;
40083 var content = config.content;
40085 if(config.autoCreate){ // xtype is available if this is called from factory
40088 this.el = Roo.get(el);
40089 if(!this.el && config && config.autoCreate){
40090 if(typeof config.autoCreate == "object"){
40091 if(!config.autoCreate.id){
40092 config.autoCreate.id = config.id||el;
40094 this.el = Roo.DomHelper.append(document.body,
40095 config.autoCreate, true);
40099 cls: (config.cls || '') +
40100 (config.background ? ' bg-' + config.background : '') +
40101 " roo-layout-inactive-content",
40104 if (config.iframe) {
40108 style : 'border: 0px',
40109 src : 'about:blank'
40115 elcfg.html = config.html;
40119 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40120 if (config.iframe) {
40121 this.iframeEl = this.el.select('iframe',true).first();
40126 this.closable = false;
40127 this.loaded = false;
40128 this.active = false;
40131 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40133 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40135 this.wrapEl = this.el; //this.el.wrap();
40137 if (config.toolbar.items) {
40138 ti = config.toolbar.items ;
40139 delete config.toolbar.items ;
40143 this.toolbar.render(this.wrapEl, 'before');
40144 for(var i =0;i < ti.length;i++) {
40145 // Roo.log(['add child', items[i]]);
40146 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40148 this.toolbar.items = nitems;
40149 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40150 delete config.toolbar;
40154 // xtype created footer. - not sure if will work as we normally have to render first..
40155 if (this.footer && !this.footer.el && this.footer.xtype) {
40156 if (!this.wrapEl) {
40157 this.wrapEl = this.el.wrap();
40160 this.footer.container = this.wrapEl.createChild();
40162 this.footer = Roo.factory(this.footer, Roo);
40167 if(typeof config == "string"){
40168 this.title = config;
40170 Roo.apply(this, config);
40174 this.resizeEl = Roo.get(this.resizeEl, true);
40176 this.resizeEl = this.el;
40178 // handle view.xtype
40186 * Fires when this panel is activated.
40187 * @param {Roo.ContentPanel} this
40191 * @event deactivate
40192 * Fires when this panel is activated.
40193 * @param {Roo.ContentPanel} this
40195 "deactivate" : true,
40199 * Fires when this panel is resized if fitToFrame is true.
40200 * @param {Roo.ContentPanel} this
40201 * @param {Number} width The width after any component adjustments
40202 * @param {Number} height The height after any component adjustments
40208 * Fires when this tab is created
40209 * @param {Roo.ContentPanel} this
40220 if(this.autoScroll && !this.iframe){
40221 this.resizeEl.setStyle("overflow", "auto");
40223 // fix randome scrolling
40224 //this.el.on('scroll', function() {
40225 // Roo.log('fix random scolling');
40226 // this.scrollTo('top',0);
40229 content = content || this.content;
40231 this.setContent(content);
40233 if(config && config.url){
40234 this.setUrl(this.url, this.params, this.loadOnce);
40239 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40241 if (this.view && typeof(this.view.xtype) != 'undefined') {
40242 this.view.el = this.el.appendChild(document.createElement("div"));
40243 this.view = Roo.factory(this.view);
40244 this.view.render && this.view.render(false, '');
40248 this.fireEvent('render', this);
40251 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40261 setRegion : function(region){
40262 this.region = region;
40263 this.setActiveClass(region && !this.background);
40267 setActiveClass: function(state)
40270 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40271 this.el.setStyle('position','relative');
40273 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40274 this.el.setStyle('position', 'absolute');
40279 * Returns the toolbar for this Panel if one was configured.
40280 * @return {Roo.Toolbar}
40282 getToolbar : function(){
40283 return this.toolbar;
40286 setActiveState : function(active)
40288 this.active = active;
40289 this.setActiveClass(active);
40291 if(this.fireEvent("deactivate", this) === false){
40296 this.fireEvent("activate", this);
40300 * Updates this panel's element (not for iframe)
40301 * @param {String} content The new content
40302 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40304 setContent : function(content, loadScripts){
40309 this.el.update(content, loadScripts);
40312 ignoreResize : function(w, h){
40313 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40316 this.lastSize = {width: w, height: h};
40321 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40322 * @return {Roo.UpdateManager} The UpdateManager
40324 getUpdateManager : function(){
40328 return this.el.getUpdateManager();
40331 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40332 * Does not work with IFRAME contents
40333 * @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:
40336 url: "your-url.php",
40337 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40338 callback: yourFunction,
40339 scope: yourObject, //(optional scope)
40342 text: "Loading...",
40348 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40349 * 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.
40350 * @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}
40351 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40352 * @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.
40353 * @return {Roo.ContentPanel} this
40361 var um = this.el.getUpdateManager();
40362 um.update.apply(um, arguments);
40368 * 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.
40369 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40370 * @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)
40371 * @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)
40372 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40374 setUrl : function(url, params, loadOnce){
40376 this.iframeEl.dom.src = url;
40380 if(this.refreshDelegate){
40381 this.removeListener("activate", this.refreshDelegate);
40383 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40384 this.on("activate", this.refreshDelegate);
40385 return this.el.getUpdateManager();
40388 _handleRefresh : function(url, params, loadOnce){
40389 if(!loadOnce || !this.loaded){
40390 var updater = this.el.getUpdateManager();
40391 updater.update(url, params, this._setLoaded.createDelegate(this));
40395 _setLoaded : function(){
40396 this.loaded = true;
40400 * Returns this panel's id
40403 getId : function(){
40408 * Returns this panel's element - used by regiosn to add.
40409 * @return {Roo.Element}
40411 getEl : function(){
40412 return this.wrapEl || this.el;
40417 adjustForComponents : function(width, height)
40419 //Roo.log('adjustForComponents ');
40420 if(this.resizeEl != this.el){
40421 width -= this.el.getFrameWidth('lr');
40422 height -= this.el.getFrameWidth('tb');
40425 var te = this.toolbar.getEl();
40426 te.setWidth(width);
40427 height -= te.getHeight();
40430 var te = this.footer.getEl();
40431 te.setWidth(width);
40432 height -= te.getHeight();
40436 if(this.adjustments){
40437 width += this.adjustments[0];
40438 height += this.adjustments[1];
40440 return {"width": width, "height": height};
40443 setSize : function(width, height){
40444 if(this.fitToFrame && !this.ignoreResize(width, height)){
40445 if(this.fitContainer && this.resizeEl != this.el){
40446 this.el.setSize(width, height);
40448 var size = this.adjustForComponents(width, height);
40450 this.iframeEl.setSize(width,height);
40453 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40454 this.fireEvent('resize', this, size.width, size.height);
40461 * Returns this panel's title
40464 getTitle : function(){
40466 if (typeof(this.title) != 'object') {
40471 for (var k in this.title) {
40472 if (!this.title.hasOwnProperty(k)) {
40476 if (k.indexOf('-') >= 0) {
40477 var s = k.split('-');
40478 for (var i = 0; i<s.length; i++) {
40479 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40482 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40489 * Set this panel's title
40490 * @param {String} title
40492 setTitle : function(title){
40493 this.title = title;
40495 this.region.updatePanelTitle(this, title);
40500 * Returns true is this panel was configured to be closable
40501 * @return {Boolean}
40503 isClosable : function(){
40504 return this.closable;
40507 beforeSlide : function(){
40509 this.resizeEl.clip();
40512 afterSlide : function(){
40514 this.resizeEl.unclip();
40518 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40519 * Will fail silently if the {@link #setUrl} method has not been called.
40520 * This does not activate the panel, just updates its content.
40522 refresh : function(){
40523 if(this.refreshDelegate){
40524 this.loaded = false;
40525 this.refreshDelegate();
40530 * Destroys this panel
40532 destroy : function(){
40533 this.el.removeAllListeners();
40534 var tempEl = document.createElement("span");
40535 tempEl.appendChild(this.el.dom);
40536 tempEl.innerHTML = "";
40542 * form - if the content panel contains a form - this is a reference to it.
40543 * @type {Roo.form.Form}
40547 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40548 * This contains a reference to it.
40554 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40564 * @param {Object} cfg Xtype definition of item to add.
40568 getChildContainer: function () {
40569 return this.getEl();
40574 var ret = new Roo.factory(cfg);
40579 if (cfg.xtype.match(/^Form$/)) {
40582 //if (this.footer) {
40583 // el = this.footer.container.insertSibling(false, 'before');
40585 el = this.el.createChild();
40588 this.form = new Roo.form.Form(cfg);
40591 if ( this.form.allItems.length) {
40592 this.form.render(el.dom);
40596 // should only have one of theses..
40597 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40598 // views.. should not be just added - used named prop 'view''
40600 cfg.el = this.el.appendChild(document.createElement("div"));
40603 var ret = new Roo.factory(cfg);
40605 ret.render && ret.render(false, ''); // render blank..
40615 * @class Roo.bootstrap.panel.Grid
40616 * @extends Roo.bootstrap.panel.Content
40618 * Create a new GridPanel.
40619 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40620 * @param {Object} config A the config object
40626 Roo.bootstrap.panel.Grid = function(config)
40630 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40631 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40633 config.el = this.wrapper;
40634 //this.el = this.wrapper;
40636 if (config.container) {
40637 // ctor'ed from a Border/panel.grid
40640 this.wrapper.setStyle("overflow", "hidden");
40641 this.wrapper.addClass('roo-grid-container');
40646 if(config.toolbar){
40647 var tool_el = this.wrapper.createChild();
40648 this.toolbar = Roo.factory(config.toolbar);
40650 if (config.toolbar.items) {
40651 ti = config.toolbar.items ;
40652 delete config.toolbar.items ;
40656 this.toolbar.render(tool_el);
40657 for(var i =0;i < ti.length;i++) {
40658 // Roo.log(['add child', items[i]]);
40659 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40661 this.toolbar.items = nitems;
40663 delete config.toolbar;
40666 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40667 config.grid.scrollBody = true;;
40668 config.grid.monitorWindowResize = false; // turn off autosizing
40669 config.grid.autoHeight = false;
40670 config.grid.autoWidth = false;
40672 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40674 if (config.background) {
40675 // render grid on panel activation (if panel background)
40676 this.on('activate', function(gp) {
40677 if (!gp.grid.rendered) {
40678 gp.grid.render(this.wrapper);
40679 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40684 this.grid.render(this.wrapper);
40685 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40688 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40689 // ??? needed ??? config.el = this.wrapper;
40694 // xtype created footer. - not sure if will work as we normally have to render first..
40695 if (this.footer && !this.footer.el && this.footer.xtype) {
40697 var ctr = this.grid.getView().getFooterPanel(true);
40698 this.footer.dataSource = this.grid.dataSource;
40699 this.footer = Roo.factory(this.footer, Roo);
40700 this.footer.render(ctr);
40710 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40711 getId : function(){
40712 return this.grid.id;
40716 * Returns the grid for this panel
40717 * @return {Roo.bootstrap.Table}
40719 getGrid : function(){
40723 setSize : function(width, height){
40724 if(!this.ignoreResize(width, height)){
40725 var grid = this.grid;
40726 var size = this.adjustForComponents(width, height);
40727 // tfoot is not a footer?
40730 var gridel = grid.getGridEl();
40731 gridel.setSize(size.width, size.height);
40733 var tbd = grid.getGridEl().select('tbody', true).first();
40734 var thd = grid.getGridEl().select('thead',true).first();
40735 var tbf= grid.getGridEl().select('tfoot', true).first();
40738 size.height -= tbf.getHeight();
40741 size.height -= thd.getHeight();
40744 tbd.setSize(size.width, size.height );
40745 // this is for the account management tab -seems to work there.
40746 var thd = grid.getGridEl().select('thead',true).first();
40748 // tbd.setSize(size.width, size.height - thd.getHeight());
40757 beforeSlide : function(){
40758 this.grid.getView().scroller.clip();
40761 afterSlide : function(){
40762 this.grid.getView().scroller.unclip();
40765 destroy : function(){
40766 this.grid.destroy();
40768 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40773 * @class Roo.bootstrap.panel.Nest
40774 * @extends Roo.bootstrap.panel.Content
40776 * Create a new Panel, that can contain a layout.Border.
40779 * @param {Roo.BorderLayout} layout The layout for this panel
40780 * @param {String/Object} config A string to set only the title or a config object
40782 Roo.bootstrap.panel.Nest = function(config)
40784 // construct with only one argument..
40785 /* FIXME - implement nicer consturctors
40786 if (layout.layout) {
40788 layout = config.layout;
40789 delete config.layout;
40791 if (layout.xtype && !layout.getEl) {
40792 // then layout needs constructing..
40793 layout = Roo.factory(layout, Roo);
40797 config.el = config.layout.getEl();
40799 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40801 config.layout.monitorWindowResize = false; // turn off autosizing
40802 this.layout = config.layout;
40803 this.layout.getEl().addClass("roo-layout-nested-layout");
40804 this.layout.parent = this;
40811 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40813 setSize : function(width, height){
40814 if(!this.ignoreResize(width, height)){
40815 var size = this.adjustForComponents(width, height);
40816 var el = this.layout.getEl();
40817 if (size.height < 1) {
40818 el.setWidth(size.width);
40820 el.setSize(size.width, size.height);
40822 var touch = el.dom.offsetWidth;
40823 this.layout.layout();
40824 // ie requires a double layout on the first pass
40825 if(Roo.isIE && !this.initialized){
40826 this.initialized = true;
40827 this.layout.layout();
40832 // activate all subpanels if not currently active..
40834 setActiveState : function(active){
40835 this.active = active;
40836 this.setActiveClass(active);
40839 this.fireEvent("deactivate", this);
40843 this.fireEvent("activate", this);
40844 // not sure if this should happen before or after..
40845 if (!this.layout) {
40846 return; // should not happen..
40849 for (var r in this.layout.regions) {
40850 reg = this.layout.getRegion(r);
40851 if (reg.getActivePanel()) {
40852 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40853 reg.setActivePanel(reg.getActivePanel());
40856 if (!reg.panels.length) {
40859 reg.showPanel(reg.getPanel(0));
40868 * Returns the nested BorderLayout for this panel
40869 * @return {Roo.BorderLayout}
40871 getLayout : function(){
40872 return this.layout;
40876 * Adds a xtype elements to the layout of the nested panel
40880 xtype : 'ContentPanel',
40887 xtype : 'NestedLayoutPanel',
40893 items : [ ... list of content panels or nested layout panels.. ]
40897 * @param {Object} cfg Xtype definition of item to add.
40899 addxtype : function(cfg) {
40900 return this.layout.addxtype(cfg);
40905 * Ext JS Library 1.1.1
40906 * Copyright(c) 2006-2007, Ext JS, LLC.
40908 * Originally Released Under LGPL - original licence link has changed is not relivant.
40911 * <script type="text/javascript">
40914 * @class Roo.TabPanel
40915 * @extends Roo.util.Observable
40916 * A lightweight tab container.
40920 // basic tabs 1, built from existing content
40921 var tabs = new Roo.TabPanel("tabs1");
40922 tabs.addTab("script", "View Script");
40923 tabs.addTab("markup", "View Markup");
40924 tabs.activate("script");
40926 // more advanced tabs, built from javascript
40927 var jtabs = new Roo.TabPanel("jtabs");
40928 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40930 // set up the UpdateManager
40931 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40932 var updater = tab2.getUpdateManager();
40933 updater.setDefaultUrl("ajax1.htm");
40934 tab2.on('activate', updater.refresh, updater, true);
40936 // Use setUrl for Ajax loading
40937 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40938 tab3.setUrl("ajax2.htm", null, true);
40941 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40944 jtabs.activate("jtabs-1");
40947 * Create a new TabPanel.
40948 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40949 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40951 Roo.bootstrap.panel.Tabs = function(config){
40953 * The container element for this TabPanel.
40954 * @type Roo.Element
40956 this.el = Roo.get(config.el);
40959 if(typeof config == "boolean"){
40960 this.tabPosition = config ? "bottom" : "top";
40962 Roo.apply(this, config);
40966 if(this.tabPosition == "bottom"){
40967 // if tabs are at the bottom = create the body first.
40968 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40969 this.el.addClass("roo-tabs-bottom");
40971 // next create the tabs holders
40973 if (this.tabPosition == "west"){
40975 var reg = this.region; // fake it..
40977 if (!reg.mgr.parent) {
40980 reg = reg.mgr.parent.region;
40982 Roo.log("got nest?");
40984 if (reg.mgr.getRegion('west')) {
40985 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40986 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40987 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40988 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40989 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40997 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40998 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40999 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41000 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41005 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41008 // finally - if tabs are at the top, then create the body last..
41009 if(this.tabPosition != "bottom"){
41010 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41011 * @type Roo.Element
41013 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41014 this.el.addClass("roo-tabs-top");
41018 this.bodyEl.setStyle("position", "relative");
41020 this.active = null;
41021 this.activateDelegate = this.activate.createDelegate(this);
41026 * Fires when the active tab changes
41027 * @param {Roo.TabPanel} this
41028 * @param {Roo.TabPanelItem} activePanel The new active tab
41032 * @event beforetabchange
41033 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41034 * @param {Roo.TabPanel} this
41035 * @param {Object} e Set cancel to true on this object to cancel the tab change
41036 * @param {Roo.TabPanelItem} tab The tab being changed to
41038 "beforetabchange" : true
41041 Roo.EventManager.onWindowResize(this.onResize, this);
41042 this.cpad = this.el.getPadding("lr");
41043 this.hiddenCount = 0;
41046 // toolbar on the tabbar support...
41047 if (this.toolbar) {
41048 alert("no toolbar support yet");
41049 this.toolbar = false;
41051 var tcfg = this.toolbar;
41052 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41053 this.toolbar = new Roo.Toolbar(tcfg);
41054 if (Roo.isSafari) {
41055 var tbl = tcfg.container.child('table', true);
41056 tbl.setAttribute('width', '100%');
41064 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41067 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41069 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41071 tabPosition : "top",
41073 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41075 currentTabWidth : 0,
41077 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41081 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41085 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41087 preferredTabWidth : 175,
41089 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41091 resizeTabs : false,
41093 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41095 monitorResize : true,
41097 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41099 toolbar : false, // set by caller..
41101 region : false, /// set by caller
41103 disableTooltips : true, // not used yet...
41106 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41107 * @param {String} id The id of the div to use <b>or create</b>
41108 * @param {String} text The text for the tab
41109 * @param {String} content (optional) Content to put in the TabPanelItem body
41110 * @param {Boolean} closable (optional) True to create a close icon on the tab
41111 * @return {Roo.TabPanelItem} The created TabPanelItem
41113 addTab : function(id, text, content, closable, tpl)
41115 var item = new Roo.bootstrap.panel.TabItem({
41119 closable : closable,
41122 this.addTabItem(item);
41124 item.setContent(content);
41130 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41131 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41132 * @return {Roo.TabPanelItem}
41134 getTab : function(id){
41135 return this.items[id];
41139 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41140 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41142 hideTab : function(id){
41143 var t = this.items[id];
41146 this.hiddenCount++;
41147 this.autoSizeTabs();
41152 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41153 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41155 unhideTab : function(id){
41156 var t = this.items[id];
41158 t.setHidden(false);
41159 this.hiddenCount--;
41160 this.autoSizeTabs();
41165 * Adds an existing {@link Roo.TabPanelItem}.
41166 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41168 addTabItem : function(item)
41170 this.items[item.id] = item;
41171 this.items.push(item);
41172 this.autoSizeTabs();
41173 // if(this.resizeTabs){
41174 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41175 // this.autoSizeTabs();
41177 // item.autoSize();
41182 * Removes a {@link Roo.TabPanelItem}.
41183 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41185 removeTab : function(id){
41186 var items = this.items;
41187 var tab = items[id];
41188 if(!tab) { return; }
41189 var index = items.indexOf(tab);
41190 if(this.active == tab && items.length > 1){
41191 var newTab = this.getNextAvailable(index);
41196 this.stripEl.dom.removeChild(tab.pnode.dom);
41197 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41198 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41200 items.splice(index, 1);
41201 delete this.items[tab.id];
41202 tab.fireEvent("close", tab);
41203 tab.purgeListeners();
41204 this.autoSizeTabs();
41207 getNextAvailable : function(start){
41208 var items = this.items;
41210 // look for a next tab that will slide over to
41211 // replace the one being removed
41212 while(index < items.length){
41213 var item = items[++index];
41214 if(item && !item.isHidden()){
41218 // if one isn't found select the previous tab (on the left)
41221 var item = items[--index];
41222 if(item && !item.isHidden()){
41230 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41231 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41233 disableTab : function(id){
41234 var tab = this.items[id];
41235 if(tab && this.active != tab){
41241 * Enables a {@link Roo.TabPanelItem} that is disabled.
41242 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41244 enableTab : function(id){
41245 var tab = this.items[id];
41250 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41251 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41252 * @return {Roo.TabPanelItem} The TabPanelItem.
41254 activate : function(id)
41256 //Roo.log('activite:' + id);
41258 var tab = this.items[id];
41262 if(tab == this.active || tab.disabled){
41266 this.fireEvent("beforetabchange", this, e, tab);
41267 if(e.cancel !== true && !tab.disabled){
41269 this.active.hide();
41271 this.active = this.items[id];
41272 this.active.show();
41273 this.fireEvent("tabchange", this, this.active);
41279 * Gets the active {@link Roo.TabPanelItem}.
41280 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41282 getActiveTab : function(){
41283 return this.active;
41287 * Updates the tab body element to fit the height of the container element
41288 * for overflow scrolling
41289 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41291 syncHeight : function(targetHeight){
41292 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41293 var bm = this.bodyEl.getMargins();
41294 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41295 this.bodyEl.setHeight(newHeight);
41299 onResize : function(){
41300 if(this.monitorResize){
41301 this.autoSizeTabs();
41306 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41308 beginUpdate : function(){
41309 this.updating = true;
41313 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41315 endUpdate : function(){
41316 this.updating = false;
41317 this.autoSizeTabs();
41321 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41323 autoSizeTabs : function()
41325 var count = this.items.length;
41326 var vcount = count - this.hiddenCount;
41329 this.stripEl.hide();
41331 this.stripEl.show();
41334 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41339 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41340 var availWidth = Math.floor(w / vcount);
41341 var b = this.stripBody;
41342 if(b.getWidth() > w){
41343 var tabs = this.items;
41344 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41345 if(availWidth < this.minTabWidth){
41346 /*if(!this.sleft){ // incomplete scrolling code
41347 this.createScrollButtons();
41350 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41353 if(this.currentTabWidth < this.preferredTabWidth){
41354 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41360 * Returns the number of tabs in this TabPanel.
41363 getCount : function(){
41364 return this.items.length;
41368 * Resizes all the tabs to the passed width
41369 * @param {Number} The new width
41371 setTabWidth : function(width){
41372 this.currentTabWidth = width;
41373 for(var i = 0, len = this.items.length; i < len; i++) {
41374 if(!this.items[i].isHidden()) {
41375 this.items[i].setWidth(width);
41381 * Destroys this TabPanel
41382 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41384 destroy : function(removeEl){
41385 Roo.EventManager.removeResizeListener(this.onResize, this);
41386 for(var i = 0, len = this.items.length; i < len; i++){
41387 this.items[i].purgeListeners();
41389 if(removeEl === true){
41390 this.el.update("");
41395 createStrip : function(container)
41397 var strip = document.createElement("nav");
41398 strip.className = Roo.bootstrap.version == 4 ?
41399 "navbar-light bg-light" :
41400 "navbar navbar-default"; //"x-tabs-wrap";
41401 container.appendChild(strip);
41405 createStripList : function(strip)
41407 // div wrapper for retard IE
41408 // returns the "tr" element.
41409 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41410 //'<div class="x-tabs-strip-wrap">'+
41411 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41412 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41413 return strip.firstChild; //.firstChild.firstChild.firstChild;
41415 createBody : function(container)
41417 var body = document.createElement("div");
41418 Roo.id(body, "tab-body");
41419 //Roo.fly(body).addClass("x-tabs-body");
41420 Roo.fly(body).addClass("tab-content");
41421 container.appendChild(body);
41424 createItemBody :function(bodyEl, id){
41425 var body = Roo.getDom(id);
41427 body = document.createElement("div");
41430 //Roo.fly(body).addClass("x-tabs-item-body");
41431 Roo.fly(body).addClass("tab-pane");
41432 bodyEl.insertBefore(body, bodyEl.firstChild);
41436 createStripElements : function(stripEl, text, closable, tpl)
41438 var td = document.createElement("li"); // was td..
41439 td.className = 'nav-item';
41441 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41444 stripEl.appendChild(td);
41446 td.className = "x-tabs-closable";
41447 if(!this.closeTpl){
41448 this.closeTpl = new Roo.Template(
41449 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41450 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41451 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41454 var el = this.closeTpl.overwrite(td, {"text": text});
41455 var close = el.getElementsByTagName("div")[0];
41456 var inner = el.getElementsByTagName("em")[0];
41457 return {"el": el, "close": close, "inner": inner};
41460 // not sure what this is..
41461 // if(!this.tabTpl){
41462 //this.tabTpl = new Roo.Template(
41463 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41464 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41466 // this.tabTpl = new Roo.Template(
41467 // '<a href="#">' +
41468 // '<span unselectable="on"' +
41469 // (this.disableTooltips ? '' : ' title="{text}"') +
41470 // ' >{text}</span></a>'
41476 var template = tpl || this.tabTpl || false;
41479 template = new Roo.Template(
41480 Roo.bootstrap.version == 4 ?
41482 '<a class="nav-link" href="#" unselectable="on"' +
41483 (this.disableTooltips ? '' : ' title="{text}"') +
41486 '<a class="nav-link" href="#">' +
41487 '<span unselectable="on"' +
41488 (this.disableTooltips ? '' : ' title="{text}"') +
41489 ' >{text}</span></a>'
41494 switch (typeof(template)) {
41498 template = new Roo.Template(template);
41504 var el = template.overwrite(td, {"text": text});
41506 var inner = el.getElementsByTagName("span")[0];
41508 return {"el": el, "inner": inner};
41516 * @class Roo.TabPanelItem
41517 * @extends Roo.util.Observable
41518 * Represents an individual item (tab plus body) in a TabPanel.
41519 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41520 * @param {String} id The id of this TabPanelItem
41521 * @param {String} text The text for the tab of this TabPanelItem
41522 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41524 Roo.bootstrap.panel.TabItem = function(config){
41526 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41527 * @type Roo.TabPanel
41529 this.tabPanel = config.panel;
41531 * The id for this TabPanelItem
41534 this.id = config.id;
41536 this.disabled = false;
41538 this.text = config.text;
41540 this.loaded = false;
41541 this.closable = config.closable;
41544 * The body element for this TabPanelItem.
41545 * @type Roo.Element
41547 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41548 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41549 this.bodyEl.setStyle("display", "block");
41550 this.bodyEl.setStyle("zoom", "1");
41551 //this.hideAction();
41553 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41555 this.el = Roo.get(els.el);
41556 this.inner = Roo.get(els.inner, true);
41557 this.textEl = Roo.bootstrap.version == 4 ?
41558 this.el : Roo.get(this.el.dom.firstChild, true);
41560 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41561 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41564 // this.el.on("mousedown", this.onTabMouseDown, this);
41565 this.el.on("click", this.onTabClick, this);
41567 if(config.closable){
41568 var c = Roo.get(els.close, true);
41569 c.dom.title = this.closeText;
41570 c.addClassOnOver("close-over");
41571 c.on("click", this.closeClick, this);
41577 * Fires when this tab becomes the active tab.
41578 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41579 * @param {Roo.TabPanelItem} this
41583 * @event beforeclose
41584 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41585 * @param {Roo.TabPanelItem} this
41586 * @param {Object} e Set cancel to true on this object to cancel the close.
41588 "beforeclose": true,
41591 * Fires when this tab is closed.
41592 * @param {Roo.TabPanelItem} this
41596 * @event deactivate
41597 * Fires when this tab is no longer the active tab.
41598 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41599 * @param {Roo.TabPanelItem} this
41601 "deactivate" : true
41603 this.hidden = false;
41605 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41608 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41610 purgeListeners : function(){
41611 Roo.util.Observable.prototype.purgeListeners.call(this);
41612 this.el.removeAllListeners();
41615 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41618 this.status_node.addClass("active");
41621 this.tabPanel.stripWrap.repaint();
41623 this.fireEvent("activate", this.tabPanel, this);
41627 * Returns true if this tab is the active tab.
41628 * @return {Boolean}
41630 isActive : function(){
41631 return this.tabPanel.getActiveTab() == this;
41635 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41638 this.status_node.removeClass("active");
41640 this.fireEvent("deactivate", this.tabPanel, this);
41643 hideAction : function(){
41644 this.bodyEl.hide();
41645 this.bodyEl.setStyle("position", "absolute");
41646 this.bodyEl.setLeft("-20000px");
41647 this.bodyEl.setTop("-20000px");
41650 showAction : function(){
41651 this.bodyEl.setStyle("position", "relative");
41652 this.bodyEl.setTop("");
41653 this.bodyEl.setLeft("");
41654 this.bodyEl.show();
41658 * Set the tooltip for the tab.
41659 * @param {String} tooltip The tab's tooltip
41661 setTooltip : function(text){
41662 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41663 this.textEl.dom.qtip = text;
41664 this.textEl.dom.removeAttribute('title');
41666 this.textEl.dom.title = text;
41670 onTabClick : function(e){
41671 e.preventDefault();
41672 this.tabPanel.activate(this.id);
41675 onTabMouseDown : function(e){
41676 e.preventDefault();
41677 this.tabPanel.activate(this.id);
41680 getWidth : function(){
41681 return this.inner.getWidth();
41684 setWidth : function(width){
41685 var iwidth = width - this.linode.getPadding("lr");
41686 this.inner.setWidth(iwidth);
41687 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41688 this.linode.setWidth(width);
41692 * Show or hide the tab
41693 * @param {Boolean} hidden True to hide or false to show.
41695 setHidden : function(hidden){
41696 this.hidden = hidden;
41697 this.linode.setStyle("display", hidden ? "none" : "");
41701 * Returns true if this tab is "hidden"
41702 * @return {Boolean}
41704 isHidden : function(){
41705 return this.hidden;
41709 * Returns the text for this tab
41712 getText : function(){
41716 autoSize : function(){
41717 //this.el.beginMeasure();
41718 this.textEl.setWidth(1);
41720 * #2804 [new] Tabs in Roojs
41721 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41723 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41724 //this.el.endMeasure();
41728 * Sets the text for the tab (Note: this also sets the tooltip text)
41729 * @param {String} text The tab's text and tooltip
41731 setText : function(text){
41733 this.textEl.update(text);
41734 this.setTooltip(text);
41735 //if(!this.tabPanel.resizeTabs){
41736 // this.autoSize();
41740 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41742 activate : function(){
41743 this.tabPanel.activate(this.id);
41747 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41749 disable : function(){
41750 if(this.tabPanel.active != this){
41751 this.disabled = true;
41752 this.status_node.addClass("disabled");
41757 * Enables this TabPanelItem if it was previously disabled.
41759 enable : function(){
41760 this.disabled = false;
41761 this.status_node.removeClass("disabled");
41765 * Sets the content for this TabPanelItem.
41766 * @param {String} content The content
41767 * @param {Boolean} loadScripts true to look for and load scripts
41769 setContent : function(content, loadScripts){
41770 this.bodyEl.update(content, loadScripts);
41774 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41775 * @return {Roo.UpdateManager} The UpdateManager
41777 getUpdateManager : function(){
41778 return this.bodyEl.getUpdateManager();
41782 * Set a URL to be used to load the content for this TabPanelItem.
41783 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41784 * @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)
41785 * @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)
41786 * @return {Roo.UpdateManager} The UpdateManager
41788 setUrl : function(url, params, loadOnce){
41789 if(this.refreshDelegate){
41790 this.un('activate', this.refreshDelegate);
41792 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41793 this.on("activate", this.refreshDelegate);
41794 return this.bodyEl.getUpdateManager();
41798 _handleRefresh : function(url, params, loadOnce){
41799 if(!loadOnce || !this.loaded){
41800 var updater = this.bodyEl.getUpdateManager();
41801 updater.update(url, params, this._setLoaded.createDelegate(this));
41806 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41807 * Will fail silently if the setUrl method has not been called.
41808 * This does not activate the panel, just updates its content.
41810 refresh : function(){
41811 if(this.refreshDelegate){
41812 this.loaded = false;
41813 this.refreshDelegate();
41818 _setLoaded : function(){
41819 this.loaded = true;
41823 closeClick : function(e){
41826 this.fireEvent("beforeclose", this, o);
41827 if(o.cancel !== true){
41828 this.tabPanel.removeTab(this.id);
41832 * The text displayed in the tooltip for the close icon.
41835 closeText : "Close this tab"
41838 * This script refer to:
41839 * Title: International Telephone Input
41840 * Author: Jack O'Connor
41841 * Code version: v12.1.12
41842 * Availability: https://github.com/jackocnr/intl-tel-input.git
41845 Roo.bootstrap.PhoneInputData = function() {
41848 "Afghanistan (افغانستان)",
41853 "Albania (Shqipëri)",
41858 "Algeria (الجزائر)",
41883 "Antigua and Barbuda",
41893 "Armenia (Հայաստան)",
41909 "Austria (Österreich)",
41914 "Azerbaijan (Azərbaycan)",
41924 "Bahrain (البحرين)",
41929 "Bangladesh (বাংলাদেশ)",
41939 "Belarus (Беларусь)",
41944 "Belgium (België)",
41974 "Bosnia and Herzegovina (Босна и Херцеговина)",
41989 "British Indian Ocean Territory",
41994 "British Virgin Islands",
42004 "Bulgaria (България)",
42014 "Burundi (Uburundi)",
42019 "Cambodia (កម្ពុជា)",
42024 "Cameroon (Cameroun)",
42033 ["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"]
42036 "Cape Verde (Kabu Verdi)",
42041 "Caribbean Netherlands",
42052 "Central African Republic (République centrafricaine)",
42072 "Christmas Island",
42078 "Cocos (Keeling) Islands",
42089 "Comoros (جزر القمر)",
42094 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42099 "Congo (Republic) (Congo-Brazzaville)",
42119 "Croatia (Hrvatska)",
42140 "Czech Republic (Česká republika)",
42145 "Denmark (Danmark)",
42160 "Dominican Republic (República Dominicana)",
42164 ["809", "829", "849"]
42182 "Equatorial Guinea (Guinea Ecuatorial)",
42202 "Falkland Islands (Islas Malvinas)",
42207 "Faroe Islands (Føroyar)",
42228 "French Guiana (Guyane française)",
42233 "French Polynesia (Polynésie française)",
42248 "Georgia (საქართველო)",
42253 "Germany (Deutschland)",
42273 "Greenland (Kalaallit Nunaat)",
42310 "Guinea-Bissau (Guiné Bissau)",
42335 "Hungary (Magyarország)",
42340 "Iceland (Ísland)",
42360 "Iraq (العراق)",
42376 "Israel (ישראל)",
42403 "Jordan (الأردن)",
42408 "Kazakhstan (Казахстан)",
42429 "Kuwait (الكويت)",
42434 "Kyrgyzstan (Кыргызстан)",
42444 "Latvia (Latvija)",
42449 "Lebanon (لبنان)",
42464 "Libya (ليبيا)",
42474 "Lithuania (Lietuva)",
42489 "Macedonia (FYROM) (Македонија)",
42494 "Madagascar (Madagasikara)",
42524 "Marshall Islands",
42534 "Mauritania (موريتانيا)",
42539 "Mauritius (Moris)",
42560 "Moldova (Republica Moldova)",
42570 "Mongolia (Монгол)",
42575 "Montenegro (Crna Gora)",
42585 "Morocco (المغرب)",
42591 "Mozambique (Moçambique)",
42596 "Myanmar (Burma) (မြန်မာ)",
42601 "Namibia (Namibië)",
42616 "Netherlands (Nederland)",
42621 "New Caledonia (Nouvelle-Calédonie)",
42656 "North Korea (조선 민주주의 인민 공화국)",
42661 "Northern Mariana Islands",
42677 "Pakistan (پاکستان)",
42687 "Palestine (فلسطين)",
42697 "Papua New Guinea",
42739 "Réunion (La Réunion)",
42745 "Romania (România)",
42761 "Saint Barthélemy",
42772 "Saint Kitts and Nevis",
42782 "Saint Martin (Saint-Martin (partie française))",
42788 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42793 "Saint Vincent and the Grenadines",
42808 "São Tomé and Príncipe (São Tomé e Príncipe)",
42813 "Saudi Arabia (المملكة العربية السعودية)",
42818 "Senegal (Sénégal)",
42848 "Slovakia (Slovensko)",
42853 "Slovenia (Slovenija)",
42863 "Somalia (Soomaaliya)",
42873 "South Korea (대한민국)",
42878 "South Sudan (جنوب السودان)",
42888 "Sri Lanka (ශ්රී ලංකාව)",
42893 "Sudan (السودان)",
42903 "Svalbard and Jan Mayen",
42914 "Sweden (Sverige)",
42919 "Switzerland (Schweiz)",
42924 "Syria (سوريا)",
42969 "Trinidad and Tobago",
42974 "Tunisia (تونس)",
42979 "Turkey (Türkiye)",
42989 "Turks and Caicos Islands",
42999 "U.S. Virgin Islands",
43009 "Ukraine (Україна)",
43014 "United Arab Emirates (الإمارات العربية المتحدة)",
43036 "Uzbekistan (Oʻzbekiston)",
43046 "Vatican City (Città del Vaticano)",
43057 "Vietnam (Việt Nam)",
43062 "Wallis and Futuna (Wallis-et-Futuna)",
43067 "Western Sahara (الصحراء الغربية)",
43073 "Yemen (اليمن)",
43097 * This script refer to:
43098 * Title: International Telephone Input
43099 * Author: Jack O'Connor
43100 * Code version: v12.1.12
43101 * Availability: https://github.com/jackocnr/intl-tel-input.git
43105 * @class Roo.bootstrap.PhoneInput
43106 * @extends Roo.bootstrap.TriggerField
43107 * An input with International dial-code selection
43109 * @cfg {String} defaultDialCode default '+852'
43110 * @cfg {Array} preferedCountries default []
43113 * Create a new PhoneInput.
43114 * @param {Object} config Configuration options
43117 Roo.bootstrap.PhoneInput = function(config) {
43118 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43121 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43123 listWidth: undefined,
43125 selectedClass: 'active',
43127 invalidClass : "has-warning",
43129 validClass: 'has-success',
43131 allowed: '0123456789',
43136 * @cfg {String} defaultDialCode The default dial code when initializing the input
43138 defaultDialCode: '+852',
43141 * @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
43143 preferedCountries: false,
43145 getAutoCreate : function()
43147 var data = Roo.bootstrap.PhoneInputData();
43148 var align = this.labelAlign || this.parentLabelAlign();
43151 this.allCountries = [];
43152 this.dialCodeMapping = [];
43154 for (var i = 0; i < data.length; i++) {
43156 this.allCountries[i] = {
43160 priority: c[3] || 0,
43161 areaCodes: c[4] || null
43163 this.dialCodeMapping[c[2]] = {
43166 priority: c[3] || 0,
43167 areaCodes: c[4] || null
43179 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43180 maxlength: this.max_length,
43181 cls : 'form-control tel-input',
43182 autocomplete: 'new-password'
43185 var hiddenInput = {
43188 cls: 'hidden-tel-input'
43192 hiddenInput.name = this.name;
43195 if (this.disabled) {
43196 input.disabled = true;
43199 var flag_container = {
43216 cls: this.hasFeedback ? 'has-feedback' : '',
43222 cls: 'dial-code-holder',
43229 cls: 'roo-select2-container input-group',
43236 if (this.fieldLabel.length) {
43239 tooltip: 'This field is required'
43245 cls: 'control-label',
43251 html: this.fieldLabel
43254 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43260 if(this.indicatorpos == 'right') {
43261 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43268 if(align == 'left') {
43276 if(this.labelWidth > 12){
43277 label.style = "width: " + this.labelWidth + 'px';
43279 if(this.labelWidth < 13 && this.labelmd == 0){
43280 this.labelmd = this.labelWidth;
43282 if(this.labellg > 0){
43283 label.cls += ' col-lg-' + this.labellg;
43284 input.cls += ' col-lg-' + (12 - this.labellg);
43286 if(this.labelmd > 0){
43287 label.cls += ' col-md-' + this.labelmd;
43288 container.cls += ' col-md-' + (12 - this.labelmd);
43290 if(this.labelsm > 0){
43291 label.cls += ' col-sm-' + this.labelsm;
43292 container.cls += ' col-sm-' + (12 - this.labelsm);
43294 if(this.labelxs > 0){
43295 label.cls += ' col-xs-' + this.labelxs;
43296 container.cls += ' col-xs-' + (12 - this.labelxs);
43306 var settings = this;
43308 ['xs','sm','md','lg'].map(function(size){
43309 if (settings[size]) {
43310 cfg.cls += ' col-' + size + '-' + settings[size];
43314 this.store = new Roo.data.Store({
43315 proxy : new Roo.data.MemoryProxy({}),
43316 reader : new Roo.data.JsonReader({
43327 'name' : 'dialCode',
43331 'name' : 'priority',
43335 'name' : 'areaCodes',
43342 if(!this.preferedCountries) {
43343 this.preferedCountries = [
43350 var p = this.preferedCountries.reverse();
43353 for (var i = 0; i < p.length; i++) {
43354 for (var j = 0; j < this.allCountries.length; j++) {
43355 if(this.allCountries[j].iso2 == p[i]) {
43356 var t = this.allCountries[j];
43357 this.allCountries.splice(j,1);
43358 this.allCountries.unshift(t);
43364 this.store.proxy.data = {
43366 data: this.allCountries
43372 initEvents : function()
43375 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43377 this.indicator = this.indicatorEl();
43378 this.flag = this.flagEl();
43379 this.dialCodeHolder = this.dialCodeHolderEl();
43381 this.trigger = this.el.select('div.flag-box',true).first();
43382 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43387 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43388 _this.list.setWidth(lw);
43391 this.list.on('mouseover', this.onViewOver, this);
43392 this.list.on('mousemove', this.onViewMove, this);
43393 this.inputEl().on("keyup", this.onKeyUp, this);
43394 this.inputEl().on("keypress", this.onKeyPress, this);
43396 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43398 this.view = new Roo.View(this.list, this.tpl, {
43399 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43402 this.view.on('click', this.onViewClick, this);
43403 this.setValue(this.defaultDialCode);
43406 onTriggerClick : function(e)
43408 Roo.log('trigger click');
43413 if(this.isExpanded()){
43415 this.hasFocus = false;
43417 this.store.load({});
43418 this.hasFocus = true;
43423 isExpanded : function()
43425 return this.list.isVisible();
43428 collapse : function()
43430 if(!this.isExpanded()){
43434 Roo.get(document).un('mousedown', this.collapseIf, this);
43435 Roo.get(document).un('mousewheel', this.collapseIf, this);
43436 this.fireEvent('collapse', this);
43440 expand : function()
43444 if(this.isExpanded() || !this.hasFocus){
43448 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43449 this.list.setWidth(lw);
43452 this.restrictHeight();
43454 Roo.get(document).on('mousedown', this.collapseIf, this);
43455 Roo.get(document).on('mousewheel', this.collapseIf, this);
43457 this.fireEvent('expand', this);
43460 restrictHeight : function()
43462 this.list.alignTo(this.inputEl(), this.listAlign);
43463 this.list.alignTo(this.inputEl(), this.listAlign);
43466 onViewOver : function(e, t)
43468 if(this.inKeyMode){
43471 var item = this.view.findItemFromChild(t);
43474 var index = this.view.indexOf(item);
43475 this.select(index, false);
43480 onViewClick : function(view, doFocus, el, e)
43482 var index = this.view.getSelectedIndexes()[0];
43484 var r = this.store.getAt(index);
43487 this.onSelect(r, index);
43489 if(doFocus !== false && !this.blockFocus){
43490 this.inputEl().focus();
43494 onViewMove : function(e, t)
43496 this.inKeyMode = false;
43499 select : function(index, scrollIntoView)
43501 this.selectedIndex = index;
43502 this.view.select(index);
43503 if(scrollIntoView !== false){
43504 var el = this.view.getNode(index);
43506 this.list.scrollChildIntoView(el, false);
43511 createList : function()
43513 this.list = Roo.get(document.body).createChild({
43515 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43516 style: 'display:none'
43519 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43522 collapseIf : function(e)
43524 var in_combo = e.within(this.el);
43525 var in_list = e.within(this.list);
43526 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43528 if (in_combo || in_list || is_list) {
43534 onSelect : function(record, index)
43536 if(this.fireEvent('beforeselect', this, record, index) !== false){
43538 this.setFlagClass(record.data.iso2);
43539 this.setDialCode(record.data.dialCode);
43540 this.hasFocus = false;
43542 this.fireEvent('select', this, record, index);
43546 flagEl : function()
43548 var flag = this.el.select('div.flag',true).first();
43555 dialCodeHolderEl : function()
43557 var d = this.el.select('input.dial-code-holder',true).first();
43564 setDialCode : function(v)
43566 this.dialCodeHolder.dom.value = '+'+v;
43569 setFlagClass : function(n)
43571 this.flag.dom.className = 'flag '+n;
43574 getValue : function()
43576 var v = this.inputEl().getValue();
43577 if(this.dialCodeHolder) {
43578 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43583 setValue : function(v)
43585 var d = this.getDialCode(v);
43587 //invalid dial code
43588 if(v.length == 0 || !d || d.length == 0) {
43590 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43591 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43597 this.setFlagClass(this.dialCodeMapping[d].iso2);
43598 this.setDialCode(d);
43599 this.inputEl().dom.value = v.replace('+'+d,'');
43600 this.hiddenEl().dom.value = this.getValue();
43605 getDialCode : function(v)
43609 if (v.length == 0) {
43610 return this.dialCodeHolder.dom.value;
43614 if (v.charAt(0) != "+") {
43617 var numericChars = "";
43618 for (var i = 1; i < v.length; i++) {
43619 var c = v.charAt(i);
43622 if (this.dialCodeMapping[numericChars]) {
43623 dialCode = v.substr(1, i);
43625 if (numericChars.length == 4) {
43635 this.setValue(this.defaultDialCode);
43639 hiddenEl : function()
43641 return this.el.select('input.hidden-tel-input',true).first();
43644 // after setting val
43645 onKeyUp : function(e){
43646 this.setValue(this.getValue());
43649 onKeyPress : function(e){
43650 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43657 * @class Roo.bootstrap.MoneyField
43658 * @extends Roo.bootstrap.ComboBox
43659 * Bootstrap MoneyField class
43662 * Create a new MoneyField.
43663 * @param {Object} config Configuration options
43666 Roo.bootstrap.MoneyField = function(config) {
43668 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43672 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43675 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43677 allowDecimals : true,
43679 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43681 decimalSeparator : ".",
43683 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43685 decimalPrecision : 0,
43687 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43689 allowNegative : true,
43691 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43695 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43697 minValue : Number.NEGATIVE_INFINITY,
43699 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43701 maxValue : Number.MAX_VALUE,
43703 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43705 minText : "The minimum value for this field is {0}",
43707 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43709 maxText : "The maximum value for this field is {0}",
43711 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43712 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43714 nanText : "{0} is not a valid number",
43716 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43720 * @cfg {String} defaults currency of the MoneyField
43721 * value should be in lkey
43723 defaultCurrency : false,
43725 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43727 thousandsDelimiter : false,
43729 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43740 getAutoCreate : function()
43742 var align = this.labelAlign || this.parentLabelAlign();
43754 cls : 'form-control roo-money-amount-input',
43755 autocomplete: 'new-password'
43758 var hiddenInput = {
43762 cls: 'hidden-number-input'
43765 if(this.max_length) {
43766 input.maxlength = this.max_length;
43770 hiddenInput.name = this.name;
43773 if (this.disabled) {
43774 input.disabled = true;
43777 var clg = 12 - this.inputlg;
43778 var cmd = 12 - this.inputmd;
43779 var csm = 12 - this.inputsm;
43780 var cxs = 12 - this.inputxs;
43784 cls : 'row roo-money-field',
43788 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43792 cls: 'roo-select2-container input-group',
43796 cls : 'form-control roo-money-currency-input',
43797 autocomplete: 'new-password',
43799 name : this.currencyName
43803 cls : 'input-group-addon',
43817 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43821 cls: this.hasFeedback ? 'has-feedback' : '',
43832 if (this.fieldLabel.length) {
43835 tooltip: 'This field is required'
43841 cls: 'control-label',
43847 html: this.fieldLabel
43850 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43856 if(this.indicatorpos == 'right') {
43857 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43864 if(align == 'left') {
43872 if(this.labelWidth > 12){
43873 label.style = "width: " + this.labelWidth + 'px';
43875 if(this.labelWidth < 13 && this.labelmd == 0){
43876 this.labelmd = this.labelWidth;
43878 if(this.labellg > 0){
43879 label.cls += ' col-lg-' + this.labellg;
43880 input.cls += ' col-lg-' + (12 - this.labellg);
43882 if(this.labelmd > 0){
43883 label.cls += ' col-md-' + this.labelmd;
43884 container.cls += ' col-md-' + (12 - this.labelmd);
43886 if(this.labelsm > 0){
43887 label.cls += ' col-sm-' + this.labelsm;
43888 container.cls += ' col-sm-' + (12 - this.labelsm);
43890 if(this.labelxs > 0){
43891 label.cls += ' col-xs-' + this.labelxs;
43892 container.cls += ' col-xs-' + (12 - this.labelxs);
43903 var settings = this;
43905 ['xs','sm','md','lg'].map(function(size){
43906 if (settings[size]) {
43907 cfg.cls += ' col-' + size + '-' + settings[size];
43914 initEvents : function()
43916 this.indicator = this.indicatorEl();
43918 this.initCurrencyEvent();
43920 this.initNumberEvent();
43923 initCurrencyEvent : function()
43926 throw "can not find store for combo";
43929 this.store = Roo.factory(this.store, Roo.data);
43930 this.store.parent = this;
43934 this.triggerEl = this.el.select('.input-group-addon', true).first();
43936 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43941 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43942 _this.list.setWidth(lw);
43945 this.list.on('mouseover', this.onViewOver, this);
43946 this.list.on('mousemove', this.onViewMove, this);
43947 this.list.on('scroll', this.onViewScroll, this);
43950 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43953 this.view = new Roo.View(this.list, this.tpl, {
43954 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43957 this.view.on('click', this.onViewClick, this);
43959 this.store.on('beforeload', this.onBeforeLoad, this);
43960 this.store.on('load', this.onLoad, this);
43961 this.store.on('loadexception', this.onLoadException, this);
43963 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43964 "up" : function(e){
43965 this.inKeyMode = true;
43969 "down" : function(e){
43970 if(!this.isExpanded()){
43971 this.onTriggerClick();
43973 this.inKeyMode = true;
43978 "enter" : function(e){
43981 if(this.fireEvent("specialkey", this, e)){
43982 this.onViewClick(false);
43988 "esc" : function(e){
43992 "tab" : function(e){
43995 if(this.fireEvent("specialkey", this, e)){
43996 this.onViewClick(false);
44004 doRelay : function(foo, bar, hname){
44005 if(hname == 'down' || this.scope.isExpanded()){
44006 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44014 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44018 initNumberEvent : function(e)
44020 this.inputEl().on("keydown" , this.fireKey, this);
44021 this.inputEl().on("focus", this.onFocus, this);
44022 this.inputEl().on("blur", this.onBlur, this);
44024 this.inputEl().relayEvent('keyup', this);
44026 if(this.indicator){
44027 this.indicator.addClass('invisible');
44030 this.originalValue = this.getValue();
44032 if(this.validationEvent == 'keyup'){
44033 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44034 this.inputEl().on('keyup', this.filterValidation, this);
44036 else if(this.validationEvent !== false){
44037 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44040 if(this.selectOnFocus){
44041 this.on("focus", this.preFocus, this);
44044 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44045 this.inputEl().on("keypress", this.filterKeys, this);
44047 this.inputEl().relayEvent('keypress', this);
44050 var allowed = "0123456789";
44052 if(this.allowDecimals){
44053 allowed += this.decimalSeparator;
44056 if(this.allowNegative){
44060 if(this.thousandsDelimiter) {
44064 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44066 var keyPress = function(e){
44068 var k = e.getKey();
44070 var c = e.getCharCode();
44073 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44074 allowed.indexOf(String.fromCharCode(c)) === -1
44080 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44084 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44089 this.inputEl().on("keypress", keyPress, this);
44093 onTriggerClick : function(e)
44100 this.loadNext = false;
44102 if(this.isExpanded()){
44107 this.hasFocus = true;
44109 if(this.triggerAction == 'all') {
44110 this.doQuery(this.allQuery, true);
44114 this.doQuery(this.getRawValue());
44117 getCurrency : function()
44119 var v = this.currencyEl().getValue();
44124 restrictHeight : function()
44126 this.list.alignTo(this.currencyEl(), this.listAlign);
44127 this.list.alignTo(this.currencyEl(), this.listAlign);
44130 onViewClick : function(view, doFocus, el, e)
44132 var index = this.view.getSelectedIndexes()[0];
44134 var r = this.store.getAt(index);
44137 this.onSelect(r, index);
44141 onSelect : function(record, index){
44143 if(this.fireEvent('beforeselect', this, record, index) !== false){
44145 this.setFromCurrencyData(index > -1 ? record.data : false);
44149 this.fireEvent('select', this, record, index);
44153 setFromCurrencyData : function(o)
44157 this.lastCurrency = o;
44159 if (this.currencyField) {
44160 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44162 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44165 this.lastSelectionText = currency;
44167 //setting default currency
44168 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44169 this.setCurrency(this.defaultCurrency);
44173 this.setCurrency(currency);
44176 setFromData : function(o)
44180 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44182 this.setFromCurrencyData(c);
44187 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44189 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44192 this.setValue(value);
44196 setCurrency : function(v)
44198 this.currencyValue = v;
44201 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44206 setValue : function(v)
44208 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44214 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44216 this.inputEl().dom.value = (v == '') ? '' :
44217 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44219 if(!this.allowZero && v === '0') {
44220 this.hiddenEl().dom.value = '';
44221 this.inputEl().dom.value = '';
44228 getRawValue : function()
44230 var v = this.inputEl().getValue();
44235 getValue : function()
44237 return this.fixPrecision(this.parseValue(this.getRawValue()));
44240 parseValue : function(value)
44242 if(this.thousandsDelimiter) {
44244 r = new RegExp(",", "g");
44245 value = value.replace(r, "");
44248 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44249 return isNaN(value) ? '' : value;
44253 fixPrecision : function(value)
44255 if(this.thousandsDelimiter) {
44257 r = new RegExp(",", "g");
44258 value = value.replace(r, "");
44261 var nan = isNaN(value);
44263 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44264 return nan ? '' : value;
44266 return parseFloat(value).toFixed(this.decimalPrecision);
44269 decimalPrecisionFcn : function(v)
44271 return Math.floor(v);
44274 validateValue : function(value)
44276 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44280 var num = this.parseValue(value);
44283 this.markInvalid(String.format(this.nanText, value));
44287 if(num < this.minValue){
44288 this.markInvalid(String.format(this.minText, this.minValue));
44292 if(num > this.maxValue){
44293 this.markInvalid(String.format(this.maxText, this.maxValue));
44300 validate : function()
44302 if(this.disabled || this.allowBlank){
44307 var currency = this.getCurrency();
44309 if(this.validateValue(this.getRawValue()) && currency.length){
44314 this.markInvalid();
44318 getName: function()
44323 beforeBlur : function()
44329 var v = this.parseValue(this.getRawValue());
44336 onBlur : function()
44340 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44341 //this.el.removeClass(this.focusClass);
44344 this.hasFocus = false;
44346 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44350 var v = this.getValue();
44352 if(String(v) !== String(this.startValue)){
44353 this.fireEvent('change', this, v, this.startValue);
44356 this.fireEvent("blur", this);
44359 inputEl : function()
44361 return this.el.select('.roo-money-amount-input', true).first();
44364 currencyEl : function()
44366 return this.el.select('.roo-money-currency-input', true).first();
44369 hiddenEl : function()
44371 return this.el.select('input.hidden-number-input',true).first();
44375 * @class Roo.bootstrap.BezierSignature
44376 * @extends Roo.bootstrap.Component
44377 * Bootstrap BezierSignature class
44378 * This script refer to:
44379 * Title: Signature Pad
44381 * Availability: https://github.com/szimek/signature_pad
44384 * Create a new BezierSignature
44385 * @param {Object} config The config object
44388 Roo.bootstrap.BezierSignature = function(config){
44389 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44395 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44402 mouse_btn_down: true,
44405 * @cfg {int} canvas height
44407 canvas_height: '200px',
44410 * @cfg {float|function} Radius of a single dot.
44415 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44420 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44425 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44430 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44435 * @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.
44437 bg_color: 'rgba(0, 0, 0, 0)',
44440 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44442 dot_color: 'black',
44445 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44447 velocity_filter_weight: 0.7,
44450 * @cfg {function} Callback when stroke begin.
44455 * @cfg {function} Callback when stroke end.
44459 getAutoCreate : function()
44461 var cls = 'roo-signature column';
44464 cls += ' ' + this.cls;
44474 for(var i = 0; i < col_sizes.length; i++) {
44475 if(this[col_sizes[i]]) {
44476 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44486 cls: 'roo-signature-body',
44490 cls: 'roo-signature-body-canvas',
44491 height: this.canvas_height,
44492 width: this.canvas_width
44499 style: 'display: none'
44507 initEvents: function()
44509 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44511 var canvas = this.canvasEl();
44513 // mouse && touch event swapping...
44514 canvas.dom.style.touchAction = 'none';
44515 canvas.dom.style.msTouchAction = 'none';
44517 this.mouse_btn_down = false;
44518 canvas.on('mousedown', this._handleMouseDown, this);
44519 canvas.on('mousemove', this._handleMouseMove, this);
44520 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44522 if (window.PointerEvent) {
44523 canvas.on('pointerdown', this._handleMouseDown, this);
44524 canvas.on('pointermove', this._handleMouseMove, this);
44525 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44528 if ('ontouchstart' in window) {
44529 canvas.on('touchstart', this._handleTouchStart, this);
44530 canvas.on('touchmove', this._handleTouchMove, this);
44531 canvas.on('touchend', this._handleTouchEnd, this);
44534 Roo.EventManager.onWindowResize(this.resize, this, true);
44536 // file input event
44537 this.fileEl().on('change', this.uploadImage, this);
44544 resize: function(){
44546 var canvas = this.canvasEl().dom;
44547 var ctx = this.canvasElCtx();
44548 var img_data = false;
44550 if(canvas.width > 0) {
44551 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44553 // setting canvas width will clean img data
44556 var style = window.getComputedStyle ?
44557 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44559 var padding_left = parseInt(style.paddingLeft) || 0;
44560 var padding_right = parseInt(style.paddingRight) || 0;
44562 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44565 ctx.putImageData(img_data, 0, 0);
44569 _handleMouseDown: function(e)
44571 if (e.browserEvent.which === 1) {
44572 this.mouse_btn_down = true;
44573 this.strokeBegin(e);
44577 _handleMouseMove: function (e)
44579 if (this.mouse_btn_down) {
44580 this.strokeMoveUpdate(e);
44584 _handleMouseUp: function (e)
44586 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44587 this.mouse_btn_down = false;
44592 _handleTouchStart: function (e) {
44594 e.preventDefault();
44595 if (e.browserEvent.targetTouches.length === 1) {
44596 // var touch = e.browserEvent.changedTouches[0];
44597 // this.strokeBegin(touch);
44599 this.strokeBegin(e); // assume e catching the correct xy...
44603 _handleTouchMove: function (e) {
44604 e.preventDefault();
44605 // var touch = event.targetTouches[0];
44606 // _this._strokeMoveUpdate(touch);
44607 this.strokeMoveUpdate(e);
44610 _handleTouchEnd: function (e) {
44611 var wasCanvasTouched = e.target === this.canvasEl().dom;
44612 if (wasCanvasTouched) {
44613 e.preventDefault();
44614 // var touch = event.changedTouches[0];
44615 // _this._strokeEnd(touch);
44620 reset: function () {
44621 this._lastPoints = [];
44622 this._lastVelocity = 0;
44623 this._lastWidth = (this.min_width + this.max_width) / 2;
44624 this.canvasElCtx().fillStyle = this.dot_color;
44627 strokeMoveUpdate: function(e)
44629 this.strokeUpdate(e);
44631 if (this.throttle) {
44632 this.throttleStroke(this.strokeUpdate, this.throttle);
44635 this.strokeUpdate(e);
44639 strokeBegin: function(e)
44641 var newPointGroup = {
44642 color: this.dot_color,
44646 if (typeof this.onBegin === 'function') {
44650 this.curve_data.push(newPointGroup);
44652 this.strokeUpdate(e);
44655 strokeUpdate: function(e)
44657 var rect = this.canvasEl().dom.getBoundingClientRect();
44658 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44659 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44660 var lastPoints = lastPointGroup.points;
44661 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44662 var isLastPointTooClose = lastPoint
44663 ? point.distanceTo(lastPoint) <= this.min_distance
44665 var color = lastPointGroup.color;
44666 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44667 var curve = this.addPoint(point);
44669 this.drawDot({color: color, point: point});
44672 this.drawCurve({color: color, curve: curve});
44682 strokeEnd: function(e)
44684 this.strokeUpdate(e);
44685 if (typeof this.onEnd === 'function') {
44690 addPoint: function (point) {
44691 var _lastPoints = this._lastPoints;
44692 _lastPoints.push(point);
44693 if (_lastPoints.length > 2) {
44694 if (_lastPoints.length === 3) {
44695 _lastPoints.unshift(_lastPoints[0]);
44697 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44698 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44699 _lastPoints.shift();
44705 calculateCurveWidths: function (startPoint, endPoint) {
44706 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44707 (1 - this.velocity_filter_weight) * this._lastVelocity;
44709 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44712 start: this._lastWidth
44715 this._lastVelocity = velocity;
44716 this._lastWidth = newWidth;
44720 drawDot: function (_a) {
44721 var color = _a.color, point = _a.point;
44722 var ctx = this.canvasElCtx();
44723 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44725 this.drawCurveSegment(point.x, point.y, width);
44727 ctx.fillStyle = color;
44731 drawCurve: function (_a) {
44732 var color = _a.color, curve = _a.curve;
44733 var ctx = this.canvasElCtx();
44734 var widthDelta = curve.endWidth - curve.startWidth;
44735 var drawSteps = Math.floor(curve.length()) * 2;
44737 ctx.fillStyle = color;
44738 for (var i = 0; i < drawSteps; i += 1) {
44739 var t = i / drawSteps;
44745 var x = uuu * curve.startPoint.x;
44746 x += 3 * uu * t * curve.control1.x;
44747 x += 3 * u * tt * curve.control2.x;
44748 x += ttt * curve.endPoint.x;
44749 var y = uuu * curve.startPoint.y;
44750 y += 3 * uu * t * curve.control1.y;
44751 y += 3 * u * tt * curve.control2.y;
44752 y += ttt * curve.endPoint.y;
44753 var width = curve.startWidth + ttt * widthDelta;
44754 this.drawCurveSegment(x, y, width);
44760 drawCurveSegment: function (x, y, width) {
44761 var ctx = this.canvasElCtx();
44763 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44764 this.is_empty = false;
44769 var ctx = this.canvasElCtx();
44770 var canvas = this.canvasEl().dom;
44771 ctx.fillStyle = this.bg_color;
44772 ctx.clearRect(0, 0, canvas.width, canvas.height);
44773 ctx.fillRect(0, 0, canvas.width, canvas.height);
44774 this.curve_data = [];
44776 this.is_empty = true;
44781 return this.el.select('input',true).first();
44784 canvasEl: function()
44786 return this.el.select('canvas',true).first();
44789 canvasElCtx: function()
44791 return this.el.select('canvas',true).first().dom.getContext('2d');
44794 getImage: function(type)
44796 if(this.is_empty) {
44801 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44804 drawFromImage: function(img_src)
44806 var img = new Image();
44808 img.onload = function(){
44809 this.canvasElCtx().drawImage(img, 0, 0);
44814 this.is_empty = false;
44817 selectImage: function()
44819 this.fileEl().dom.click();
44822 uploadImage: function(e)
44824 var reader = new FileReader();
44826 reader.onload = function(e){
44827 var img = new Image();
44828 img.onload = function(){
44830 this.canvasElCtx().drawImage(img, 0, 0);
44832 img.src = e.target.result;
44835 reader.readAsDataURL(e.target.files[0]);
44838 // Bezier Point Constructor
44839 Point: (function () {
44840 function Point(x, y, time) {
44843 this.time = time || Date.now();
44845 Point.prototype.distanceTo = function (start) {
44846 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44848 Point.prototype.equals = function (other) {
44849 return this.x === other.x && this.y === other.y && this.time === other.time;
44851 Point.prototype.velocityFrom = function (start) {
44852 return this.time !== start.time
44853 ? this.distanceTo(start) / (this.time - start.time)
44860 // Bezier Constructor
44861 Bezier: (function () {
44862 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44863 this.startPoint = startPoint;
44864 this.control2 = control2;
44865 this.control1 = control1;
44866 this.endPoint = endPoint;
44867 this.startWidth = startWidth;
44868 this.endWidth = endWidth;
44870 Bezier.fromPoints = function (points, widths, scope) {
44871 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44872 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44873 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44875 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44876 var dx1 = s1.x - s2.x;
44877 var dy1 = s1.y - s2.y;
44878 var dx2 = s2.x - s3.x;
44879 var dy2 = s2.y - s3.y;
44880 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44881 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44882 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44883 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44884 var dxm = m1.x - m2.x;
44885 var dym = m1.y - m2.y;
44886 var k = l2 / (l1 + l2);
44887 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44888 var tx = s2.x - cm.x;
44889 var ty = s2.y - cm.y;
44891 c1: new scope.Point(m1.x + tx, m1.y + ty),
44892 c2: new scope.Point(m2.x + tx, m2.y + ty)
44895 Bezier.prototype.length = function () {
44900 for (var i = 0; i <= steps; i += 1) {
44902 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44903 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44905 var xdiff = cx - px;
44906 var ydiff = cy - py;
44907 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44914 Bezier.prototype.point = function (t, start, c1, c2, end) {
44915 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44916 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44917 + (3.0 * c2 * (1.0 - t) * t * t)
44918 + (end * t * t * t);
44923 throttleStroke: function(fn, wait) {
44924 if (wait === void 0) { wait = 250; }
44926 var timeout = null;
44930 var later = function () {
44931 previous = Date.now();
44933 result = fn.apply(storedContext, storedArgs);
44935 storedContext = null;
44939 return function wrapper() {
44941 for (var _i = 0; _i < arguments.length; _i++) {
44942 args[_i] = arguments[_i];
44944 var now = Date.now();
44945 var remaining = wait - (now - previous);
44946 storedContext = this;
44948 if (remaining <= 0 || remaining > wait) {
44950 clearTimeout(timeout);
44954 result = fn.apply(storedContext, storedArgs);
44956 storedContext = null;
44960 else if (!timeout) {
44961 timeout = window.setTimeout(later, remaining);