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,
3084 getAutoCreate : function()
3086 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3087 return this.createSingleImg();
3092 cls: 'roo-image-responsive-group',
3097 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3099 if(!_this[size + 'Url']){
3105 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3106 html: _this.html || cfg.html,
3107 src: _this[size + 'Url']
3110 img.cls += ' roo-image-responsive-' + size;
3112 var s = ['xs', 'sm', 'md', 'lg'];
3114 s.splice(s.indexOf(size), 1);
3116 Roo.each(s, function(ss){
3117 img.cls += ' hidden-' + ss;
3120 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3121 cfg.cls += ' img-' + _this.border;
3125 cfg.alt = _this.alt;
3138 a.target = _this.target;
3142 cfg.cn.push((_this.href) ? a : img);
3149 createSingleImg : function()
3153 cls: (this.imgResponsive) ? 'img-responsive' : '',
3155 src : 'about:blank' // just incase src get's set to undefined?!?
3158 cfg.html = this.html || cfg.html;
3160 cfg.src = this.src || cfg.src;
3162 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3163 cfg.cls += ' img-' + this.border;
3180 a.target = this.target;
3185 return (this.href) ? a : cfg;
3188 initEvents: function()
3191 this.el.on('click', this.onClick, this);
3193 this.el.select('img', true).on('load', this.onImageLoad, this);
3196 onClick : function(e)
3198 Roo.log('img onclick');
3199 this.fireEvent('click', this, e);
3201 onImageLoad: function(e)
3203 Roo.log('img load');
3204 this.fireEvent('load', this, e);
3208 * Sets the url of the image - used to update it
3209 * @param {String} url the url of the image
3212 setSrc : function(url)
3216 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3217 this.el.dom.src = url;
3221 this.el.select('img', true).first().dom.src = url;
3237 * @class Roo.bootstrap.Link
3238 * @extends Roo.bootstrap.Component
3239 * Bootstrap Link Class
3240 * @cfg {String} alt image alternative text
3241 * @cfg {String} href a tag href
3242 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3243 * @cfg {String} html the content of the link.
3244 * @cfg {String} anchor name for the anchor link
3245 * @cfg {String} fa - favicon
3247 * @cfg {Boolean} preventDefault (true | false) default false
3251 * Create a new Input
3252 * @param {Object} config The config object
3255 Roo.bootstrap.Link = function(config){
3256 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3262 * The img click event for the img.
3263 * @param {Roo.EventObject} e
3269 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3273 preventDefault: false,
3279 getAutoCreate : function()
3281 var html = this.html || '';
3283 if (this.fa !== false) {
3284 html = '<i class="fa fa-' + this.fa + '"></i>';
3289 // anchor's do not require html/href...
3290 if (this.anchor === false) {
3292 cfg.href = this.href || '#';
3294 cfg.name = this.anchor;
3295 if (this.html !== false || this.fa !== false) {
3298 if (this.href !== false) {
3299 cfg.href = this.href;
3303 if(this.alt !== false){
3308 if(this.target !== false) {
3309 cfg.target = this.target;
3315 initEvents: function() {
3317 if(!this.href || this.preventDefault){
3318 this.el.on('click', this.onClick, this);
3322 onClick : function(e)
3324 if(this.preventDefault){
3327 //Roo.log('img onclick');
3328 this.fireEvent('click', this, e);
3341 * @class Roo.bootstrap.Header
3342 * @extends Roo.bootstrap.Component
3343 * Bootstrap Header class
3344 * @cfg {String} html content of header
3345 * @cfg {Number} level (1|2|3|4|5|6) default 1
3348 * Create a new Header
3349 * @param {Object} config The config object
3353 Roo.bootstrap.Header = function(config){
3354 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3357 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3365 getAutoCreate : function(){
3370 tag: 'h' + (1 *this.level),
3371 html: this.html || ''
3383 * Ext JS Library 1.1.1
3384 * Copyright(c) 2006-2007, Ext JS, LLC.
3386 * Originally Released Under LGPL - original licence link has changed is not relivant.
3389 * <script type="text/javascript">
3393 * @class Roo.bootstrap.MenuMgr
3394 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3397 Roo.bootstrap.MenuMgr = function(){
3398 var menus, active, groups = {}, attached = false, lastShow = new Date();
3400 // private - called when first menu is created
3403 active = new Roo.util.MixedCollection();
3404 Roo.get(document).addKeyListener(27, function(){
3405 if(active.length > 0){
3413 if(active && active.length > 0){
3414 var c = active.clone();
3424 if(active.length < 1){
3425 Roo.get(document).un("mouseup", onMouseDown);
3433 var last = active.last();
3434 lastShow = new Date();
3437 Roo.get(document).on("mouseup", onMouseDown);
3442 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3443 m.parentMenu.activeChild = m;
3444 }else if(last && last.isVisible()){
3445 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3450 function onBeforeHide(m){
3452 m.activeChild.hide();
3454 if(m.autoHideTimer){
3455 clearTimeout(m.autoHideTimer);
3456 delete m.autoHideTimer;
3461 function onBeforeShow(m){
3462 var pm = m.parentMenu;
3463 if(!pm && !m.allowOtherMenus){
3465 }else if(pm && pm.activeChild && active != m){
3466 pm.activeChild.hide();
3470 // private this should really trigger on mouseup..
3471 function onMouseDown(e){
3472 Roo.log("on Mouse Up");
3474 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3475 Roo.log("MenuManager hideAll");
3484 function onBeforeCheck(mi, state){
3486 var g = groups[mi.group];
3487 for(var i = 0, l = g.length; i < l; i++){
3489 g[i].setChecked(false);
3498 * Hides all menus that are currently visible
3500 hideAll : function(){
3505 register : function(menu){
3509 menus[menu.id] = menu;
3510 menu.on("beforehide", onBeforeHide);
3511 menu.on("hide", onHide);
3512 menu.on("beforeshow", onBeforeShow);
3513 menu.on("show", onShow);
3515 if(g && menu.events["checkchange"]){
3519 groups[g].push(menu);
3520 menu.on("checkchange", onCheck);
3525 * Returns a {@link Roo.menu.Menu} object
3526 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3527 * be used to generate and return a new Menu instance.
3529 get : function(menu){
3530 if(typeof menu == "string"){ // menu id
3532 }else if(menu.events){ // menu instance
3535 /*else if(typeof menu.length == 'number'){ // array of menu items?
3536 return new Roo.bootstrap.Menu({items:menu});
3537 }else{ // otherwise, must be a config
3538 return new Roo.bootstrap.Menu(menu);
3545 unregister : function(menu){
3546 delete menus[menu.id];
3547 menu.un("beforehide", onBeforeHide);
3548 menu.un("hide", onHide);
3549 menu.un("beforeshow", onBeforeShow);
3550 menu.un("show", onShow);
3552 if(g && menu.events["checkchange"]){
3553 groups[g].remove(menu);
3554 menu.un("checkchange", onCheck);
3559 registerCheckable : function(menuItem){
3560 var g = menuItem.group;
3565 groups[g].push(menuItem);
3566 menuItem.on("beforecheckchange", onBeforeCheck);
3571 unregisterCheckable : function(menuItem){
3572 var g = menuItem.group;
3574 groups[g].remove(menuItem);
3575 menuItem.un("beforecheckchange", onBeforeCheck);
3587 * @class Roo.bootstrap.Menu
3588 * @extends Roo.bootstrap.Component
3589 * Bootstrap Menu class - container for MenuItems
3590 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3591 * @cfg {bool} hidden if the menu should be hidden when rendered.
3592 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3593 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3594 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3595 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3599 * @param {Object} config The config object
3603 Roo.bootstrap.Menu = function(config){
3605 if (config.type == 'treeview') {
3606 // normally menu's are drawn attached to the document to handle layering etc..
3607 // however treeview (used by the docs menu is drawn into the parent element)
3608 this.container_method = 'getChildContainer';
3611 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3612 if (this.registerMenu && this.type != 'treeview') {
3613 Roo.bootstrap.MenuMgr.register(this);
3620 * Fires before this menu is displayed (return false to block)
3621 * @param {Roo.menu.Menu} this
3626 * Fires before this menu is hidden (return false to block)
3627 * @param {Roo.menu.Menu} this
3632 * Fires after this menu is displayed
3633 * @param {Roo.menu.Menu} this
3638 * Fires after this menu is hidden
3639 * @param {Roo.menu.Menu} this
3644 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3645 * @param {Roo.menu.Menu} this
3646 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3647 * @param {Roo.EventObject} e
3652 * Fires when the mouse is hovering over this menu
3653 * @param {Roo.menu.Menu} this
3654 * @param {Roo.EventObject} e
3655 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3660 * Fires when the mouse exits this menu
3661 * @param {Roo.menu.Menu} this
3662 * @param {Roo.EventObject} e
3663 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668 * Fires when a menu item contained in this menu is clicked
3669 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3670 * @param {Roo.EventObject} e
3674 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3677 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3681 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3684 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3686 registerMenu : true,
3688 menuItems :false, // stores the menu items..
3698 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3700 hideTrigger : false,
3705 getChildContainer : function() {
3709 getAutoCreate : function(){
3711 //if (['right'].indexOf(this.align)!==-1) {
3712 // cfg.cn[1].cls += ' pull-right'
3717 cls : 'dropdown-menu shadow' ,
3718 style : 'z-index:1000'
3722 if (this.type === 'submenu') {
3723 cfg.cls = 'submenu active';
3725 if (this.type === 'treeview') {
3726 cfg.cls = 'treeview-menu';
3731 initEvents : function() {
3733 // Roo.log("ADD event");
3734 // Roo.log(this.triggerEl.dom);
3735 if (this.triggerEl) {
3737 this.triggerEl.on('click', this.onTriggerClick, this);
3739 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3741 if (!this.hideTrigger) {
3742 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3743 // dropdown toggle on the 'a' in BS4?
3744 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3746 this.triggerEl.addClass('dropdown-toggle');
3752 this.el.on('touchstart' , this.onTouch, this);
3754 this.el.on('click' , this.onClick, this);
3756 this.el.on("mouseover", this.onMouseOver, this);
3757 this.el.on("mouseout", this.onMouseOut, this);
3761 findTargetItem : function(e)
3763 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3767 //Roo.log(t); Roo.log(t.id);
3769 //Roo.log(this.menuitems);
3770 return this.menuitems.get(t.id);
3772 //return this.items.get(t.menuItemId);
3778 onTouch : function(e)
3780 Roo.log("menu.onTouch");
3781 //e.stopEvent(); this make the user popdown broken
3785 onClick : function(e)
3787 Roo.log("menu.onClick");
3789 var t = this.findTargetItem(e);
3790 if(!t || t.isContainer){
3795 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3796 if(t == this.activeItem && t.shouldDeactivate(e)){
3797 this.activeItem.deactivate();
3798 delete this.activeItem;
3802 this.setActiveItem(t, true);
3810 Roo.log('pass click event');
3814 this.fireEvent("click", this, t, e);
3818 if(!t.href.length || t.href == '#'){
3819 (function() { _this.hide(); }).defer(100);
3824 onMouseOver : function(e){
3825 var t = this.findTargetItem(e);
3828 // if(t.canActivate && !t.disabled){
3829 // this.setActiveItem(t, true);
3833 this.fireEvent("mouseover", this, e, t);
3835 isVisible : function(){
3836 return !this.hidden;
3838 onMouseOut : function(e){
3839 var t = this.findTargetItem(e);
3842 // if(t == this.activeItem && t.shouldDeactivate(e)){
3843 // this.activeItem.deactivate();
3844 // delete this.activeItem;
3847 this.fireEvent("mouseout", this, e, t);
3852 * Displays this menu relative to another element
3853 * @param {String/HTMLElement/Roo.Element} element The element to align to
3854 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3855 * the element (defaults to this.defaultAlign)
3856 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3858 show : function(el, pos, parentMenu)
3860 if (false === this.fireEvent("beforeshow", this)) {
3861 Roo.log("show canceled");
3864 this.parentMenu = parentMenu;
3868 this.el.addClass('show'); // show otherwise we do not know how big we are..
3870 var xy = this.el.getAlignToXY(el, pos);
3872 // bl-tl << left align below
3873 // tl-bl << left align
3875 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3876 // if it goes to far to the right.. -> align left.
3877 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3880 // was left align - go right?
3881 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3884 // goes down the bottom
3885 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3887 var a = this.align.replace('?', '').split('-');
3888 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3892 this.showAt( xy , parentMenu, false);
3895 * Displays this menu at a specific xy position
3896 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3897 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3899 showAt : function(xy, parentMenu, /* private: */_e){
3900 this.parentMenu = parentMenu;
3905 this.fireEvent("beforeshow", this);
3906 //xy = this.el.adjustForConstraints(xy);
3910 this.hideMenuItems();
3911 this.hidden = false;
3912 if (this.triggerEl) {
3913 this.triggerEl.addClass('open');
3916 this.el.addClass('show');
3920 // reassign x when hitting right
3922 // reassign y when hitting bottom
3924 // but the list may align on trigger left or trigger top... should it be a properity?
3926 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3931 this.fireEvent("show", this);
3937 this.doFocus.defer(50, this);
3941 doFocus : function(){
3943 this.focusEl.focus();
3948 * Hides this menu and optionally all parent menus
3949 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3951 hide : function(deep)
3953 if (false === this.fireEvent("beforehide", this)) {
3954 Roo.log("hide canceled");
3957 this.hideMenuItems();
3958 if(this.el && this.isVisible()){
3960 if(this.activeItem){
3961 this.activeItem.deactivate();
3962 this.activeItem = null;
3964 if (this.triggerEl) {
3965 this.triggerEl.removeClass('open');
3968 this.el.removeClass('show');
3970 this.fireEvent("hide", this);
3972 if(deep === true && this.parentMenu){
3973 this.parentMenu.hide(true);
3977 onTriggerClick : function(e)
3979 Roo.log('trigger click');
3981 var target = e.getTarget();
3983 Roo.log(target.nodeName.toLowerCase());
3985 if(target.nodeName.toLowerCase() === 'i'){
3991 onTriggerPress : function(e)
3993 Roo.log('trigger press');
3994 //Roo.log(e.getTarget());
3995 // Roo.log(this.triggerEl.dom);
3997 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3998 var pel = Roo.get(e.getTarget());
3999 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4000 Roo.log('is treeview or dropdown?');
4004 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4008 if (this.isVisible()) {
4014 this.show(this.triggerEl, this.align, false);
4017 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4024 hideMenuItems : function()
4026 Roo.log("hide Menu Items");
4031 this.el.select('.open',true).each(function(aa) {
4033 aa.removeClass('open');
4037 addxtypeChild : function (tree, cntr) {
4038 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4040 this.menuitems.add(comp);
4052 this.getEl().dom.innerHTML = '';
4053 this.menuitems.clear();
4067 * @class Roo.bootstrap.MenuItem
4068 * @extends Roo.bootstrap.Component
4069 * Bootstrap MenuItem class
4070 * @cfg {String} html the menu label
4071 * @cfg {String} href the link
4072 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4073 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4074 * @cfg {Boolean} active used on sidebars to highlight active itesm
4075 * @cfg {String} fa favicon to show on left of menu item.
4076 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4080 * Create a new MenuItem
4081 * @param {Object} config The config object
4085 Roo.bootstrap.MenuItem = function(config){
4086 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4091 * The raw click event for the entire grid.
4092 * @param {Roo.bootstrap.MenuItem} this
4093 * @param {Roo.EventObject} e
4099 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4103 preventDefault: false,
4104 isContainer : false,
4108 getAutoCreate : function(){
4110 if(this.isContainer){
4113 cls: 'dropdown-menu-item '
4123 cls : 'dropdown-item',
4128 if (this.fa !== false) {
4131 cls : 'fa fa-' + this.fa
4140 cls: 'dropdown-menu-item',
4143 if (this.parent().type == 'treeview') {
4144 cfg.cls = 'treeview-menu';
4147 cfg.cls += ' active';
4152 anc.href = this.href || cfg.cn[0].href ;
4153 ctag.html = this.html || cfg.cn[0].html ;
4157 initEvents: function()
4159 if (this.parent().type == 'treeview') {
4160 this.el.select('a').on('click', this.onClick, this);
4164 this.menu.parentType = this.xtype;
4165 this.menu.triggerEl = this.el;
4166 this.menu = this.addxtype(Roo.apply({}, this.menu));
4170 onClick : function(e)
4172 Roo.log('item on click ');
4174 if(this.preventDefault){
4177 //this.parent().hideMenuItems();
4179 this.fireEvent('click', this, e);
4198 * @class Roo.bootstrap.MenuSeparator
4199 * @extends Roo.bootstrap.Component
4200 * Bootstrap MenuSeparator class
4203 * Create a new MenuItem
4204 * @param {Object} config The config object
4208 Roo.bootstrap.MenuSeparator = function(config){
4209 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4212 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4214 getAutoCreate : function(){
4233 * @class Roo.bootstrap.Modal
4234 * @extends Roo.bootstrap.Component
4235 * Bootstrap Modal class
4236 * @cfg {String} title Title of dialog
4237 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4238 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4239 * @cfg {Boolean} specificTitle default false
4240 * @cfg {Array} buttons Array of buttons or standard button set..
4241 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4242 * @cfg {Boolean} animate default true
4243 * @cfg {Boolean} allow_close default true
4244 * @cfg {Boolean} fitwindow default false
4245 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4246 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4247 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4248 * @cfg {String} size (sm|lg|xl) default empty
4249 * @cfg {Number} max_width set the max width of modal
4250 * @cfg {Boolean} editableTitle can the title be edited
4255 * Create a new Modal Dialog
4256 * @param {Object} config The config object
4259 Roo.bootstrap.Modal = function(config){
4260 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4265 * The raw btnclick event for the button
4266 * @param {Roo.EventObject} e
4271 * Fire when dialog resize
4272 * @param {Roo.bootstrap.Modal} this
4273 * @param {Roo.EventObject} e
4277 * @event titlechanged
4278 * Fire when the editable title has been changed
4279 * @param {Roo.bootstrap.Modal} this
4280 * @param {Roo.EventObject} value
4282 "titlechanged" : true
4285 this.buttons = this.buttons || [];
4288 this.tmpl = Roo.factory(this.tmpl);
4293 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4295 title : 'test dialog',
4305 specificTitle: false,
4307 buttonPosition: 'right',
4329 editableTitle : false,
4331 onRender : function(ct, position)
4333 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4336 var cfg = Roo.apply({}, this.getAutoCreate());
4339 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4341 //if (!cfg.name.length) {
4345 cfg.cls += ' ' + this.cls;
4348 cfg.style = this.style;
4350 this.el = Roo.get(document.body).createChild(cfg, position);
4352 //var type = this.el.dom.type;
4355 if(this.tabIndex !== undefined){
4356 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4359 this.dialogEl = this.el.select('.modal-dialog',true).first();
4360 this.bodyEl = this.el.select('.modal-body',true).first();
4361 this.closeEl = this.el.select('.modal-header .close', true).first();
4362 this.headerEl = this.el.select('.modal-header',true).first();
4363 this.titleEl = this.el.select('.modal-title',true).first();
4364 this.footerEl = this.el.select('.modal-footer',true).first();
4366 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4368 //this.el.addClass("x-dlg-modal");
4370 if (this.buttons.length) {
4371 Roo.each(this.buttons, function(bb) {
4372 var b = Roo.apply({}, bb);
4373 b.xns = b.xns || Roo.bootstrap;
4374 b.xtype = b.xtype || 'Button';
4375 if (typeof(b.listeners) == 'undefined') {
4376 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4379 var btn = Roo.factory(b);
4381 btn.render(this.getButtonContainer());
4385 // render the children.
4388 if(typeof(this.items) != 'undefined'){
4389 var items = this.items;
4392 for(var i =0;i < items.length;i++) {
4393 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4397 this.items = nitems;
4399 // where are these used - they used to be body/close/footer
4403 //this.el.addClass([this.fieldClass, this.cls]);
4407 getAutoCreate : function()
4409 // we will default to modal-body-overflow - might need to remove or make optional later.
4411 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4412 html : this.html || ''
4417 cls : 'modal-title',
4421 if(this.specificTitle){ // WTF is this?
4426 if (this.allow_close && Roo.bootstrap.version == 3) {
4436 if (this.editableTitle) {
4438 cls: 'form-control roo-editable-title d-none',
4444 if (this.allow_close && Roo.bootstrap.version == 4) {
4454 if(this.size.length){
4455 size = 'modal-' + this.size;
4458 var footer = Roo.bootstrap.version == 3 ?
4460 cls : 'modal-footer',
4464 cls: 'btn-' + this.buttonPosition
4469 { // BS4 uses mr-auto on left buttons....
4470 cls : 'modal-footer'
4481 cls: "modal-dialog " + size,
4484 cls : "modal-content",
4487 cls : 'modal-header',
4502 modal.cls += ' fade';
4508 getChildContainer : function() {
4513 getButtonContainer : function() {
4515 return Roo.bootstrap.version == 4 ?
4516 this.el.select('.modal-footer',true).first()
4517 : this.el.select('.modal-footer div',true).first();
4520 initEvents : function()
4522 if (this.allow_close) {
4523 this.closeEl.on('click', this.hide, this);
4525 Roo.EventManager.onWindowResize(this.resize, this, true);
4526 if (this.editableTitle) {
4527 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4528 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4529 this.headerEditEl.on('keyup', function(e) {
4530 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4531 this.toggleHeaderInput(false)
4534 this.headerEditEl.on('blur', function(e) {
4535 this.toggleHeaderInput(false)
4544 this.maskEl.setSize(
4545 Roo.lib.Dom.getViewWidth(true),
4546 Roo.lib.Dom.getViewHeight(true)
4549 if (this.fitwindow) {
4551 this.dialogEl.setStyle( { 'max-width' : '100%' });
4553 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4554 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4559 if(this.max_width !== 0) {
4561 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4564 this.setSize(w, this.height);
4568 if(this.max_height) {
4569 this.setSize(w,Math.min(
4571 Roo.lib.Dom.getViewportHeight(true) - 60
4577 if(!this.fit_content) {
4578 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4582 this.setSize(w, Math.min(
4584 this.headerEl.getHeight() +
4585 this.footerEl.getHeight() +
4586 this.getChildHeight(this.bodyEl.dom.childNodes),
4587 Roo.lib.Dom.getViewportHeight(true) - 60)
4593 setSize : function(w,h)
4604 if (!this.rendered) {
4607 this.toggleHeaderInput(false);
4608 //this.el.setStyle('display', 'block');
4609 this.el.removeClass('hideing');
4610 this.el.dom.style.display='block';
4612 Roo.get(document.body).addClass('modal-open');
4614 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4617 this.el.addClass('show');
4618 this.el.addClass('in');
4621 this.el.addClass('show');
4622 this.el.addClass('in');
4625 // not sure how we can show data in here..
4627 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4630 Roo.get(document.body).addClass("x-body-masked");
4632 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4633 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.maskEl.dom.style.display = 'block';
4635 this.maskEl.addClass('show');
4640 this.fireEvent('show', this);
4642 // set zindex here - otherwise it appears to be ignored...
4643 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4646 this.items.forEach( function(e) {
4647 e.layout ? e.layout() : false;
4655 if(this.fireEvent("beforehide", this) !== false){
4657 this.maskEl.removeClass('show');
4659 this.maskEl.dom.style.display = '';
4660 Roo.get(document.body).removeClass("x-body-masked");
4661 this.el.removeClass('in');
4662 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4664 if(this.animate){ // why
4665 this.el.addClass('hideing');
4666 this.el.removeClass('show');
4668 if (!this.el.hasClass('hideing')) {
4669 return; // it's been shown again...
4672 this.el.dom.style.display='';
4674 Roo.get(document.body).removeClass('modal-open');
4675 this.el.removeClass('hideing');
4679 this.el.removeClass('show');
4680 this.el.dom.style.display='';
4681 Roo.get(document.body).removeClass('modal-open');
4684 this.fireEvent('hide', this);
4687 isVisible : function()
4690 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4694 addButton : function(str, cb)
4698 var b = Roo.apply({}, { html : str } );
4699 b.xns = b.xns || Roo.bootstrap;
4700 b.xtype = b.xtype || 'Button';
4701 if (typeof(b.listeners) == 'undefined') {
4702 b.listeners = { click : cb.createDelegate(this) };
4705 var btn = Roo.factory(b);
4707 btn.render(this.getButtonContainer());
4713 setDefaultButton : function(btn)
4715 //this.el.select('.modal-footer').()
4718 resizeTo: function(w,h)
4720 this.dialogEl.setWidth(w);
4722 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4724 this.bodyEl.setHeight(h - diff);
4726 this.fireEvent('resize', this);
4729 setContentSize : function(w, h)
4733 onButtonClick: function(btn,e)
4736 this.fireEvent('btnclick', btn.name, e);
4739 * Set the title of the Dialog
4740 * @param {String} str new Title
4742 setTitle: function(str) {
4743 this.titleEl.dom.innerHTML = str;
4747 * Set the body of the Dialog
4748 * @param {String} str new Title
4750 setBody: function(str) {
4751 this.bodyEl.dom.innerHTML = str;
4754 * Set the body of the Dialog using the template
4755 * @param {Obj} data - apply this data to the template and replace the body contents.
4757 applyBody: function(obj)
4760 Roo.log("Error - using apply Body without a template");
4763 this.tmpl.overwrite(this.bodyEl, obj);
4766 getChildHeight : function(child_nodes)
4770 child_nodes.length == 0
4775 var child_height = 0;
4777 for(var i = 0; i < child_nodes.length; i++) {
4780 * for modal with tabs...
4781 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4783 var layout_childs = child_nodes[i].childNodes;
4785 for(var j = 0; j < layout_childs.length; j++) {
4787 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4789 var layout_body_childs = layout_childs[j].childNodes;
4791 for(var k = 0; k < layout_body_childs.length; k++) {
4793 if(layout_body_childs[k].classList.contains('navbar')) {
4794 child_height += layout_body_childs[k].offsetHeight;
4798 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4800 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4802 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4804 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4805 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4820 child_height += child_nodes[i].offsetHeight;
4821 // Roo.log(child_nodes[i].offsetHeight);
4824 return child_height;
4826 toggleHeaderInput : function(is_edit)
4828 if (!this.editableTitle) {
4829 return; // not editable.
4831 if (is_edit && this.is_header_editing) {
4832 return; // already editing..
4836 this.headerEditEl.dom.value = this.title;
4837 this.headerEditEl.removeClass('d-none');
4838 this.headerEditEl.dom.focus();
4839 this.titleEl.addClass('d-none');
4841 this.is_header_editing = true;
4844 // flip back to not editing.
4845 this.title = this.headerEditEl.dom.value;
4846 this.headerEditEl.addClass('d-none');
4847 this.titleEl.removeClass('d-none');
4848 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4849 this.is_header_editing = false;
4850 this.fireEvent('titlechanged', this, this.title);
4859 Roo.apply(Roo.bootstrap.Modal, {
4861 * Button config that displays a single OK button
4870 * Button config that displays Yes and No buttons
4886 * Button config that displays OK and Cancel buttons
4901 * Button config that displays Yes, No and Cancel buttons
4926 * messagebox - can be used as a replace
4930 * @class Roo.MessageBox
4931 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4935 Roo.Msg.alert('Status', 'Changes saved successfully.');
4937 // Prompt for user data:
4938 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4940 // process text value...
4944 // Show a dialog using config options:
4946 title:'Save Changes?',
4947 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4948 buttons: Roo.Msg.YESNOCANCEL,
4955 Roo.bootstrap.MessageBox = function(){
4956 var dlg, opt, mask, waitTimer;
4957 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4958 var buttons, activeTextEl, bwidth;
4962 var handleButton = function(button){
4964 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4968 var handleHide = function(){
4970 dlg.el.removeClass(opt.cls);
4973 // Roo.TaskMgr.stop(waitTimer);
4974 // waitTimer = null;
4979 var updateButtons = function(b){
4982 buttons["ok"].hide();
4983 buttons["cancel"].hide();
4984 buttons["yes"].hide();
4985 buttons["no"].hide();
4986 dlg.footerEl.hide();
4990 dlg.footerEl.show();
4991 for(var k in buttons){
4992 if(typeof buttons[k] != "function"){
4995 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4996 width += buttons[k].el.getWidth()+15;
5006 var handleEsc = function(d, k, e){
5007 if(opt && opt.closable !== false){
5017 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5018 * @return {Roo.BasicDialog} The BasicDialog element
5020 getDialog : function(){
5022 dlg = new Roo.bootstrap.Modal( {
5025 //constraintoviewport:false,
5027 //collapsible : false,
5032 //buttonAlign:"center",
5033 closeClick : function(){
5034 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5037 handleButton("cancel");
5042 dlg.on("hide", handleHide);
5044 //dlg.addKeyListener(27, handleEsc);
5046 this.buttons = buttons;
5047 var bt = this.buttonText;
5048 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5049 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5050 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5051 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5053 bodyEl = dlg.bodyEl.createChild({
5055 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5056 '<textarea class="roo-mb-textarea"></textarea>' +
5057 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5059 msgEl = bodyEl.dom.firstChild;
5060 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5061 textboxEl.enableDisplayMode();
5062 textboxEl.addKeyListener([10,13], function(){
5063 if(dlg.isVisible() && opt && opt.buttons){
5066 }else if(opt.buttons.yes){
5067 handleButton("yes");
5071 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5072 textareaEl.enableDisplayMode();
5073 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5074 progressEl.enableDisplayMode();
5076 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5077 var pf = progressEl.dom.firstChild;
5079 pp = Roo.get(pf.firstChild);
5080 pp.setHeight(pf.offsetHeight);
5088 * Updates the message box body text
5089 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5090 * the XHTML-compliant non-breaking space character '&#160;')
5091 * @return {Roo.MessageBox} This message box
5093 updateText : function(text)
5095 if(!dlg.isVisible() && !opt.width){
5096 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5097 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5099 msgEl.innerHTML = text || ' ';
5101 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5102 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5104 Math.min(opt.width || cw , this.maxWidth),
5105 Math.max(opt.minWidth || this.minWidth, bwidth)
5108 activeTextEl.setWidth(w);
5110 if(dlg.isVisible()){
5111 dlg.fixedcenter = false;
5113 // to big, make it scroll. = But as usual stupid IE does not support
5116 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5117 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5118 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5120 bodyEl.dom.style.height = '';
5121 bodyEl.dom.style.overflowY = '';
5124 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5126 bodyEl.dom.style.overflowX = '';
5129 dlg.setContentSize(w, bodyEl.getHeight());
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = true;
5137 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5138 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5139 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5140 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5141 * @return {Roo.MessageBox} This message box
5143 updateProgress : function(value, text){
5145 this.updateText(text);
5148 if (pp) { // weird bug on my firefox - for some reason this is not defined
5149 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5150 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5156 * Returns true if the message box is currently displayed
5157 * @return {Boolean} True if the message box is visible, else false
5159 isVisible : function(){
5160 return dlg && dlg.isVisible();
5164 * Hides the message box if it is displayed
5167 if(this.isVisible()){
5173 * Displays a new message box, or reinitializes an existing message box, based on the config options
5174 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5175 * The following config object properties are supported:
5177 Property Type Description
5178 ---------- --------------- ------------------------------------------------------------------------------------
5179 animEl String/Element An id or Element from which the message box should animate as it opens and
5180 closes (defaults to undefined)
5181 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5182 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5183 closable Boolean False to hide the top-right close button (defaults to true). Note that
5184 progress and wait dialogs will ignore this property and always hide the
5185 close button as they can only be closed programmatically.
5186 cls String A custom CSS class to apply to the message box element
5187 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5188 displayed (defaults to 75)
5189 fn Function A callback function to execute after closing the dialog. The arguments to the
5190 function will be btn (the name of the button that was clicked, if applicable,
5191 e.g. "ok"), and text (the value of the active text field, if applicable).
5192 Progress and wait dialogs will ignore this option since they do not respond to
5193 user actions and can only be closed programmatically, so any required function
5194 should be called by the same code after it closes the dialog.
5195 icon String A CSS class that provides a background image to be used as an icon for
5196 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5197 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5198 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5199 modal Boolean False to allow user interaction with the page while the message box is
5200 displayed (defaults to true)
5201 msg String A string that will replace the existing message box body text (defaults
5202 to the XHTML-compliant non-breaking space character ' ')
5203 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5204 progress Boolean True to display a progress bar (defaults to false)
5205 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5206 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5207 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5208 title String The title text
5209 value String The string value to set into the active textbox element if displayed
5210 wait Boolean True to display a progress bar (defaults to false)
5211 width Number The width of the dialog in pixels
5218 msg: 'Please enter your address:',
5220 buttons: Roo.MessageBox.OKCANCEL,
5223 animEl: 'addAddressBtn'
5226 * @param {Object} config Configuration options
5227 * @return {Roo.MessageBox} This message box
5229 show : function(options)
5232 // this causes nightmares if you show one dialog after another
5233 // especially on callbacks..
5235 if(this.isVisible()){
5238 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5239 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5240 Roo.log("New Dialog Message:" + options.msg )
5241 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5242 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5245 var d = this.getDialog();
5247 d.setTitle(opt.title || " ");
5248 d.closeEl.setDisplayed(opt.closable !== false);
5249 activeTextEl = textboxEl;
5250 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5255 textareaEl.setHeight(typeof opt.multiline == "number" ?
5256 opt.multiline : this.defaultTextHeight);
5257 activeTextEl = textareaEl;
5266 progressEl.setDisplayed(opt.progress === true);
5268 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5270 this.updateProgress(0);
5271 activeTextEl.dom.value = opt.value || "";
5273 dlg.setDefaultButton(activeTextEl);
5275 var bs = opt.buttons;
5279 }else if(bs && bs.yes){
5280 db = buttons["yes"];
5282 dlg.setDefaultButton(db);
5284 bwidth = updateButtons(opt.buttons);
5285 this.updateText(opt.msg);
5287 d.el.addClass(opt.cls);
5289 d.proxyDrag = opt.proxyDrag === true;
5290 d.modal = opt.modal !== false;
5291 d.mask = opt.modal !== false ? mask : false;
5293 // force it to the end of the z-index stack so it gets a cursor in FF
5294 document.body.appendChild(dlg.el.dom);
5295 d.animateTarget = null;
5296 d.show(options.animEl);
5302 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5303 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5304 * and closing the message box when the process is complete.
5305 * @param {String} title The title bar text
5306 * @param {String} msg The message box body text
5307 * @return {Roo.MessageBox} This message box
5309 progress : function(title, msg){
5316 minWidth: this.minProgressWidth,
5323 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5324 * If a callback function is passed it will be called after the user clicks the button, and the
5325 * id of the button that was clicked will be passed as the only parameter to the callback
5326 * (could also be the top-right close button).
5327 * @param {String} title The title bar text
5328 * @param {String} msg The message box body text
5329 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5330 * @param {Object} scope (optional) The scope of the callback function
5331 * @return {Roo.MessageBox} This message box
5333 alert : function(title, msg, fn, scope)
5348 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5349 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5350 * You are responsible for closing the message box when the process is complete.
5351 * @param {String} msg The message box body text
5352 * @param {String} title (optional) The title bar text
5353 * @return {Roo.MessageBox} This message box
5355 wait : function(msg, title){
5366 waitTimer = Roo.TaskMgr.start({
5368 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5376 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5377 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5378 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5379 * @param {String} title The title bar text
5380 * @param {String} msg The message box body text
5381 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5382 * @param {Object} scope (optional) The scope of the callback function
5383 * @return {Roo.MessageBox} This message box
5385 confirm : function(title, msg, fn, scope){
5389 buttons: this.YESNO,
5398 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5399 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5400 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5401 * (could also be the top-right close button) and the text that was entered will be passed as the two
5402 * parameters to the callback.
5403 * @param {String} title The title bar text
5404 * @param {String} msg The message box body text
5405 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5406 * @param {Object} scope (optional) The scope of the callback function
5407 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5408 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5409 * @return {Roo.MessageBox} This message box
5411 prompt : function(title, msg, fn, scope, multiline){
5415 buttons: this.OKCANCEL,
5420 multiline: multiline,
5427 * Button config that displays a single OK button
5432 * Button config that displays Yes and No buttons
5435 YESNO : {yes:true, no:true},
5437 * Button config that displays OK and Cancel buttons
5440 OKCANCEL : {ok:true, cancel:true},
5442 * Button config that displays Yes, No and Cancel buttons
5445 YESNOCANCEL : {yes:true, no:true, cancel:true},
5448 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5451 defaultTextHeight : 75,
5453 * The maximum width in pixels of the message box (defaults to 600)
5458 * The minimum width in pixels of the message box (defaults to 100)
5463 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5464 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5467 minProgressWidth : 250,
5469 * An object containing the default button text strings that can be overriden for localized language support.
5470 * Supported properties are: ok, cancel, yes and no.
5471 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5484 * Shorthand for {@link Roo.MessageBox}
5486 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5487 Roo.Msg = Roo.Msg || Roo.MessageBox;
5496 * @class Roo.bootstrap.Navbar
5497 * @extends Roo.bootstrap.Component
5498 * Bootstrap Navbar class
5501 * Create a new Navbar
5502 * @param {Object} config The config object
5506 Roo.bootstrap.Navbar = function(config){
5507 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5511 * @event beforetoggle
5512 * Fire before toggle the menu
5513 * @param {Roo.EventObject} e
5515 "beforetoggle" : true
5519 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5528 getAutoCreate : function(){
5531 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5535 initEvents :function ()
5537 //Roo.log(this.el.select('.navbar-toggle',true));
5538 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5545 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5547 var size = this.el.getSize();
5548 this.maskEl.setSize(size.width, size.height);
5549 this.maskEl.enableDisplayMode("block");
5558 getChildContainer : function()
5560 if (this.el && this.el.select('.collapse').getCount()) {
5561 return this.el.select('.collapse',true).first();
5576 onToggle : function()
5579 if(this.fireEvent('beforetoggle', this) === false){
5582 var ce = this.el.select('.navbar-collapse',true).first();
5584 if (!ce.hasClass('show')) {
5594 * Expand the navbar pulldown
5596 expand : function ()
5599 var ce = this.el.select('.navbar-collapse',true).first();
5600 if (ce.hasClass('collapsing')) {
5603 ce.dom.style.height = '';
5605 ce.addClass('in'); // old...
5606 ce.removeClass('collapse');
5607 ce.addClass('show');
5608 var h = ce.getHeight();
5610 ce.removeClass('show');
5611 // at this point we should be able to see it..
5612 ce.addClass('collapsing');
5614 ce.setHeight(0); // resize it ...
5615 ce.on('transitionend', function() {
5616 //Roo.log('done transition');
5617 ce.removeClass('collapsing');
5618 ce.addClass('show');
5619 ce.removeClass('collapse');
5621 ce.dom.style.height = '';
5622 }, this, { single: true} );
5624 ce.dom.scrollTop = 0;
5627 * Collapse the navbar pulldown
5629 collapse : function()
5631 var ce = this.el.select('.navbar-collapse',true).first();
5633 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5634 // it's collapsed or collapsing..
5637 ce.removeClass('in'); // old...
5638 ce.setHeight(ce.getHeight());
5639 ce.removeClass('show');
5640 ce.addClass('collapsing');
5642 ce.on('transitionend', function() {
5643 ce.dom.style.height = '';
5644 ce.removeClass('collapsing');
5645 ce.addClass('collapse');
5646 }, this, { single: true} );
5666 * @class Roo.bootstrap.NavSimplebar
5667 * @extends Roo.bootstrap.Navbar
5668 * Bootstrap Sidebar class
5670 * @cfg {Boolean} inverse is inverted color
5672 * @cfg {String} type (nav | pills | tabs)
5673 * @cfg {Boolean} arrangement stacked | justified
5674 * @cfg {String} align (left | right) alignment
5676 * @cfg {Boolean} main (true|false) main nav bar? default false
5677 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5679 * @cfg {String} tag (header|footer|nav|div) default is nav
5681 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5685 * Create a new Sidebar
5686 * @param {Object} config The config object
5690 Roo.bootstrap.NavSimplebar = function(config){
5691 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5694 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5710 getAutoCreate : function(){
5714 tag : this.tag || 'div',
5715 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5717 if (['light','white'].indexOf(this.weight) > -1) {
5718 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5720 cfg.cls += ' bg-' + this.weight;
5723 cfg.cls += ' navbar-inverse';
5727 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5729 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5738 cls: 'nav nav-' + this.xtype,
5744 this.type = this.type || 'nav';
5745 if (['tabs','pills'].indexOf(this.type) != -1) {
5746 cfg.cn[0].cls += ' nav-' + this.type
5750 if (this.type!=='nav') {
5751 Roo.log('nav type must be nav/tabs/pills')
5753 cfg.cn[0].cls += ' navbar-nav'
5759 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5760 cfg.cn[0].cls += ' nav-' + this.arrangement;
5764 if (this.align === 'right') {
5765 cfg.cn[0].cls += ' navbar-right';
5790 * navbar-expand-md fixed-top
5794 * @class Roo.bootstrap.NavHeaderbar
5795 * @extends Roo.bootstrap.NavSimplebar
5796 * Bootstrap Sidebar class
5798 * @cfg {String} brand what is brand
5799 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5800 * @cfg {String} brand_href href of the brand
5801 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5802 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5803 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5804 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5807 * Create a new Sidebar
5808 * @param {Object} config The config object
5812 Roo.bootstrap.NavHeaderbar = function(config){
5813 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5817 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5824 desktopCenter : false,
5827 getAutoCreate : function(){
5830 tag: this.nav || 'nav',
5831 cls: 'navbar navbar-expand-md',
5837 if (this.desktopCenter) {
5838 cn.push({cls : 'container', cn : []});
5846 cls: 'navbar-toggle navbar-toggler',
5847 'data-toggle': 'collapse',
5852 html: 'Toggle navigation'
5856 cls: 'icon-bar navbar-toggler-icon'
5869 cn.push( Roo.bootstrap.version == 4 ? btn : {
5871 cls: 'navbar-header',
5880 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5884 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5886 if (['light','white'].indexOf(this.weight) > -1) {
5887 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5889 cfg.cls += ' bg-' + this.weight;
5892 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5893 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5895 // tag can override this..
5897 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5900 if (this.brand !== '') {
5901 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5902 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5904 href: this.brand_href ? this.brand_href : '#',
5905 cls: 'navbar-brand',
5913 cfg.cls += ' main-nav';
5921 getHeaderChildContainer : function()
5923 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5924 return this.el.select('.navbar-header',true).first();
5927 return this.getChildContainer();
5930 getChildContainer : function()
5933 return this.el.select('.roo-navbar-collapse',true).first();
5938 initEvents : function()
5940 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5942 if (this.autohide) {
5947 Roo.get(document).on('scroll',function(e) {
5948 var ns = Roo.get(document).getScroll().top;
5949 var os = prevScroll;
5953 ft.removeClass('slideDown');
5954 ft.addClass('slideUp');
5957 ft.removeClass('slideUp');
5958 ft.addClass('slideDown');
5979 * @class Roo.bootstrap.NavSidebar
5980 * @extends Roo.bootstrap.Navbar
5981 * Bootstrap Sidebar class
5984 * Create a new Sidebar
5985 * @param {Object} config The config object
5989 Roo.bootstrap.NavSidebar = function(config){
5990 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5993 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5995 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5997 getAutoCreate : function(){
6002 cls: 'sidebar sidebar-nav'
6024 * @class Roo.bootstrap.NavGroup
6025 * @extends Roo.bootstrap.Component
6026 * Bootstrap NavGroup class
6027 * @cfg {String} align (left|right)
6028 * @cfg {Boolean} inverse
6029 * @cfg {String} type (nav|pills|tab) default nav
6030 * @cfg {String} navId - reference Id for navbar.
6031 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6034 * Create a new nav group
6035 * @param {Object} config The config object
6038 Roo.bootstrap.NavGroup = function(config){
6039 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6042 Roo.bootstrap.NavGroup.register(this);
6046 * Fires when the active item changes
6047 * @param {Roo.bootstrap.NavGroup} this
6048 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6049 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6056 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6068 getAutoCreate : function()
6070 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6076 if (Roo.bootstrap.version == 4) {
6077 if (['tabs','pills'].indexOf(this.type) != -1) {
6078 cfg.cls += ' nav-' + this.type;
6080 // trying to remove so header bar can right align top?
6081 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6082 // do not use on header bar...
6083 cfg.cls += ' navbar-nav';
6088 if (['tabs','pills'].indexOf(this.type) != -1) {
6089 cfg.cls += ' nav-' + this.type
6091 if (this.type !== 'nav') {
6092 Roo.log('nav type must be nav/tabs/pills')
6094 cfg.cls += ' navbar-nav'
6098 if (this.parent() && this.parent().sidebar) {
6101 cls: 'dashboard-menu sidebar-menu'
6107 if (this.form === true) {
6110 cls: 'navbar-form form-inline'
6112 //nav navbar-right ml-md-auto
6113 if (this.align === 'right') {
6114 cfg.cls += ' navbar-right ml-md-auto';
6116 cfg.cls += ' navbar-left';
6120 if (this.align === 'right') {
6121 cfg.cls += ' navbar-right ml-md-auto';
6123 cfg.cls += ' mr-auto';
6127 cfg.cls += ' navbar-inverse';
6135 * sets the active Navigation item
6136 * @param {Roo.bootstrap.NavItem} the new current navitem
6138 setActiveItem : function(item)
6141 Roo.each(this.navItems, function(v){
6146 v.setActive(false, true);
6153 item.setActive(true, true);
6154 this.fireEvent('changed', this, item, prev);
6159 * gets the active Navigation item
6160 * @return {Roo.bootstrap.NavItem} the current navitem
6162 getActive : function()
6166 Roo.each(this.navItems, function(v){
6177 indexOfNav : function()
6181 Roo.each(this.navItems, function(v,i){
6192 * adds a Navigation item
6193 * @param {Roo.bootstrap.NavItem} the navitem to add
6195 addItem : function(cfg)
6197 if (this.form && Roo.bootstrap.version == 4) {
6200 var cn = new Roo.bootstrap.NavItem(cfg);
6202 cn.parentId = this.id;
6203 cn.onRender(this.el, null);
6207 * register a Navigation item
6208 * @param {Roo.bootstrap.NavItem} the navitem to add
6210 register : function(item)
6212 this.navItems.push( item);
6213 item.navId = this.navId;
6218 * clear all the Navigation item
6221 clearAll : function()
6224 this.el.dom.innerHTML = '';
6227 getNavItem: function(tabId)
6230 Roo.each(this.navItems, function(e) {
6231 if (e.tabId == tabId) {
6241 setActiveNext : function()
6243 var i = this.indexOfNav(this.getActive());
6244 if (i > this.navItems.length) {
6247 this.setActiveItem(this.navItems[i+1]);
6249 setActivePrev : function()
6251 var i = this.indexOfNav(this.getActive());
6255 this.setActiveItem(this.navItems[i-1]);
6257 clearWasActive : function(except) {
6258 Roo.each(this.navItems, function(e) {
6259 if (e.tabId != except.tabId && e.was_active) {
6260 e.was_active = false;
6267 getWasActive : function ()
6270 Roo.each(this.navItems, function(e) {
6285 Roo.apply(Roo.bootstrap.NavGroup, {
6289 * register a Navigation Group
6290 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6292 register : function(navgrp)
6294 this.groups[navgrp.navId] = navgrp;
6298 * fetch a Navigation Group based on the navigation ID
6299 * @param {string} the navgroup to add
6300 * @returns {Roo.bootstrap.NavGroup} the navgroup
6302 get: function(navId) {
6303 if (typeof(this.groups[navId]) == 'undefined') {
6305 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6307 return this.groups[navId] ;
6322 * @class Roo.bootstrap.NavItem
6323 * @extends Roo.bootstrap.Component
6324 * Bootstrap Navbar.NavItem class
6325 * @cfg {String} href link to
6326 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6327 * @cfg {Boolean} button_outline show and outlined button
6328 * @cfg {String} html content of button
6329 * @cfg {String} badge text inside badge
6330 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6331 * @cfg {String} glyphicon DEPRICATED - use fa
6332 * @cfg {String} icon DEPRICATED - use fa
6333 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6334 * @cfg {Boolean} active Is item active
6335 * @cfg {Boolean} disabled Is item disabled
6336 * @cfg {String} linkcls Link Class
6337 * @cfg {Boolean} preventDefault (true | false) default false
6338 * @cfg {String} tabId the tab that this item activates.
6339 * @cfg {String} tagtype (a|span) render as a href or span?
6340 * @cfg {Boolean} animateRef (true|false) link to element default false
6343 * Create a new Navbar Item
6344 * @param {Object} config The config object
6346 Roo.bootstrap.NavItem = function(config){
6347 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6352 * The raw click event for the entire grid.
6353 * @param {Roo.EventObject} e
6358 * Fires when the active item active state changes
6359 * @param {Roo.bootstrap.NavItem} this
6360 * @param {boolean} state the new state
6366 * Fires when scroll to element
6367 * @param {Roo.bootstrap.NavItem} this
6368 * @param {Object} options
6369 * @param {Roo.EventObject} e
6377 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6386 preventDefault : false,
6394 button_outline : false,
6398 getAutoCreate : function(){
6405 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6408 cfg.cls += ' active' ;
6410 if (this.disabled) {
6411 cfg.cls += ' disabled';
6415 if (this.button_weight.length) {
6416 cfg.tag = this.href ? 'a' : 'button';
6417 cfg.html = this.html || '';
6418 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6420 cfg.href = this.href;
6423 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6425 cfg.cls += " nav-html";
6428 // menu .. should add dropdown-menu class - so no need for carat..
6430 if (this.badge !== '') {
6432 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6437 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6441 href : this.href || "#",
6442 html: this.html || '',
6446 if (this.tagtype == 'a') {
6447 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6451 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6452 } else if (this.fa) {
6453 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6454 } else if(this.glyphicon) {
6455 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6457 cfg.cn[0].cls += " nav-html";
6461 cfg.cn[0].html += " <span class='caret'></span>";
6465 if (this.badge !== '') {
6466 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474 onRender : function(ct, position)
6476 // Roo.log("Call onRender: " + this.xtype);
6477 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6481 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6482 this.navLink = this.el.select('.nav-link',true).first();
6483 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6488 initEvents: function()
6490 if (typeof (this.menu) != 'undefined') {
6491 this.menu.parentType = this.xtype;
6492 this.menu.triggerEl = this.el;
6493 this.menu = this.addxtype(Roo.apply({}, this.menu));
6496 this.el.on('click', this.onClick, this);
6498 //if(this.tagtype == 'span'){
6499 // this.el.select('span',true).on('click', this.onClick, this);
6502 // at this point parent should be available..
6503 this.parent().register(this);
6506 onClick : function(e)
6508 if (e.getTarget('.dropdown-menu-item')) {
6509 // did you click on a menu itemm.... - then don't trigger onclick..
6514 this.preventDefault ||
6517 Roo.log("NavItem - prevent Default?");
6521 if (this.disabled) {
6525 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6526 if (tg && tg.transition) {
6527 Roo.log("waiting for the transitionend");
6533 //Roo.log("fire event clicked");
6534 if(this.fireEvent('click', this, e) === false){
6538 if(this.tagtype == 'span'){
6542 //Roo.log(this.href);
6543 var ael = this.el.select('a',true).first();
6546 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6547 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6548 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6549 return; // ignore... - it's a 'hash' to another page.
6551 Roo.log("NavItem - prevent Default?");
6553 this.scrollToElement(e);
6557 var p = this.parent();
6559 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6560 if (typeof(p.setActiveItem) !== 'undefined') {
6561 p.setActiveItem(this);
6565 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6566 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6567 // remove the collapsed menu expand...
6568 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6572 isActive: function () {
6575 setActive : function(state, fire, is_was_active)
6577 if (this.active && !state && this.navId) {
6578 this.was_active = true;
6579 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6581 nv.clearWasActive(this);
6585 this.active = state;
6588 this.el.removeClass('active');
6589 this.navLink ? this.navLink.removeClass('active') : false;
6590 } else if (!this.el.hasClass('active')) {
6592 this.el.addClass('active');
6593 if (Roo.bootstrap.version == 4 && this.navLink ) {
6594 this.navLink.addClass('active');
6599 this.fireEvent('changed', this, state);
6602 // show a panel if it's registered and related..
6604 if (!this.navId || !this.tabId || !state || is_was_active) {
6608 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6612 var pan = tg.getPanelByName(this.tabId);
6616 // if we can not flip to new panel - go back to old nav highlight..
6617 if (false == tg.showPanel(pan)) {
6618 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6620 var onav = nv.getWasActive();
6622 onav.setActive(true, false, true);
6631 // this should not be here...
6632 setDisabled : function(state)
6634 this.disabled = state;
6636 this.el.removeClass('disabled');
6637 } else if (!this.el.hasClass('disabled')) {
6638 this.el.addClass('disabled');
6644 * Fetch the element to display the tooltip on.
6645 * @return {Roo.Element} defaults to this.el
6647 tooltipEl : function()
6649 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6652 scrollToElement : function(e)
6654 var c = document.body;
6657 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6659 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6660 c = document.documentElement;
6663 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6669 var o = target.calcOffsetsTo(c);
6676 this.fireEvent('scrollto', this, options, e);
6678 Roo.get(c).scrollTo('top', options.value, true);
6683 * Set the HTML (text content) of the item
6684 * @param {string} html content for the nav item
6686 setHtml : function(html)
6689 this.htmlEl.dom.innerHTML = html;
6701 * <span> icon </span>
6702 * <span> text </span>
6703 * <span>badge </span>
6707 * @class Roo.bootstrap.NavSidebarItem
6708 * @extends Roo.bootstrap.NavItem
6709 * Bootstrap Navbar.NavSidebarItem class
6710 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6711 * {Boolean} open is the menu open
6712 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6713 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6714 * {String} buttonSize (sm|md|lg)the extra classes for the button
6715 * {Boolean} showArrow show arrow next to the text (default true)
6717 * Create a new Navbar Button
6718 * @param {Object} config The config object
6720 Roo.bootstrap.NavSidebarItem = function(config){
6721 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6726 * The raw click event for the entire grid.
6727 * @param {Roo.EventObject} e
6732 * Fires when the active item active state changes
6733 * @param {Roo.bootstrap.NavSidebarItem} this
6734 * @param {boolean} state the new state
6742 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6744 badgeWeight : 'default',
6750 buttonWeight : 'default',
6756 getAutoCreate : function(){
6761 href : this.href || '#',
6767 if(this.buttonView){
6770 href : this.href || '#',
6771 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6784 cfg.cls += ' active';
6787 if (this.disabled) {
6788 cfg.cls += ' disabled';
6791 cfg.cls += ' open x-open';
6794 if (this.glyphicon || this.icon) {
6795 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6796 a.cn.push({ tag : 'i', cls : c }) ;
6799 if(!this.buttonView){
6802 html : this.html || ''
6809 if (this.badge !== '') {
6810 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6816 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6819 a.cls += ' dropdown-toggle treeview' ;
6825 initEvents : function()
6827 if (typeof (this.menu) != 'undefined') {
6828 this.menu.parentType = this.xtype;
6829 this.menu.triggerEl = this.el;
6830 this.menu = this.addxtype(Roo.apply({}, this.menu));
6833 this.el.on('click', this.onClick, this);
6835 if(this.badge !== ''){
6836 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6841 onClick : function(e)
6848 if(this.preventDefault){
6852 this.fireEvent('click', this, e);
6855 disable : function()
6857 this.setDisabled(true);
6862 this.setDisabled(false);
6865 setDisabled : function(state)
6867 if(this.disabled == state){
6871 this.disabled = state;
6874 this.el.addClass('disabled');
6878 this.el.removeClass('disabled');
6883 setActive : function(state)
6885 if(this.active == state){
6889 this.active = state;
6892 this.el.addClass('active');
6896 this.el.removeClass('active');
6901 isActive: function ()
6906 setBadge : function(str)
6912 this.badgeEl.dom.innerHTML = str;
6927 Roo.namespace('Roo.bootstrap.breadcrumb');
6931 * @class Roo.bootstrap.breadcrumb.Nav
6932 * @extends Roo.bootstrap.Component
6933 * Bootstrap Breadcrumb Nav Class
6935 * @children Roo.bootstrap.breadcrumb.Item
6938 * Create a new breadcrumb.Nav
6939 * @param {Object} config The config object
6943 Roo.bootstrap.breadcrumb.Nav = function(config){
6944 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6949 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6951 getAutoCreate : function()
6968 initEvents: function()
6970 this.olEl = this.el.select('ol',true).first();
6972 getChildContainer : function()
6988 * @class Roo.bootstrap.breadcrumb.Nav
6989 * @extends Roo.bootstrap.Component
6990 * Bootstrap Breadcrumb Nav Class
6992 * @children Roo.bootstrap.breadcrumb.Component
6993 * @cfg {String} html the content of the link.
6994 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6995 * @cfg {Boolean} active is it active
6999 * Create a new breadcrumb.Nav
7000 * @param {Object} config The config object
7003 Roo.bootstrap.breadcrumb.Item = function(config){
7004 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7009 * The img click event for the img.
7010 * @param {Roo.EventObject} e
7017 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7022 getAutoCreate : function()
7027 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7029 if (this.href !== false) {
7036 cfg.html = this.html;
7042 initEvents: function()
7045 this.el.select('a', true).first().on('click',this.onClick, this)
7049 onClick : function(e)
7052 this.fireEvent('click',this, e);
7065 * @class Roo.bootstrap.Row
7066 * @extends Roo.bootstrap.Component
7067 * Bootstrap Row class (contains columns...)
7071 * @param {Object} config The config object
7074 Roo.bootstrap.Row = function(config){
7075 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7078 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7080 getAutoCreate : function(){
7099 * @class Roo.bootstrap.Pagination
7100 * @extends Roo.bootstrap.Component
7101 * Bootstrap Pagination class
7102 * @cfg {String} size xs | sm | md | lg
7103 * @cfg {Boolean} inverse false | true
7106 * Create a new Pagination
7107 * @param {Object} config The config object
7110 Roo.bootstrap.Pagination = function(config){
7111 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7114 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7120 getAutoCreate : function(){
7126 cfg.cls += ' inverse';
7132 cfg.cls += " " + this.cls;
7150 * @class Roo.bootstrap.PaginationItem
7151 * @extends Roo.bootstrap.Component
7152 * Bootstrap PaginationItem class
7153 * @cfg {String} html text
7154 * @cfg {String} href the link
7155 * @cfg {Boolean} preventDefault (true | false) default true
7156 * @cfg {Boolean} active (true | false) default false
7157 * @cfg {Boolean} disabled default false
7161 * Create a new PaginationItem
7162 * @param {Object} config The config object
7166 Roo.bootstrap.PaginationItem = function(config){
7167 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7172 * The raw click event for the entire grid.
7173 * @param {Roo.EventObject} e
7179 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7183 preventDefault: true,
7188 getAutoCreate : function(){
7194 href : this.href ? this.href : '#',
7195 html : this.html ? this.html : ''
7205 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7209 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7215 initEvents: function() {
7217 this.el.on('click', this.onClick, this);
7220 onClick : function(e)
7222 Roo.log('PaginationItem on click ');
7223 if(this.preventDefault){
7231 this.fireEvent('click', this, e);
7247 * @class Roo.bootstrap.Slider
7248 * @extends Roo.bootstrap.Component
7249 * Bootstrap Slider class
7252 * Create a new Slider
7253 * @param {Object} config The config object
7256 Roo.bootstrap.Slider = function(config){
7257 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7260 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7262 getAutoCreate : function(){
7266 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7270 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7282 * Ext JS Library 1.1.1
7283 * Copyright(c) 2006-2007, Ext JS, LLC.
7285 * Originally Released Under LGPL - original licence link has changed is not relivant.
7288 * <script type="text/javascript">
7293 * @class Roo.grid.ColumnModel
7294 * @extends Roo.util.Observable
7295 * This is the default implementation of a ColumnModel used by the Grid. It defines
7296 * the columns in the grid.
7299 var colModel = new Roo.grid.ColumnModel([
7300 {header: "Ticker", width: 60, sortable: true, locked: true},
7301 {header: "Company Name", width: 150, sortable: true},
7302 {header: "Market Cap.", width: 100, sortable: true},
7303 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7304 {header: "Employees", width: 100, sortable: true, resizable: false}
7309 * The config options listed for this class are options which may appear in each
7310 * individual column definition.
7311 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7313 * @param {Object} config An Array of column config objects. See this class's
7314 * config objects for details.
7316 Roo.grid.ColumnModel = function(config){
7318 * The config passed into the constructor
7320 this.config = []; //config;
7323 // if no id, create one
7324 // if the column does not have a dataIndex mapping,
7325 // map it to the order it is in the config
7326 for(var i = 0, len = config.length; i < len; i++){
7327 this.addColumn(config[i]);
7332 * The width of columns which have no width specified (defaults to 100)
7335 this.defaultWidth = 100;
7338 * Default sortable of columns which have no sortable specified (defaults to false)
7341 this.defaultSortable = false;
7345 * @event widthchange
7346 * Fires when the width of a column changes.
7347 * @param {ColumnModel} this
7348 * @param {Number} columnIndex The column index
7349 * @param {Number} newWidth The new width
7351 "widthchange": true,
7353 * @event headerchange
7354 * Fires when the text of a header changes.
7355 * @param {ColumnModel} this
7356 * @param {Number} columnIndex The column index
7357 * @param {Number} newText The new header text
7359 "headerchange": true,
7361 * @event hiddenchange
7362 * Fires when a column is hidden or "unhidden".
7363 * @param {ColumnModel} this
7364 * @param {Number} columnIndex The column index
7365 * @param {Boolean} hidden true if hidden, false otherwise
7367 "hiddenchange": true,
7369 * @event columnmoved
7370 * Fires when a column is moved.
7371 * @param {ColumnModel} this
7372 * @param {Number} oldIndex
7373 * @param {Number} newIndex
7375 "columnmoved" : true,
7377 * @event columlockchange
7378 * Fires when a column's locked state is changed
7379 * @param {ColumnModel} this
7380 * @param {Number} colIndex
7381 * @param {Boolean} locked true if locked
7383 "columnlockchange" : true
7385 Roo.grid.ColumnModel.superclass.constructor.call(this);
7387 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7389 * @cfg {String} header The header text to display in the Grid view.
7392 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7393 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7394 * specified, the column's index is used as an index into the Record's data Array.
7397 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7398 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7401 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7402 * Defaults to the value of the {@link #defaultSortable} property.
7403 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7406 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7409 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7412 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7415 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7418 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7419 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7420 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7421 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7424 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7427 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7430 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7433 * @cfg {String} cursor (Optional)
7436 * @cfg {String} tooltip (Optional)
7439 * @cfg {Number} xs (Optional)
7442 * @cfg {Number} sm (Optional)
7445 * @cfg {Number} md (Optional)
7448 * @cfg {Number} lg (Optional)
7451 * Returns the id of the column at the specified index.
7452 * @param {Number} index The column index
7453 * @return {String} the id
7455 getColumnId : function(index){
7456 return this.config[index].id;
7460 * Returns the column for a specified id.
7461 * @param {String} id The column id
7462 * @return {Object} the column
7464 getColumnById : function(id){
7465 return this.lookup[id];
7470 * Returns the column Object for a specified dataIndex.
7471 * @param {String} dataIndex The column dataIndex
7472 * @return {Object|Boolean} the column or false if not found
7474 getColumnByDataIndex: function(dataIndex){
7475 var index = this.findColumnIndex(dataIndex);
7476 return index > -1 ? this.config[index] : false;
7480 * Returns the index for a specified column id.
7481 * @param {String} id The column id
7482 * @return {Number} the index, or -1 if not found
7484 getIndexById : function(id){
7485 for(var i = 0, len = this.config.length; i < len; i++){
7486 if(this.config[i].id == id){
7494 * Returns the index for a specified column dataIndex.
7495 * @param {String} dataIndex The column dataIndex
7496 * @return {Number} the index, or -1 if not found
7499 findColumnIndex : function(dataIndex){
7500 for(var i = 0, len = this.config.length; i < len; i++){
7501 if(this.config[i].dataIndex == dataIndex){
7509 moveColumn : function(oldIndex, newIndex){
7510 var c = this.config[oldIndex];
7511 this.config.splice(oldIndex, 1);
7512 this.config.splice(newIndex, 0, c);
7513 this.dataMap = null;
7514 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7517 isLocked : function(colIndex){
7518 return this.config[colIndex].locked === true;
7521 setLocked : function(colIndex, value, suppressEvent){
7522 if(this.isLocked(colIndex) == value){
7525 this.config[colIndex].locked = value;
7527 this.fireEvent("columnlockchange", this, colIndex, value);
7531 getTotalLockedWidth : function(){
7533 for(var i = 0; i < this.config.length; i++){
7534 if(this.isLocked(i) && !this.isHidden(i)){
7535 this.totalWidth += this.getColumnWidth(i);
7541 getLockedCount : function(){
7542 for(var i = 0, len = this.config.length; i < len; i++){
7543 if(!this.isLocked(i)){
7548 return this.config.length;
7552 * Returns the number of columns.
7555 getColumnCount : function(visibleOnly){
7556 if(visibleOnly === true){
7558 for(var i = 0, len = this.config.length; i < len; i++){
7559 if(!this.isHidden(i)){
7565 return this.config.length;
7569 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7570 * @param {Function} fn
7571 * @param {Object} scope (optional)
7572 * @return {Array} result
7574 getColumnsBy : function(fn, scope){
7576 for(var i = 0, len = this.config.length; i < len; i++){
7577 var c = this.config[i];
7578 if(fn.call(scope||this, c, i) === true){
7586 * Returns true if the specified column is sortable.
7587 * @param {Number} col The column index
7590 isSortable : function(col){
7591 if(typeof this.config[col].sortable == "undefined"){
7592 return this.defaultSortable;
7594 return this.config[col].sortable;
7598 * Returns the rendering (formatting) function defined for the column.
7599 * @param {Number} col The column index.
7600 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7602 getRenderer : function(col){
7603 if(!this.config[col].renderer){
7604 return Roo.grid.ColumnModel.defaultRenderer;
7606 return this.config[col].renderer;
7610 * Sets the rendering (formatting) function for a column.
7611 * @param {Number} col The column index
7612 * @param {Function} fn The function to use to process the cell's raw data
7613 * to return HTML markup for the grid view. The render function is called with
7614 * the following parameters:<ul>
7615 * <li>Data value.</li>
7616 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7617 * <li>css A CSS style string to apply to the table cell.</li>
7618 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7619 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7620 * <li>Row index</li>
7621 * <li>Column index</li>
7622 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7624 setRenderer : function(col, fn){
7625 this.config[col].renderer = fn;
7629 * Returns the width for the specified column.
7630 * @param {Number} col The column index
7633 getColumnWidth : function(col){
7634 return this.config[col].width * 1 || this.defaultWidth;
7638 * Sets the width for a column.
7639 * @param {Number} col The column index
7640 * @param {Number} width The new width
7642 setColumnWidth : function(col, width, suppressEvent){
7643 this.config[col].width = width;
7644 this.totalWidth = null;
7646 this.fireEvent("widthchange", this, col, width);
7651 * Returns the total width of all columns.
7652 * @param {Boolean} includeHidden True to include hidden column widths
7655 getTotalWidth : function(includeHidden){
7656 if(!this.totalWidth){
7657 this.totalWidth = 0;
7658 for(var i = 0, len = this.config.length; i < len; i++){
7659 if(includeHidden || !this.isHidden(i)){
7660 this.totalWidth += this.getColumnWidth(i);
7664 return this.totalWidth;
7668 * Returns the header for the specified column.
7669 * @param {Number} col The column index
7672 getColumnHeader : function(col){
7673 return this.config[col].header;
7677 * Sets the header for a column.
7678 * @param {Number} col The column index
7679 * @param {String} header The new header
7681 setColumnHeader : function(col, header){
7682 this.config[col].header = header;
7683 this.fireEvent("headerchange", this, col, header);
7687 * Returns the tooltip for the specified column.
7688 * @param {Number} col The column index
7691 getColumnTooltip : function(col){
7692 return this.config[col].tooltip;
7695 * Sets the tooltip for a column.
7696 * @param {Number} col The column index
7697 * @param {String} tooltip The new tooltip
7699 setColumnTooltip : function(col, tooltip){
7700 this.config[col].tooltip = tooltip;
7704 * Returns the dataIndex for the specified column.
7705 * @param {Number} col The column index
7708 getDataIndex : function(col){
7709 return this.config[col].dataIndex;
7713 * Sets the dataIndex for a column.
7714 * @param {Number} col The column index
7715 * @param {Number} dataIndex The new dataIndex
7717 setDataIndex : function(col, dataIndex){
7718 this.config[col].dataIndex = dataIndex;
7724 * Returns true if the cell is editable.
7725 * @param {Number} colIndex The column index
7726 * @param {Number} rowIndex The row index - this is nto actually used..?
7729 isCellEditable : function(colIndex, rowIndex){
7730 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7734 * Returns the editor defined for the cell/column.
7735 * return false or null to disable editing.
7736 * @param {Number} colIndex The column index
7737 * @param {Number} rowIndex The row index
7740 getCellEditor : function(colIndex, rowIndex){
7741 return this.config[colIndex].editor;
7745 * Sets if a column is editable.
7746 * @param {Number} col The column index
7747 * @param {Boolean} editable True if the column is editable
7749 setEditable : function(col, editable){
7750 this.config[col].editable = editable;
7755 * Returns true if the column is hidden.
7756 * @param {Number} colIndex The column index
7759 isHidden : function(colIndex){
7760 return this.config[colIndex].hidden;
7765 * Returns true if the column width cannot be changed
7767 isFixed : function(colIndex){
7768 return this.config[colIndex].fixed;
7772 * Returns true if the column can be resized
7775 isResizable : function(colIndex){
7776 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7779 * Sets if a column is hidden.
7780 * @param {Number} colIndex The column index
7781 * @param {Boolean} hidden True if the column is hidden
7783 setHidden : function(colIndex, hidden){
7784 this.config[colIndex].hidden = hidden;
7785 this.totalWidth = null;
7786 this.fireEvent("hiddenchange", this, colIndex, hidden);
7790 * Sets the editor for a column.
7791 * @param {Number} col The column index
7792 * @param {Object} editor The editor object
7794 setEditor : function(col, editor){
7795 this.config[col].editor = editor;
7798 * Add a column (experimental...) - defaults to adding to the end..
7799 * @param {Object} config
7801 addColumn : function(c)
7804 var i = this.config.length;
7807 if(typeof c.dataIndex == "undefined"){
7810 if(typeof c.renderer == "string"){
7811 c.renderer = Roo.util.Format[c.renderer];
7813 if(typeof c.id == "undefined"){
7816 if(c.editor && c.editor.xtype){
7817 c.editor = Roo.factory(c.editor, Roo.grid);
7819 if(c.editor && c.editor.isFormField){
7820 c.editor = new Roo.grid.GridEditor(c.editor);
7822 this.lookup[c.id] = c;
7827 Roo.grid.ColumnModel.defaultRenderer = function(value)
7829 if(typeof value == "object") {
7832 if(typeof value == "string" && value.length < 1){
7836 return String.format("{0}", value);
7839 // Alias for backwards compatibility
7840 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7843 * Ext JS Library 1.1.1
7844 * Copyright(c) 2006-2007, Ext JS, LLC.
7846 * Originally Released Under LGPL - original licence link has changed is not relivant.
7849 * <script type="text/javascript">
7853 * @class Roo.LoadMask
7854 * A simple utility class for generically masking elements while loading data. If the element being masked has
7855 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7856 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7857 * element's UpdateManager load indicator and will be destroyed after the initial load.
7859 * Create a new LoadMask
7860 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7861 * @param {Object} config The config object
7863 Roo.LoadMask = function(el, config){
7864 this.el = Roo.get(el);
7865 Roo.apply(this, config);
7867 this.store.on('beforeload', this.onBeforeLoad, this);
7868 this.store.on('load', this.onLoad, this);
7869 this.store.on('loadexception', this.onLoadException, this);
7870 this.removeMask = false;
7872 var um = this.el.getUpdateManager();
7873 um.showLoadIndicator = false; // disable the default indicator
7874 um.on('beforeupdate', this.onBeforeLoad, this);
7875 um.on('update', this.onLoad, this);
7876 um.on('failure', this.onLoad, this);
7877 this.removeMask = true;
7881 Roo.LoadMask.prototype = {
7883 * @cfg {Boolean} removeMask
7884 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7885 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7889 * The text to display in a centered loading message box (defaults to 'Loading...')
7893 * @cfg {String} msgCls
7894 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7896 msgCls : 'x-mask-loading',
7899 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7905 * Disables the mask to prevent it from being displayed
7907 disable : function(){
7908 this.disabled = true;
7912 * Enables the mask so that it can be displayed
7914 enable : function(){
7915 this.disabled = false;
7918 onLoadException : function()
7922 if (typeof(arguments[3]) != 'undefined') {
7923 Roo.MessageBox.alert("Error loading",arguments[3]);
7927 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7928 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7935 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7940 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7944 onBeforeLoad : function(){
7946 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7951 destroy : function(){
7953 this.store.un('beforeload', this.onBeforeLoad, this);
7954 this.store.un('load', this.onLoad, this);
7955 this.store.un('loadexception', this.onLoadException, this);
7957 var um = this.el.getUpdateManager();
7958 um.un('beforeupdate', this.onBeforeLoad, this);
7959 um.un('update', this.onLoad, this);
7960 um.un('failure', this.onLoad, this);
7971 * @class Roo.bootstrap.Table
7972 * @extends Roo.bootstrap.Component
7973 * Bootstrap Table class
7974 * @cfg {String} cls table class
7975 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7976 * @cfg {String} bgcolor Specifies the background color for a table
7977 * @cfg {Number} border Specifies whether the table cells should have borders or not
7978 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7979 * @cfg {Number} cellspacing Specifies the space between cells
7980 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7981 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7982 * @cfg {String} sortable Specifies that the table should be sortable
7983 * @cfg {String} summary Specifies a summary of the content of a table
7984 * @cfg {Number} width Specifies the width of a table
7985 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7987 * @cfg {boolean} striped Should the rows be alternative striped
7988 * @cfg {boolean} bordered Add borders to the table
7989 * @cfg {boolean} hover Add hover highlighting
7990 * @cfg {boolean} condensed Format condensed
7991 * @cfg {boolean} responsive Format condensed
7992 * @cfg {Boolean} loadMask (true|false) default false
7993 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7994 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7995 * @cfg {Boolean} rowSelection (true|false) default false
7996 * @cfg {Boolean} cellSelection (true|false) default false
7997 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7998 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7999 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8000 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8004 * Create a new Table
8005 * @param {Object} config The config object
8008 Roo.bootstrap.Table = function(config){
8009 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8014 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8015 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8016 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8017 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8019 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8021 this.sm.grid = this;
8022 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8023 this.sm = this.selModel;
8024 this.sm.xmodule = this.xmodule || false;
8027 if (this.cm && typeof(this.cm.config) == 'undefined') {
8028 this.colModel = new Roo.grid.ColumnModel(this.cm);
8029 this.cm = this.colModel;
8030 this.cm.xmodule = this.xmodule || false;
8033 this.store= Roo.factory(this.store, Roo.data);
8034 this.ds = this.store;
8035 this.ds.xmodule = this.xmodule || false;
8038 if (this.footer && this.store) {
8039 this.footer.dataSource = this.ds;
8040 this.footer = Roo.factory(this.footer);
8047 * Fires when a cell is clicked
8048 * @param {Roo.bootstrap.Table} this
8049 * @param {Roo.Element} el
8050 * @param {Number} rowIndex
8051 * @param {Number} columnIndex
8052 * @param {Roo.EventObject} e
8056 * @event celldblclick
8057 * Fires when a cell is double clicked
8058 * @param {Roo.bootstrap.Table} this
8059 * @param {Roo.Element} el
8060 * @param {Number} rowIndex
8061 * @param {Number} columnIndex
8062 * @param {Roo.EventObject} e
8064 "celldblclick" : true,
8067 * Fires when a row is clicked
8068 * @param {Roo.bootstrap.Table} this
8069 * @param {Roo.Element} el
8070 * @param {Number} rowIndex
8071 * @param {Roo.EventObject} e
8075 * @event rowdblclick
8076 * Fires when a row is double clicked
8077 * @param {Roo.bootstrap.Table} this
8078 * @param {Roo.Element} el
8079 * @param {Number} rowIndex
8080 * @param {Roo.EventObject} e
8082 "rowdblclick" : true,
8085 * Fires when a mouseover occur
8086 * @param {Roo.bootstrap.Table} this
8087 * @param {Roo.Element} el
8088 * @param {Number} rowIndex
8089 * @param {Number} columnIndex
8090 * @param {Roo.EventObject} e
8095 * Fires when a mouseout occur
8096 * @param {Roo.bootstrap.Table} this
8097 * @param {Roo.Element} el
8098 * @param {Number} rowIndex
8099 * @param {Number} columnIndex
8100 * @param {Roo.EventObject} e
8105 * Fires when a row is rendered, so you can change add a style to it.
8106 * @param {Roo.bootstrap.Table} this
8107 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8111 * @event rowsrendered
8112 * Fires when all the rows have been rendered
8113 * @param {Roo.bootstrap.Table} this
8115 'rowsrendered' : true,
8117 * @event contextmenu
8118 * The raw contextmenu event for the entire grid.
8119 * @param {Roo.EventObject} e
8121 "contextmenu" : true,
8123 * @event rowcontextmenu
8124 * Fires when a row is right clicked
8125 * @param {Roo.bootstrap.Table} this
8126 * @param {Number} rowIndex
8127 * @param {Roo.EventObject} e
8129 "rowcontextmenu" : true,
8131 * @event cellcontextmenu
8132 * Fires when a cell is right clicked
8133 * @param {Roo.bootstrap.Table} this
8134 * @param {Number} rowIndex
8135 * @param {Number} cellIndex
8136 * @param {Roo.EventObject} e
8138 "cellcontextmenu" : true,
8140 * @event headercontextmenu
8141 * Fires when a header is right clicked
8142 * @param {Roo.bootstrap.Table} this
8143 * @param {Number} columnIndex
8144 * @param {Roo.EventObject} e
8146 "headercontextmenu" : true
8150 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8176 rowSelection : false,
8177 cellSelection : false,
8180 // Roo.Element - the tbody
8182 // Roo.Element - thead element
8185 container: false, // used by gridpanel...
8191 auto_hide_footer : false,
8193 getAutoCreate : function()
8195 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8202 if (this.scrollBody) {
8203 cfg.cls += ' table-body-fixed';
8206 cfg.cls += ' table-striped';
8210 cfg.cls += ' table-hover';
8212 if (this.bordered) {
8213 cfg.cls += ' table-bordered';
8215 if (this.condensed) {
8216 cfg.cls += ' table-condensed';
8218 if (this.responsive) {
8219 cfg.cls += ' table-responsive';
8223 cfg.cls+= ' ' +this.cls;
8226 // this lot should be simplifed...
8239 ].forEach(function(k) {
8247 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8250 if(this.store || this.cm){
8251 if(this.headerShow){
8252 cfg.cn.push(this.renderHeader());
8255 cfg.cn.push(this.renderBody());
8257 if(this.footerShow){
8258 cfg.cn.push(this.renderFooter());
8260 // where does this come from?
8261 //cfg.cls+= ' TableGrid';
8264 return { cn : [ cfg ] };
8267 initEvents : function()
8269 if(!this.store || !this.cm){
8272 if (this.selModel) {
8273 this.selModel.initEvents();
8277 //Roo.log('initEvents with ds!!!!');
8279 this.mainBody = this.el.select('tbody', true).first();
8280 this.mainHead = this.el.select('thead', true).first();
8281 this.mainFoot = this.el.select('tfoot', true).first();
8286 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8287 e.on('click', this.sort, this);
8290 this.mainBody.on("click", this.onClick, this);
8291 this.mainBody.on("dblclick", this.onDblClick, this);
8293 // why is this done????? = it breaks dialogs??
8294 //this.parent().el.setStyle('position', 'relative');
8298 this.footer.parentId = this.id;
8299 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8302 this.el.select('tfoot tr td').first().addClass('hide');
8307 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8310 this.store.on('load', this.onLoad, this);
8311 this.store.on('beforeload', this.onBeforeLoad, this);
8312 this.store.on('update', this.onUpdate, this);
8313 this.store.on('add', this.onAdd, this);
8314 this.store.on("clear", this.clear, this);
8316 this.el.on("contextmenu", this.onContextMenu, this);
8318 this.mainBody.on('scroll', this.onBodyScroll, this);
8320 this.cm.on("headerchange", this.onHeaderChange, this);
8322 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8326 onContextMenu : function(e, t)
8328 this.processEvent("contextmenu", e);
8331 processEvent : function(name, e)
8333 if (name != 'touchstart' ) {
8334 this.fireEvent(name, e);
8337 var t = e.getTarget();
8339 var cell = Roo.get(t);
8345 if(cell.findParent('tfoot', false, true)){
8349 if(cell.findParent('thead', false, true)){
8351 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8352 cell = Roo.get(t).findParent('th', false, true);
8354 Roo.log("failed to find th in thead?");
8355 Roo.log(e.getTarget());
8360 var cellIndex = cell.dom.cellIndex;
8362 var ename = name == 'touchstart' ? 'click' : name;
8363 this.fireEvent("header" + ename, this, cellIndex, e);
8368 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8369 cell = Roo.get(t).findParent('td', false, true);
8371 Roo.log("failed to find th in tbody?");
8372 Roo.log(e.getTarget());
8377 var row = cell.findParent('tr', false, true);
8378 var cellIndex = cell.dom.cellIndex;
8379 var rowIndex = row.dom.rowIndex - 1;
8383 this.fireEvent("row" + name, this, rowIndex, e);
8387 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8393 onMouseover : function(e, el)
8395 var cell = Roo.get(el);
8401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402 cell = cell.findParent('td', false, true);
8405 var row = cell.findParent('tr', false, true);
8406 var cellIndex = cell.dom.cellIndex;
8407 var rowIndex = row.dom.rowIndex - 1; // start from 0
8409 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8413 onMouseout : function(e, el)
8415 var cell = Roo.get(el);
8421 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8422 cell = cell.findParent('td', false, true);
8425 var row = cell.findParent('tr', false, true);
8426 var cellIndex = cell.dom.cellIndex;
8427 var rowIndex = row.dom.rowIndex - 1; // start from 0
8429 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8433 onClick : function(e, el)
8435 var cell = Roo.get(el);
8437 if(!cell || (!this.cellSelection && !this.rowSelection)){
8441 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8442 cell = cell.findParent('td', false, true);
8445 if(!cell || typeof(cell) == 'undefined'){
8449 var row = cell.findParent('tr', false, true);
8451 if(!row || typeof(row) == 'undefined'){
8455 var cellIndex = cell.dom.cellIndex;
8456 var rowIndex = this.getRowIndex(row);
8458 // why??? - should these not be based on SelectionModel?
8459 //if(this.cellSelection){
8460 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8463 //if(this.rowSelection){
8464 this.fireEvent('rowclick', this, row, rowIndex, e);
8469 onDblClick : function(e,el)
8471 var cell = Roo.get(el);
8473 if(!cell || (!this.cellSelection && !this.rowSelection)){
8477 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8478 cell = cell.findParent('td', false, true);
8481 if(!cell || typeof(cell) == 'undefined'){
8485 var row = cell.findParent('tr', false, true);
8487 if(!row || typeof(row) == 'undefined'){
8491 var cellIndex = cell.dom.cellIndex;
8492 var rowIndex = this.getRowIndex(row);
8494 if(this.cellSelection){
8495 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8498 if(this.rowSelection){
8499 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8503 sort : function(e,el)
8505 var col = Roo.get(el);
8507 if(!col.hasClass('sortable')){
8511 var sort = col.attr('sort');
8514 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8518 this.store.sortInfo = {field : sort, direction : dir};
8521 Roo.log("calling footer first");
8522 this.footer.onClick('first');
8525 this.store.load({ params : { start : 0 } });
8529 renderHeader : function()
8537 this.totalWidth = 0;
8539 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541 var config = cm.config[i];
8545 cls : 'x-hcol-' + i,
8548 html: cm.getColumnHeader(i)
8551 var tooltip = cm.getColumnTooltip(i);
8553 c.tooltip = tooltip;
8559 if(typeof(config.sortable) != 'undefined' && config.sortable){
8561 c.html = '<i class="fa"></i>' + c.html;
8564 // could use BS4 hidden-..-down
8566 if(typeof(config.lgHeader) != 'undefined'){
8567 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8570 if(typeof(config.mdHeader) != 'undefined'){
8571 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8574 if(typeof(config.smHeader) != 'undefined'){
8575 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8578 if(typeof(config.xsHeader) != 'undefined'){
8579 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8586 if(typeof(config.tooltip) != 'undefined'){
8587 c.tooltip = config.tooltip;
8590 if(typeof(config.colspan) != 'undefined'){
8591 c.colspan = config.colspan;
8594 if(typeof(config.hidden) != 'undefined' && config.hidden){
8595 c.style += ' display:none;';
8598 if(typeof(config.dataIndex) != 'undefined'){
8599 c.sort = config.dataIndex;
8604 if(typeof(config.align) != 'undefined' && config.align.length){
8605 c.style += ' text-align:' + config.align + ';';
8608 if(typeof(config.width) != 'undefined'){
8609 c.style += ' width:' + config.width + 'px;';
8610 this.totalWidth += config.width;
8612 this.totalWidth += 100; // assume minimum of 100 per column?
8615 if(typeof(config.cls) != 'undefined'){
8616 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8619 ['xs','sm','md','lg'].map(function(size){
8621 if(typeof(config[size]) == 'undefined'){
8625 if (!config[size]) { // 0 = hidden
8626 // BS 4 '0' is treated as hide that column and below.
8627 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8631 c.cls += ' col-' + size + '-' + config[size] + (
8632 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8644 renderBody : function()
8654 colspan : this.cm.getColumnCount()
8664 renderFooter : function()
8674 colspan : this.cm.getColumnCount()
8688 // Roo.log('ds onload');
8693 var ds = this.store;
8695 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8696 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8697 if (_this.store.sortInfo) {
8699 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8700 e.select('i', true).addClass(['fa-arrow-up']);
8703 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8704 e.select('i', true).addClass(['fa-arrow-down']);
8709 var tbody = this.mainBody;
8711 if(ds.getCount() > 0){
8712 ds.data.each(function(d,rowIndex){
8713 var row = this.renderRow(cm, ds, rowIndex);
8715 tbody.createChild(row);
8719 if(row.cellObjects.length){
8720 Roo.each(row.cellObjects, function(r){
8721 _this.renderCellObject(r);
8728 var tfoot = this.el.select('tfoot', true).first();
8730 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8732 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8734 var total = this.ds.getTotalCount();
8736 if(this.footer.pageSize < total){
8737 this.mainFoot.show();
8741 Roo.each(this.el.select('tbody td', true).elements, function(e){
8742 e.on('mouseover', _this.onMouseover, _this);
8745 Roo.each(this.el.select('tbody td', true).elements, function(e){
8746 e.on('mouseout', _this.onMouseout, _this);
8748 this.fireEvent('rowsrendered', this);
8754 onUpdate : function(ds,record)
8756 this.refreshRow(record);
8760 onRemove : function(ds, record, index, isUpdate){
8761 if(isUpdate !== true){
8762 this.fireEvent("beforerowremoved", this, index, record);
8764 var bt = this.mainBody.dom;
8766 var rows = this.el.select('tbody > tr', true).elements;
8768 if(typeof(rows[index]) != 'undefined'){
8769 bt.removeChild(rows[index].dom);
8772 // if(bt.rows[index]){
8773 // bt.removeChild(bt.rows[index]);
8776 if(isUpdate !== true){
8777 //this.stripeRows(index);
8778 //this.syncRowHeights(index, index);
8780 this.fireEvent("rowremoved", this, index, record);
8784 onAdd : function(ds, records, rowIndex)
8786 //Roo.log('on Add called');
8787 // - note this does not handle multiple adding very well..
8788 var bt = this.mainBody.dom;
8789 for (var i =0 ; i < records.length;i++) {
8790 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8791 //Roo.log(records[i]);
8792 //Roo.log(this.store.getAt(rowIndex+i));
8793 this.insertRow(this.store, rowIndex + i, false);
8800 refreshRow : function(record){
8801 var ds = this.store, index;
8802 if(typeof record == 'number'){
8804 record = ds.getAt(index);
8806 index = ds.indexOf(record);
8808 return; // should not happen - but seems to
8811 this.insertRow(ds, index, true);
8813 this.onRemove(ds, record, index+1, true);
8815 //this.syncRowHeights(index, index);
8817 this.fireEvent("rowupdated", this, index, record);
8820 insertRow : function(dm, rowIndex, isUpdate){
8823 this.fireEvent("beforerowsinserted", this, rowIndex);
8825 //var s = this.getScrollState();
8826 var row = this.renderRow(this.cm, this.store, rowIndex);
8827 // insert before rowIndex..
8828 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8832 if(row.cellObjects.length){
8833 Roo.each(row.cellObjects, function(r){
8834 _this.renderCellObject(r);
8839 this.fireEvent("rowsinserted", this, rowIndex);
8840 //this.syncRowHeights(firstRow, lastRow);
8841 //this.stripeRows(firstRow);
8848 getRowDom : function(rowIndex)
8850 var rows = this.el.select('tbody > tr', true).elements;
8852 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8855 // returns the object tree for a tr..
8858 renderRow : function(cm, ds, rowIndex)
8860 var d = ds.getAt(rowIndex);
8864 cls : 'x-row-' + rowIndex,
8868 var cellObjects = [];
8870 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8871 var config = cm.config[i];
8873 var renderer = cm.getRenderer(i);
8877 if(typeof(renderer) !== 'undefined'){
8878 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8880 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8881 // and are rendered into the cells after the row is rendered - using the id for the element.
8883 if(typeof(value) === 'object'){
8893 rowIndex : rowIndex,
8898 this.fireEvent('rowclass', this, rowcfg);
8902 // this might end up displaying HTML?
8903 // this is too messy... - better to only do it on columsn you know are going to be too long
8904 //tooltip : (typeof(value) === 'object') ? '' : value,
8905 cls : rowcfg.rowClass + ' x-col-' + i,
8907 html: (typeof(value) === 'object') ? '' : value
8914 if(typeof(config.colspan) != 'undefined'){
8915 td.colspan = config.colspan;
8918 if(typeof(config.hidden) != 'undefined' && config.hidden){
8919 td.style += ' display:none;';
8922 if(typeof(config.align) != 'undefined' && config.align.length){
8923 td.style += ' text-align:' + config.align + ';';
8925 if(typeof(config.valign) != 'undefined' && config.valign.length){
8926 td.style += ' vertical-align:' + config.valign + ';';
8929 if(typeof(config.width) != 'undefined'){
8930 td.style += ' width:' + config.width + 'px;';
8933 if(typeof(config.cursor) != 'undefined'){
8934 td.style += ' cursor:' + config.cursor + ';';
8937 if(typeof(config.cls) != 'undefined'){
8938 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8941 ['xs','sm','md','lg'].map(function(size){
8943 if(typeof(config[size]) == 'undefined'){
8949 if (!config[size]) { // 0 = hidden
8950 // BS 4 '0' is treated as hide that column and below.
8951 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8955 td.cls += ' col-' + size + '-' + config[size] + (
8956 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8966 row.cellObjects = cellObjects;
8974 onBeforeLoad : function()
8983 this.el.select('tbody', true).first().dom.innerHTML = '';
8986 * Show or hide a row.
8987 * @param {Number} rowIndex to show or hide
8988 * @param {Boolean} state hide
8990 setRowVisibility : function(rowIndex, state)
8992 var bt = this.mainBody.dom;
8994 var rows = this.el.select('tbody > tr', true).elements;
8996 if(typeof(rows[rowIndex]) == 'undefined'){
8999 rows[rowIndex].dom.style.display = state ? '' : 'none';
9003 getSelectionModel : function(){
9005 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9007 return this.selModel;
9010 * Render the Roo.bootstrap object from renderder
9012 renderCellObject : function(r)
9016 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9018 var t = r.cfg.render(r.container);
9021 Roo.each(r.cfg.cn, function(c){
9023 container: t.getChildContainer(),
9026 _this.renderCellObject(child);
9031 getRowIndex : function(row)
9035 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9046 * Returns the grid's underlying element = used by panel.Grid
9047 * @return {Element} The element
9049 getGridEl : function(){
9053 * Forces a resize - used by panel.Grid
9054 * @return {Element} The element
9056 autoSize : function()
9058 //var ctr = Roo.get(this.container.dom.parentElement);
9059 var ctr = Roo.get(this.el.dom);
9061 var thd = this.getGridEl().select('thead',true).first();
9062 var tbd = this.getGridEl().select('tbody', true).first();
9063 var tfd = this.getGridEl().select('tfoot', true).first();
9065 var cw = ctr.getWidth();
9066 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9070 tbd.setWidth(ctr.getWidth());
9071 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9072 // this needs fixing for various usage - currently only hydra job advers I think..
9074 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9076 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9079 cw = Math.max(cw, this.totalWidth);
9080 this.getGridEl().select('tbody tr',true).setWidth(cw);
9082 // resize 'expandable coloumn?
9084 return; // we doe not have a view in this design..
9087 onBodyScroll: function()
9089 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9091 this.mainHead.setStyle({
9092 'position' : 'relative',
9093 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9099 var scrollHeight = this.mainBody.dom.scrollHeight;
9101 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9103 var height = this.mainBody.getHeight();
9105 if(scrollHeight - height == scrollTop) {
9107 var total = this.ds.getTotalCount();
9109 if(this.footer.cursor + this.footer.pageSize < total){
9111 this.footer.ds.load({
9113 start : this.footer.cursor + this.footer.pageSize,
9114 limit : this.footer.pageSize
9124 onHeaderChange : function()
9126 var header = this.renderHeader();
9127 var table = this.el.select('table', true).first();
9129 this.mainHead.remove();
9130 this.mainHead = table.createChild(header, this.mainBody, false);
9132 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9133 e.on('click', this.sort, this);
9139 onHiddenChange : function(colModel, colIndex, hidden)
9141 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9142 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9144 this.CSS.updateRule(thSelector, "display", "");
9145 this.CSS.updateRule(tdSelector, "display", "");
9148 this.CSS.updateRule(thSelector, "display", "none");
9149 this.CSS.updateRule(tdSelector, "display", "none");
9152 this.onHeaderChange();
9156 setColumnWidth: function(col_index, width)
9158 // width = "md-2 xs-2..."
9159 if(!this.colModel.config[col_index]) {
9163 var w = width.split(" ");
9165 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9167 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9170 for(var j = 0; j < w.length; j++) {
9176 var size_cls = w[j].split("-");
9178 if(!Number.isInteger(size_cls[1] * 1)) {
9182 if(!this.colModel.config[col_index][size_cls[0]]) {
9186 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9190 h_row[0].classList.replace(
9191 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9192 "col-"+size_cls[0]+"-"+size_cls[1]
9195 for(var i = 0; i < rows.length; i++) {
9197 var size_cls = w[j].split("-");
9199 if(!Number.isInteger(size_cls[1] * 1)) {
9203 if(!this.colModel.config[col_index][size_cls[0]]) {
9207 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9211 rows[i].classList.replace(
9212 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9213 "col-"+size_cls[0]+"-"+size_cls[1]
9217 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9232 * @class Roo.bootstrap.TableCell
9233 * @extends Roo.bootstrap.Component
9234 * Bootstrap TableCell class
9235 * @cfg {String} html cell contain text
9236 * @cfg {String} cls cell class
9237 * @cfg {String} tag cell tag (td|th) default td
9238 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9239 * @cfg {String} align Aligns the content in a cell
9240 * @cfg {String} axis Categorizes cells
9241 * @cfg {String} bgcolor Specifies the background color of a cell
9242 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9243 * @cfg {Number} colspan Specifies the number of columns a cell should span
9244 * @cfg {String} headers Specifies one or more header cells a cell is related to
9245 * @cfg {Number} height Sets the height of a cell
9246 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9247 * @cfg {Number} rowspan Sets the number of rows a cell should span
9248 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9249 * @cfg {String} valign Vertical aligns the content in a cell
9250 * @cfg {Number} width Specifies the width of a cell
9253 * Create a new TableCell
9254 * @param {Object} config The config object
9257 Roo.bootstrap.TableCell = function(config){
9258 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9261 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9281 getAutoCreate : function(){
9282 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9302 cfg.align=this.align
9308 cfg.bgcolor=this.bgcolor
9311 cfg.charoff=this.charoff
9314 cfg.colspan=this.colspan
9317 cfg.headers=this.headers
9320 cfg.height=this.height
9323 cfg.nowrap=this.nowrap
9326 cfg.rowspan=this.rowspan
9329 cfg.scope=this.scope
9332 cfg.valign=this.valign
9335 cfg.width=this.width
9354 * @class Roo.bootstrap.TableRow
9355 * @extends Roo.bootstrap.Component
9356 * Bootstrap TableRow class
9357 * @cfg {String} cls row class
9358 * @cfg {String} align Aligns the content in a table row
9359 * @cfg {String} bgcolor Specifies a background color for a table row
9360 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9361 * @cfg {String} valign Vertical aligns the content in a table row
9364 * Create a new TableRow
9365 * @param {Object} config The config object
9368 Roo.bootstrap.TableRow = function(config){
9369 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9372 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9380 getAutoCreate : function(){
9381 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9391 cfg.align = this.align;
9394 cfg.bgcolor = this.bgcolor;
9397 cfg.charoff = this.charoff;
9400 cfg.valign = this.valign;
9418 * @class Roo.bootstrap.TableBody
9419 * @extends Roo.bootstrap.Component
9420 * Bootstrap TableBody class
9421 * @cfg {String} cls element class
9422 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9423 * @cfg {String} align Aligns the content inside the element
9424 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9425 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9428 * Create a new TableBody
9429 * @param {Object} config The config object
9432 Roo.bootstrap.TableBody = function(config){
9433 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9436 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9444 getAutoCreate : function(){
9445 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9459 cfg.align = this.align;
9462 cfg.charoff = this.charoff;
9465 cfg.valign = this.valign;
9472 // initEvents : function()
9479 // this.store = Roo.factory(this.store, Roo.data);
9480 // this.store.on('load', this.onLoad, this);
9482 // this.store.load();
9486 // onLoad: function ()
9488 // this.fireEvent('load', this);
9498 * Ext JS Library 1.1.1
9499 * Copyright(c) 2006-2007, Ext JS, LLC.
9501 * Originally Released Under LGPL - original licence link has changed is not relivant.
9504 * <script type="text/javascript">
9507 // as we use this in bootstrap.
9508 Roo.namespace('Roo.form');
9510 * @class Roo.form.Action
9511 * Internal Class used to handle form actions
9513 * @param {Roo.form.BasicForm} el The form element or its id
9514 * @param {Object} config Configuration options
9519 // define the action interface
9520 Roo.form.Action = function(form, options){
9522 this.options = options || {};
9525 * Client Validation Failed
9528 Roo.form.Action.CLIENT_INVALID = 'client';
9530 * Server Validation Failed
9533 Roo.form.Action.SERVER_INVALID = 'server';
9535 * Connect to Server Failed
9538 Roo.form.Action.CONNECT_FAILURE = 'connect';
9540 * Reading Data from Server Failed
9543 Roo.form.Action.LOAD_FAILURE = 'load';
9545 Roo.form.Action.prototype = {
9547 failureType : undefined,
9548 response : undefined,
9552 run : function(options){
9557 success : function(response){
9562 handleResponse : function(response){
9566 // default connection failure
9567 failure : function(response){
9569 this.response = response;
9570 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9571 this.form.afterAction(this, false);
9574 processResponse : function(response){
9575 this.response = response;
9576 if(!response.responseText){
9579 this.result = this.handleResponse(response);
9583 // utility functions used internally
9584 getUrl : function(appendParams){
9585 var url = this.options.url || this.form.url || this.form.el.dom.action;
9587 var p = this.getParams();
9589 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9595 getMethod : function(){
9596 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9599 getParams : function(){
9600 var bp = this.form.baseParams;
9601 var p = this.options.params;
9603 if(typeof p == "object"){
9604 p = Roo.urlEncode(Roo.applyIf(p, bp));
9605 }else if(typeof p == 'string' && bp){
9606 p += '&' + Roo.urlEncode(bp);
9609 p = Roo.urlEncode(bp);
9614 createCallback : function(){
9616 success: this.success,
9617 failure: this.failure,
9619 timeout: (this.form.timeout*1000),
9620 upload: this.form.fileUpload ? this.success : undefined
9625 Roo.form.Action.Submit = function(form, options){
9626 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9629 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9632 haveProgress : false,
9633 uploadComplete : false,
9635 // uploadProgress indicator.
9636 uploadProgress : function()
9638 if (!this.form.progressUrl) {
9642 if (!this.haveProgress) {
9643 Roo.MessageBox.progress("Uploading", "Uploading");
9645 if (this.uploadComplete) {
9646 Roo.MessageBox.hide();
9650 this.haveProgress = true;
9652 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9654 var c = new Roo.data.Connection();
9656 url : this.form.progressUrl,
9661 success : function(req){
9662 //console.log(data);
9666 rdata = Roo.decode(req.responseText)
9668 Roo.log("Invalid data from server..");
9672 if (!rdata || !rdata.success) {
9674 Roo.MessageBox.alert(Roo.encode(rdata));
9677 var data = rdata.data;
9679 if (this.uploadComplete) {
9680 Roo.MessageBox.hide();
9685 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9686 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9689 this.uploadProgress.defer(2000,this);
9692 failure: function(data) {
9693 Roo.log('progress url failed ');
9704 // run get Values on the form, so it syncs any secondary forms.
9705 this.form.getValues();
9707 var o = this.options;
9708 var method = this.getMethod();
9709 var isPost = method == 'POST';
9710 if(o.clientValidation === false || this.form.isValid()){
9712 if (this.form.progressUrl) {
9713 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9714 (new Date() * 1) + '' + Math.random());
9719 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9720 form:this.form.el.dom,
9721 url:this.getUrl(!isPost),
9723 params:isPost ? this.getParams() : null,
9724 isUpload: this.form.fileUpload,
9725 formData : this.form.formData
9728 this.uploadProgress();
9730 }else if (o.clientValidation !== false){ // client validation failed
9731 this.failureType = Roo.form.Action.CLIENT_INVALID;
9732 this.form.afterAction(this, false);
9736 success : function(response)
9738 this.uploadComplete= true;
9739 if (this.haveProgress) {
9740 Roo.MessageBox.hide();
9744 var result = this.processResponse(response);
9745 if(result === true || result.success){
9746 this.form.afterAction(this, true);
9750 this.form.markInvalid(result.errors);
9751 this.failureType = Roo.form.Action.SERVER_INVALID;
9753 this.form.afterAction(this, false);
9755 failure : function(response)
9757 this.uploadComplete= true;
9758 if (this.haveProgress) {
9759 Roo.MessageBox.hide();
9762 this.response = response;
9763 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9764 this.form.afterAction(this, false);
9767 handleResponse : function(response){
9768 if(this.form.errorReader){
9769 var rs = this.form.errorReader.read(response);
9772 for(var i = 0, len = rs.records.length; i < len; i++) {
9773 var r = rs.records[i];
9777 if(errors.length < 1){
9781 success : rs.success,
9787 ret = Roo.decode(response.responseText);
9791 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9801 Roo.form.Action.Load = function(form, options){
9802 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9803 this.reader = this.form.reader;
9806 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9811 Roo.Ajax.request(Roo.apply(
9812 this.createCallback(), {
9813 method:this.getMethod(),
9814 url:this.getUrl(false),
9815 params:this.getParams()
9819 success : function(response){
9821 var result = this.processResponse(response);
9822 if(result === true || !result.success || !result.data){
9823 this.failureType = Roo.form.Action.LOAD_FAILURE;
9824 this.form.afterAction(this, false);
9827 this.form.clearInvalid();
9828 this.form.setValues(result.data);
9829 this.form.afterAction(this, true);
9832 handleResponse : function(response){
9833 if(this.form.reader){
9834 var rs = this.form.reader.read(response);
9835 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9837 success : rs.success,
9841 return Roo.decode(response.responseText);
9845 Roo.form.Action.ACTION_TYPES = {
9846 'load' : Roo.form.Action.Load,
9847 'submit' : Roo.form.Action.Submit
9856 * @class Roo.bootstrap.Form
9857 * @extends Roo.bootstrap.Component
9858 * Bootstrap Form class
9859 * @cfg {String} method GET | POST (default POST)
9860 * @cfg {String} labelAlign top | left (default top)
9861 * @cfg {String} align left | right - for navbars
9862 * @cfg {Boolean} loadMask load mask when submit (default true)
9867 * @param {Object} config The config object
9871 Roo.bootstrap.Form = function(config){
9873 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9875 Roo.bootstrap.Form.popover.apply();
9879 * @event clientvalidation
9880 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9881 * @param {Form} this
9882 * @param {Boolean} valid true if the form has passed client-side validation
9884 clientvalidation: true,
9886 * @event beforeaction
9887 * Fires before any action is performed. Return false to cancel the action.
9888 * @param {Form} this
9889 * @param {Action} action The action to be performed
9893 * @event actionfailed
9894 * Fires when an action fails.
9895 * @param {Form} this
9896 * @param {Action} action The action that failed
9898 actionfailed : true,
9900 * @event actioncomplete
9901 * Fires when an action is completed.
9902 * @param {Form} this
9903 * @param {Action} action The action that completed
9905 actioncomplete : true
9909 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9912 * @cfg {String} method
9913 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9918 * The URL to use for form actions if one isn't supplied in the action options.
9921 * @cfg {Boolean} fileUpload
9922 * Set to true if this form is a file upload.
9926 * @cfg {Object} baseParams
9927 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9931 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9935 * @cfg {Sting} align (left|right) for navbar forms
9940 activeAction : null,
9943 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9944 * element by passing it or its id or mask the form itself by passing in true.
9947 waitMsgTarget : false,
9952 * @cfg {Boolean} errorMask (true|false) default false
9957 * @cfg {Number} maskOffset Default 100
9962 * @cfg {Boolean} maskBody
9966 getAutoCreate : function(){
9970 method : this.method || 'POST',
9971 id : this.id || Roo.id(),
9974 if (this.parent().xtype.match(/^Nav/)) {
9975 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9979 if (this.labelAlign == 'left' ) {
9980 cfg.cls += ' form-horizontal';
9986 initEvents : function()
9988 this.el.on('submit', this.onSubmit, this);
9989 // this was added as random key presses on the form where triggering form submit.
9990 this.el.on('keypress', function(e) {
9991 if (e.getCharCode() != 13) {
9994 // we might need to allow it for textareas.. and some other items.
9995 // check e.getTarget().
9997 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10001 Roo.log("keypress blocked");
10003 e.preventDefault();
10009 onSubmit : function(e){
10014 * Returns true if client-side validation on the form is successful.
10017 isValid : function(){
10018 var items = this.getItems();
10020 var target = false;
10022 items.each(function(f){
10028 Roo.log('invalid field: ' + f.name);
10032 if(!target && f.el.isVisible(true)){
10038 if(this.errorMask && !valid){
10039 Roo.bootstrap.Form.popover.mask(this, target);
10046 * Returns true if any fields in this form have changed since their original load.
10049 isDirty : function(){
10051 var items = this.getItems();
10052 items.each(function(f){
10062 * Performs a predefined action (submit or load) or custom actions you define on this form.
10063 * @param {String} actionName The name of the action type
10064 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10065 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10066 * accept other config options):
10068 Property Type Description
10069 ---------------- --------------- ----------------------------------------------------------------------------------
10070 url String The url for the action (defaults to the form's url)
10071 method String The form method to use (defaults to the form's method, or POST if not defined)
10072 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10073 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10074 validate the form on the client (defaults to false)
10076 * @return {BasicForm} this
10078 doAction : function(action, options){
10079 if(typeof action == 'string'){
10080 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10082 if(this.fireEvent('beforeaction', this, action) !== false){
10083 this.beforeAction(action);
10084 action.run.defer(100, action);
10090 beforeAction : function(action){
10091 var o = action.options;
10096 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10098 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10101 // not really supported yet.. ??
10103 //if(this.waitMsgTarget === true){
10104 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10105 //}else if(this.waitMsgTarget){
10106 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10107 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10109 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10115 afterAction : function(action, success){
10116 this.activeAction = null;
10117 var o = action.options;
10122 Roo.get(document.body).unmask();
10128 //if(this.waitMsgTarget === true){
10129 // this.el.unmask();
10130 //}else if(this.waitMsgTarget){
10131 // this.waitMsgTarget.unmask();
10133 // Roo.MessageBox.updateProgress(1);
10134 // Roo.MessageBox.hide();
10141 Roo.callback(o.success, o.scope, [this, action]);
10142 this.fireEvent('actioncomplete', this, action);
10146 // failure condition..
10147 // we have a scenario where updates need confirming.
10148 // eg. if a locking scenario exists..
10149 // we look for { errors : { needs_confirm : true }} in the response.
10151 (typeof(action.result) != 'undefined') &&
10152 (typeof(action.result.errors) != 'undefined') &&
10153 (typeof(action.result.errors.needs_confirm) != 'undefined')
10156 Roo.log("not supported yet");
10159 Roo.MessageBox.confirm(
10160 "Change requires confirmation",
10161 action.result.errorMsg,
10166 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10176 Roo.callback(o.failure, o.scope, [this, action]);
10177 // show an error message if no failed handler is set..
10178 if (!this.hasListener('actionfailed')) {
10179 Roo.log("need to add dialog support");
10181 Roo.MessageBox.alert("Error",
10182 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10183 action.result.errorMsg :
10184 "Saving Failed, please check your entries or try again"
10189 this.fireEvent('actionfailed', this, action);
10194 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10195 * @param {String} id The value to search for
10198 findField : function(id){
10199 var items = this.getItems();
10200 var field = items.get(id);
10202 items.each(function(f){
10203 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10210 return field || null;
10213 * Mark fields in this form invalid in bulk.
10214 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10215 * @return {BasicForm} this
10217 markInvalid : function(errors){
10218 if(errors instanceof Array){
10219 for(var i = 0, len = errors.length; i < len; i++){
10220 var fieldError = errors[i];
10221 var f = this.findField(fieldError.id);
10223 f.markInvalid(fieldError.msg);
10229 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10230 field.markInvalid(errors[id]);
10234 //Roo.each(this.childForms || [], function (f) {
10235 // f.markInvalid(errors);
10242 * Set values for fields in this form in bulk.
10243 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10244 * @return {BasicForm} this
10246 setValues : function(values){
10247 if(values instanceof Array){ // array of objects
10248 for(var i = 0, len = values.length; i < len; i++){
10250 var f = this.findField(v.id);
10252 f.setValue(v.value);
10253 if(this.trackResetOnLoad){
10254 f.originalValue = f.getValue();
10258 }else{ // object hash
10261 if(typeof values[id] != 'function' && (field = this.findField(id))){
10263 if (field.setFromData &&
10264 field.valueField &&
10265 field.displayField &&
10266 // combos' with local stores can
10267 // be queried via setValue()
10268 // to set their value..
10269 (field.store && !field.store.isLocal)
10273 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10274 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10275 field.setFromData(sd);
10277 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10279 field.setFromData(values);
10282 field.setValue(values[id]);
10286 if(this.trackResetOnLoad){
10287 field.originalValue = field.getValue();
10293 //Roo.each(this.childForms || [], function (f) {
10294 // f.setValues(values);
10301 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10302 * they are returned as an array.
10303 * @param {Boolean} asString
10306 getValues : function(asString){
10307 //if (this.childForms) {
10308 // copy values from the child forms
10309 // Roo.each(this.childForms, function (f) {
10310 // this.setValues(f.getValues());
10316 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10317 if(asString === true){
10320 return Roo.urlDecode(fs);
10324 * Returns the fields in this form as an object with key/value pairs.
10325 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10328 getFieldValues : function(with_hidden)
10330 var items = this.getItems();
10332 items.each(function(f){
10334 if (!f.getName()) {
10338 var v = f.getValue();
10340 if (f.inputType =='radio') {
10341 if (typeof(ret[f.getName()]) == 'undefined') {
10342 ret[f.getName()] = ''; // empty..
10345 if (!f.el.dom.checked) {
10349 v = f.el.dom.value;
10353 if(f.xtype == 'MoneyField'){
10354 ret[f.currencyName] = f.getCurrency();
10357 // not sure if this supported any more..
10358 if ((typeof(v) == 'object') && f.getRawValue) {
10359 v = f.getRawValue() ; // dates..
10361 // combo boxes where name != hiddenName...
10362 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10363 ret[f.name] = f.getRawValue();
10365 ret[f.getName()] = v;
10372 * Clears all invalid messages in this form.
10373 * @return {BasicForm} this
10375 clearInvalid : function(){
10376 var items = this.getItems();
10378 items.each(function(f){
10386 * Resets this form.
10387 * @return {BasicForm} this
10389 reset : function(){
10390 var items = this.getItems();
10391 items.each(function(f){
10395 Roo.each(this.childForms || [], function (f) {
10403 getItems : function()
10405 var r=new Roo.util.MixedCollection(false, function(o){
10406 return o.id || (o.id = Roo.id());
10408 var iter = function(el) {
10415 Roo.each(el.items,function(e) {
10424 hideFields : function(items)
10426 Roo.each(items, function(i){
10428 var f = this.findField(i);
10439 showFields : function(items)
10441 Roo.each(items, function(i){
10443 var f = this.findField(i);
10456 Roo.apply(Roo.bootstrap.Form, {
10472 intervalID : false,
10478 if(this.isApplied){
10483 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10484 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10485 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10486 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10489 this.maskEl.top.enableDisplayMode("block");
10490 this.maskEl.left.enableDisplayMode("block");
10491 this.maskEl.bottom.enableDisplayMode("block");
10492 this.maskEl.right.enableDisplayMode("block");
10494 this.toolTip = new Roo.bootstrap.Tooltip({
10495 cls : 'roo-form-error-popover',
10497 'left' : ['r-l', [-2,0], 'right'],
10498 'right' : ['l-r', [2,0], 'left'],
10499 'bottom' : ['tl-bl', [0,2], 'top'],
10500 'top' : [ 'bl-tl', [0,-2], 'bottom']
10504 this.toolTip.render(Roo.get(document.body));
10506 this.toolTip.el.enableDisplayMode("block");
10508 Roo.get(document.body).on('click', function(){
10512 Roo.get(document.body).on('touchstart', function(){
10516 this.isApplied = true
10519 mask : function(form, target)
10523 this.target = target;
10525 if(!this.form.errorMask || !target.el){
10529 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10531 Roo.log(scrollable);
10533 var ot = this.target.el.calcOffsetsTo(scrollable);
10535 var scrollTo = ot[1] - this.form.maskOffset;
10537 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10539 scrollable.scrollTo('top', scrollTo);
10541 var box = this.target.el.getBox();
10543 var zIndex = Roo.bootstrap.Modal.zIndex++;
10546 this.maskEl.top.setStyle('position', 'absolute');
10547 this.maskEl.top.setStyle('z-index', zIndex);
10548 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10549 this.maskEl.top.setLeft(0);
10550 this.maskEl.top.setTop(0);
10551 this.maskEl.top.show();
10553 this.maskEl.left.setStyle('position', 'absolute');
10554 this.maskEl.left.setStyle('z-index', zIndex);
10555 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10556 this.maskEl.left.setLeft(0);
10557 this.maskEl.left.setTop(box.y - this.padding);
10558 this.maskEl.left.show();
10560 this.maskEl.bottom.setStyle('position', 'absolute');
10561 this.maskEl.bottom.setStyle('z-index', zIndex);
10562 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10563 this.maskEl.bottom.setLeft(0);
10564 this.maskEl.bottom.setTop(box.bottom + this.padding);
10565 this.maskEl.bottom.show();
10567 this.maskEl.right.setStyle('position', 'absolute');
10568 this.maskEl.right.setStyle('z-index', zIndex);
10569 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10570 this.maskEl.right.setLeft(box.right + this.padding);
10571 this.maskEl.right.setTop(box.y - this.padding);
10572 this.maskEl.right.show();
10574 this.toolTip.bindEl = this.target.el;
10576 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10578 var tip = this.target.blankText;
10580 if(this.target.getValue() !== '' ) {
10582 if (this.target.invalidText.length) {
10583 tip = this.target.invalidText;
10584 } else if (this.target.regexText.length){
10585 tip = this.target.regexText;
10589 this.toolTip.show(tip);
10591 this.intervalID = window.setInterval(function() {
10592 Roo.bootstrap.Form.popover.unmask();
10595 window.onwheel = function(){ return false;};
10597 (function(){ this.isMasked = true; }).defer(500, this);
10601 unmask : function()
10603 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10607 this.maskEl.top.setStyle('position', 'absolute');
10608 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10609 this.maskEl.top.hide();
10611 this.maskEl.left.setStyle('position', 'absolute');
10612 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10613 this.maskEl.left.hide();
10615 this.maskEl.bottom.setStyle('position', 'absolute');
10616 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10617 this.maskEl.bottom.hide();
10619 this.maskEl.right.setStyle('position', 'absolute');
10620 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10621 this.maskEl.right.hide();
10623 this.toolTip.hide();
10625 this.toolTip.el.hide();
10627 window.onwheel = function(){ return true;};
10629 if(this.intervalID){
10630 window.clearInterval(this.intervalID);
10631 this.intervalID = false;
10634 this.isMasked = false;
10644 * Ext JS Library 1.1.1
10645 * Copyright(c) 2006-2007, Ext JS, LLC.
10647 * Originally Released Under LGPL - original licence link has changed is not relivant.
10650 * <script type="text/javascript">
10653 * @class Roo.form.VTypes
10654 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10657 Roo.form.VTypes = function(){
10658 // closure these in so they are only created once.
10659 var alpha = /^[a-zA-Z_]+$/;
10660 var alphanum = /^[a-zA-Z0-9_]+$/;
10661 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10662 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10664 // All these messages and functions are configurable
10667 * The function used to validate email addresses
10668 * @param {String} value The email address
10670 'email' : function(v){
10671 return email.test(v);
10674 * The error text to display when the email validation function returns false
10677 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10679 * The keystroke filter mask to be applied on email input
10682 'emailMask' : /[a-z0-9_\.\-@]/i,
10685 * The function used to validate URLs
10686 * @param {String} value The URL
10688 'url' : function(v){
10689 return url.test(v);
10692 * The error text to display when the url validation function returns false
10695 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10698 * The function used to validate alpha values
10699 * @param {String} value The value
10701 'alpha' : function(v){
10702 return alpha.test(v);
10705 * The error text to display when the alpha validation function returns false
10708 'alphaText' : 'This field should only contain letters and _',
10710 * The keystroke filter mask to be applied on alpha input
10713 'alphaMask' : /[a-z_]/i,
10716 * The function used to validate alphanumeric values
10717 * @param {String} value The value
10719 'alphanum' : function(v){
10720 return alphanum.test(v);
10723 * The error text to display when the alphanumeric validation function returns false
10726 'alphanumText' : 'This field should only contain letters, numbers and _',
10728 * The keystroke filter mask to be applied on alphanumeric input
10731 'alphanumMask' : /[a-z0-9_]/i
10741 * @class Roo.bootstrap.Input
10742 * @extends Roo.bootstrap.Component
10743 * Bootstrap Input class
10744 * @cfg {Boolean} disabled is it disabled
10745 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10746 * @cfg {String} name name of the input
10747 * @cfg {string} fieldLabel - the label associated
10748 * @cfg {string} placeholder - placeholder to put in text.
10749 * @cfg {string} before - input group add on before
10750 * @cfg {string} after - input group add on after
10751 * @cfg {string} size - (lg|sm) or leave empty..
10752 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10753 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10754 * @cfg {Number} md colspan out of 12 for computer-sized screens
10755 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10756 * @cfg {string} value default value of the input
10757 * @cfg {Number} labelWidth set the width of label
10758 * @cfg {Number} labellg set the width of label (1-12)
10759 * @cfg {Number} labelmd set the width of label (1-12)
10760 * @cfg {Number} labelsm set the width of label (1-12)
10761 * @cfg {Number} labelxs set the width of label (1-12)
10762 * @cfg {String} labelAlign (top|left)
10763 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10764 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10765 * @cfg {String} indicatorpos (left|right) default left
10766 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10767 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10768 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10770 * @cfg {String} align (left|center|right) Default left
10771 * @cfg {Boolean} forceFeedback (true|false) Default false
10774 * Create a new Input
10775 * @param {Object} config The config object
10778 Roo.bootstrap.Input = function(config){
10780 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10785 * Fires when this field receives input focus.
10786 * @param {Roo.form.Field} this
10791 * Fires when this field loses input focus.
10792 * @param {Roo.form.Field} this
10796 * @event specialkey
10797 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10798 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10799 * @param {Roo.form.Field} this
10800 * @param {Roo.EventObject} e The event object
10805 * Fires just before the field blurs if the field value has changed.
10806 * @param {Roo.form.Field} this
10807 * @param {Mixed} newValue The new value
10808 * @param {Mixed} oldValue The original value
10813 * Fires after the field has been marked as invalid.
10814 * @param {Roo.form.Field} this
10815 * @param {String} msg The validation message
10820 * Fires after the field has been validated with no errors.
10821 * @param {Roo.form.Field} this
10826 * Fires after the key up
10827 * @param {Roo.form.Field} this
10828 * @param {Roo.EventObject} e The event Object
10833 * Fires after the user pastes into input
10834 * @param {Roo.form.Field} this
10835 * @param {Roo.EventObject} e The event Object
10841 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10843 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10844 automatic validation (defaults to "keyup").
10846 validationEvent : "keyup",
10848 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10850 validateOnBlur : true,
10852 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10854 validationDelay : 250,
10856 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10858 focusClass : "x-form-focus", // not needed???
10862 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10864 invalidClass : "has-warning",
10867 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10869 validClass : "has-success",
10872 * @cfg {Boolean} hasFeedback (true|false) default true
10874 hasFeedback : true,
10877 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10879 invalidFeedbackClass : "glyphicon-warning-sign",
10882 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10884 validFeedbackClass : "glyphicon-ok",
10887 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10889 selectOnFocus : false,
10892 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10896 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10901 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10903 disableKeyFilter : false,
10906 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10910 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10914 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10916 blankText : "Please complete this mandatory field",
10919 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10923 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10925 maxLength : Number.MAX_VALUE,
10927 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10929 minLengthText : "The minimum length for this field is {0}",
10931 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10933 maxLengthText : "The maximum length for this field is {0}",
10937 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10938 * If available, this function will be called only after the basic validators all return true, and will be passed the
10939 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10943 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10944 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10945 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10949 * @cfg {String} regexText -- Depricated - use Invalid Text
10954 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10960 autocomplete: false,
10964 inputType : 'text',
10967 placeholder: false,
10972 preventMark: false,
10973 isFormField : true,
10976 labelAlign : false,
10979 formatedValue : false,
10980 forceFeedback : false,
10982 indicatorpos : 'left',
10992 parentLabelAlign : function()
10995 while (parent.parent()) {
10996 parent = parent.parent();
10997 if (typeof(parent.labelAlign) !='undefined') {
10998 return parent.labelAlign;
11005 getAutoCreate : function()
11007 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11013 if(this.inputType != 'hidden'){
11014 cfg.cls = 'form-group' //input-group
11020 type : this.inputType,
11021 value : this.value,
11022 cls : 'form-control',
11023 placeholder : this.placeholder || '',
11024 autocomplete : this.autocomplete || 'new-password'
11026 if (this.inputType == 'file') {
11027 input.style = 'overflow:hidden'; // why not in CSS?
11030 if(this.capture.length){
11031 input.capture = this.capture;
11034 if(this.accept.length){
11035 input.accept = this.accept + "/*";
11039 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11042 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11043 input.maxLength = this.maxLength;
11046 if (this.disabled) {
11047 input.disabled=true;
11050 if (this.readOnly) {
11051 input.readonly=true;
11055 input.name = this.name;
11059 input.cls += ' input-' + this.size;
11063 ['xs','sm','md','lg'].map(function(size){
11064 if (settings[size]) {
11065 cfg.cls += ' col-' + size + '-' + settings[size];
11069 var inputblock = input;
11073 cls: 'glyphicon form-control-feedback'
11076 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11079 cls : 'has-feedback',
11087 if (this.before || this.after) {
11090 cls : 'input-group',
11094 if (this.before && typeof(this.before) == 'string') {
11096 inputblock.cn.push({
11098 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11102 if (this.before && typeof(this.before) == 'object') {
11103 this.before = Roo.factory(this.before);
11105 inputblock.cn.push({
11107 cls : 'roo-input-before input-group-prepend input-group-' +
11108 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11112 inputblock.cn.push(input);
11114 if (this.after && typeof(this.after) == 'string') {
11115 inputblock.cn.push({
11117 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11121 if (this.after && typeof(this.after) == 'object') {
11122 this.after = Roo.factory(this.after);
11124 inputblock.cn.push({
11126 cls : 'roo-input-after input-group-append input-group-' +
11127 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11131 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11132 inputblock.cls += ' has-feedback';
11133 inputblock.cn.push(feedback);
11138 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11139 tooltip : 'This field is required'
11141 if (this.allowBlank ) {
11142 indicator.style = this.allowBlank ? ' display:none' : '';
11144 if (align ==='left' && this.fieldLabel.length) {
11146 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11153 cls : 'control-label col-form-label',
11154 html : this.fieldLabel
11165 var labelCfg = cfg.cn[1];
11166 var contentCfg = cfg.cn[2];
11168 if(this.indicatorpos == 'right'){
11173 cls : 'control-label col-form-label',
11177 html : this.fieldLabel
11191 labelCfg = cfg.cn[0];
11192 contentCfg = cfg.cn[1];
11196 if(this.labelWidth > 12){
11197 labelCfg.style = "width: " + this.labelWidth + 'px';
11200 if(this.labelWidth < 13 && this.labelmd == 0){
11201 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11204 if(this.labellg > 0){
11205 labelCfg.cls += ' col-lg-' + this.labellg;
11206 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11209 if(this.labelmd > 0){
11210 labelCfg.cls += ' col-md-' + this.labelmd;
11211 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11214 if(this.labelsm > 0){
11215 labelCfg.cls += ' col-sm-' + this.labelsm;
11216 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11219 if(this.labelxs > 0){
11220 labelCfg.cls += ' col-xs-' + this.labelxs;
11221 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11225 } else if ( this.fieldLabel.length) {
11232 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11233 tooltip : 'This field is required',
11234 style : this.allowBlank ? ' display:none' : ''
11238 //cls : 'input-group-addon',
11239 html : this.fieldLabel
11247 if(this.indicatorpos == 'right'){
11252 //cls : 'input-group-addon',
11253 html : this.fieldLabel
11258 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11259 tooltip : 'This field is required',
11260 style : this.allowBlank ? ' display:none' : ''
11280 if (this.parentType === 'Navbar' && this.parent().bar) {
11281 cfg.cls += ' navbar-form';
11284 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11285 // on BS4 we do this only if not form
11286 cfg.cls += ' navbar-form';
11294 * return the real input element.
11296 inputEl: function ()
11298 return this.el.select('input.form-control',true).first();
11301 tooltipEl : function()
11303 return this.inputEl();
11306 indicatorEl : function()
11308 if (Roo.bootstrap.version == 4) {
11309 return false; // not enabled in v4 yet.
11312 var indicator = this.el.select('i.roo-required-indicator',true).first();
11322 setDisabled : function(v)
11324 var i = this.inputEl().dom;
11326 i.removeAttribute('disabled');
11330 i.setAttribute('disabled','true');
11332 initEvents : function()
11335 this.inputEl().on("keydown" , this.fireKey, this);
11336 this.inputEl().on("focus", this.onFocus, this);
11337 this.inputEl().on("blur", this.onBlur, this);
11339 this.inputEl().relayEvent('keyup', this);
11340 this.inputEl().relayEvent('paste', this);
11342 this.indicator = this.indicatorEl();
11344 if(this.indicator){
11345 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11348 // reference to original value for reset
11349 this.originalValue = this.getValue();
11350 //Roo.form.TextField.superclass.initEvents.call(this);
11351 if(this.validationEvent == 'keyup'){
11352 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11353 this.inputEl().on('keyup', this.filterValidation, this);
11355 else if(this.validationEvent !== false){
11356 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11359 if(this.selectOnFocus){
11360 this.on("focus", this.preFocus, this);
11363 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11364 this.inputEl().on("keypress", this.filterKeys, this);
11366 this.inputEl().relayEvent('keypress', this);
11369 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11370 this.el.on("click", this.autoSize, this);
11373 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11374 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11377 if (typeof(this.before) == 'object') {
11378 this.before.render(this.el.select('.roo-input-before',true).first());
11380 if (typeof(this.after) == 'object') {
11381 this.after.render(this.el.select('.roo-input-after',true).first());
11384 this.inputEl().on('change', this.onChange, this);
11387 filterValidation : function(e){
11388 if(!e.isNavKeyPress()){
11389 this.validationTask.delay(this.validationDelay);
11393 * Validates the field value
11394 * @return {Boolean} True if the value is valid, else false
11396 validate : function(){
11397 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11398 if(this.disabled || this.validateValue(this.getRawValue())){
11403 this.markInvalid();
11409 * Validates a value according to the field's validation rules and marks the field as invalid
11410 * if the validation fails
11411 * @param {Mixed} value The value to validate
11412 * @return {Boolean} True if the value is valid, else false
11414 validateValue : function(value)
11416 if(this.getVisibilityEl().hasClass('hidden')){
11420 if(value.length < 1) { // if it's blank
11421 if(this.allowBlank){
11427 if(value.length < this.minLength){
11430 if(value.length > this.maxLength){
11434 var vt = Roo.form.VTypes;
11435 if(!vt[this.vtype](value, this)){
11439 if(typeof this.validator == "function"){
11440 var msg = this.validator(value);
11444 if (typeof(msg) == 'string') {
11445 this.invalidText = msg;
11449 if(this.regex && !this.regex.test(value)){
11457 fireKey : function(e){
11458 //Roo.log('field ' + e.getKey());
11459 if(e.isNavKeyPress()){
11460 this.fireEvent("specialkey", this, e);
11463 focus : function (selectText){
11465 this.inputEl().focus();
11466 if(selectText === true){
11467 this.inputEl().dom.select();
11473 onFocus : function(){
11474 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11475 // this.el.addClass(this.focusClass);
11477 if(!this.hasFocus){
11478 this.hasFocus = true;
11479 this.startValue = this.getValue();
11480 this.fireEvent("focus", this);
11484 beforeBlur : Roo.emptyFn,
11488 onBlur : function(){
11490 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11491 //this.el.removeClass(this.focusClass);
11493 this.hasFocus = false;
11494 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11497 var v = this.getValue();
11498 if(String(v) !== String(this.startValue)){
11499 this.fireEvent('change', this, v, this.startValue);
11501 this.fireEvent("blur", this);
11504 onChange : function(e)
11506 var v = this.getValue();
11507 if(String(v) !== String(this.startValue)){
11508 this.fireEvent('change', this, v, this.startValue);
11514 * Resets the current field value to the originally loaded value and clears any validation messages
11516 reset : function(){
11517 this.setValue(this.originalValue);
11521 * Returns the name of the field
11522 * @return {Mixed} name The name field
11524 getName: function(){
11528 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11529 * @return {Mixed} value The field value
11531 getValue : function(){
11533 var v = this.inputEl().getValue();
11538 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11539 * @return {Mixed} value The field value
11541 getRawValue : function(){
11542 var v = this.inputEl().getValue();
11548 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11549 * @param {Mixed} value The value to set
11551 setRawValue : function(v){
11552 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11555 selectText : function(start, end){
11556 var v = this.getRawValue();
11558 start = start === undefined ? 0 : start;
11559 end = end === undefined ? v.length : end;
11560 var d = this.inputEl().dom;
11561 if(d.setSelectionRange){
11562 d.setSelectionRange(start, end);
11563 }else if(d.createTextRange){
11564 var range = d.createTextRange();
11565 range.moveStart("character", start);
11566 range.moveEnd("character", v.length-end);
11573 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11574 * @param {Mixed} value The value to set
11576 setValue : function(v){
11579 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11585 processValue : function(value){
11586 if(this.stripCharsRe){
11587 var newValue = value.replace(this.stripCharsRe, '');
11588 if(newValue !== value){
11589 this.setRawValue(newValue);
11596 preFocus : function(){
11598 if(this.selectOnFocus){
11599 this.inputEl().dom.select();
11602 filterKeys : function(e){
11603 var k = e.getKey();
11604 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11607 var c = e.getCharCode(), cc = String.fromCharCode(c);
11608 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11611 if(!this.maskRe.test(cc)){
11616 * Clear any invalid styles/messages for this field
11618 clearInvalid : function(){
11620 if(!this.el || this.preventMark){ // not rendered
11625 this.el.removeClass([this.invalidClass, 'is-invalid']);
11627 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11637 if(this.indicator){
11638 this.indicator.removeClass('visible');
11639 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11642 this.fireEvent('valid', this);
11646 * Mark this field as valid
11648 markValid : function()
11650 if(!this.el || this.preventMark){ // not rendered...
11654 this.el.removeClass([this.invalidClass, this.validClass]);
11655 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11657 var feedback = this.el.select('.form-control-feedback', true).first();
11660 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11663 if(this.indicator){
11664 this.indicator.removeClass('visible');
11665 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11673 if(this.allowBlank && !this.getRawValue().length){
11676 if (Roo.bootstrap.version == 3) {
11677 this.el.addClass(this.validClass);
11679 this.inputEl().addClass('is-valid');
11682 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11684 var feedback = this.el.select('.form-control-feedback', true).first();
11687 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11688 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11693 this.fireEvent('valid', this);
11697 * Mark this field as invalid
11698 * @param {String} msg The validation message
11700 markInvalid : function(msg)
11702 if(!this.el || this.preventMark){ // not rendered
11706 this.el.removeClass([this.invalidClass, this.validClass]);
11707 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11709 var feedback = this.el.select('.form-control-feedback', true).first();
11712 this.el.select('.form-control-feedback', true).first().removeClass(
11713 [this.invalidFeedbackClass, this.validFeedbackClass]);
11720 if(this.allowBlank && !this.getRawValue().length){
11724 if(this.indicator){
11725 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11726 this.indicator.addClass('visible');
11728 if (Roo.bootstrap.version == 3) {
11729 this.el.addClass(this.invalidClass);
11731 this.inputEl().addClass('is-invalid');
11736 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738 var feedback = this.el.select('.form-control-feedback', true).first();
11741 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11743 if(this.getValue().length || this.forceFeedback){
11744 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11751 this.fireEvent('invalid', this, msg);
11754 SafariOnKeyDown : function(event)
11756 // this is a workaround for a password hang bug on chrome/ webkit.
11757 if (this.inputEl().dom.type != 'password') {
11761 var isSelectAll = false;
11763 if(this.inputEl().dom.selectionEnd > 0){
11764 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11766 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11767 event.preventDefault();
11772 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11774 event.preventDefault();
11775 // this is very hacky as keydown always get's upper case.
11777 var cc = String.fromCharCode(event.getCharCode());
11778 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11782 adjustWidth : function(tag, w){
11783 tag = tag.toLowerCase();
11784 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11785 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11786 if(tag == 'input'){
11789 if(tag == 'textarea'){
11792 }else if(Roo.isOpera){
11793 if(tag == 'input'){
11796 if(tag == 'textarea'){
11804 setFieldLabel : function(v)
11806 if(!this.rendered){
11810 if(this.indicatorEl()){
11811 var ar = this.el.select('label > span',true);
11813 if (ar.elements.length) {
11814 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11815 this.fieldLabel = v;
11819 var br = this.el.select('label',true);
11821 if(br.elements.length) {
11822 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11823 this.fieldLabel = v;
11827 Roo.log('Cannot Found any of label > span || label in input');
11831 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11832 this.fieldLabel = v;
11847 * @class Roo.bootstrap.TextArea
11848 * @extends Roo.bootstrap.Input
11849 * Bootstrap TextArea class
11850 * @cfg {Number} cols Specifies the visible width of a text area
11851 * @cfg {Number} rows Specifies the visible number of lines in a text area
11852 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11853 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11854 * @cfg {string} html text
11857 * Create a new TextArea
11858 * @param {Object} config The config object
11861 Roo.bootstrap.TextArea = function(config){
11862 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11866 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11876 getAutoCreate : function(){
11878 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11884 if(this.inputType != 'hidden'){
11885 cfg.cls = 'form-group' //input-group
11893 value : this.value || '',
11894 html: this.html || '',
11895 cls : 'form-control',
11896 placeholder : this.placeholder || ''
11900 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11901 input.maxLength = this.maxLength;
11905 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11909 input.cols = this.cols;
11912 if (this.readOnly) {
11913 input.readonly = true;
11917 input.name = this.name;
11921 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11925 ['xs','sm','md','lg'].map(function(size){
11926 if (settings[size]) {
11927 cfg.cls += ' col-' + size + '-' + settings[size];
11931 var inputblock = input;
11933 if(this.hasFeedback && !this.allowBlank){
11937 cls: 'glyphicon form-control-feedback'
11941 cls : 'has-feedback',
11950 if (this.before || this.after) {
11953 cls : 'input-group',
11957 inputblock.cn.push({
11959 cls : 'input-group-addon',
11964 inputblock.cn.push(input);
11966 if(this.hasFeedback && !this.allowBlank){
11967 inputblock.cls += ' has-feedback';
11968 inputblock.cn.push(feedback);
11972 inputblock.cn.push({
11974 cls : 'input-group-addon',
11981 if (align ==='left' && this.fieldLabel.length) {
11986 cls : 'control-label',
11987 html : this.fieldLabel
11998 if(this.labelWidth > 12){
11999 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12002 if(this.labelWidth < 13 && this.labelmd == 0){
12003 this.labelmd = this.labelWidth;
12006 if(this.labellg > 0){
12007 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12008 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12011 if(this.labelmd > 0){
12012 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12013 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12016 if(this.labelsm > 0){
12017 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12018 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12021 if(this.labelxs > 0){
12022 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12023 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12026 } else if ( this.fieldLabel.length) {
12031 //cls : 'input-group-addon',
12032 html : this.fieldLabel
12050 if (this.disabled) {
12051 input.disabled=true;
12058 * return the real textarea element.
12060 inputEl: function ()
12062 return this.el.select('textarea.form-control',true).first();
12066 * Clear any invalid styles/messages for this field
12068 clearInvalid : function()
12071 if(!this.el || this.preventMark){ // not rendered
12075 var label = this.el.select('label', true).first();
12076 var icon = this.el.select('i.fa-star', true).first();
12081 this.el.removeClass( this.validClass);
12082 this.inputEl().removeClass('is-invalid');
12084 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12086 var feedback = this.el.select('.form-control-feedback', true).first();
12089 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12094 this.fireEvent('valid', this);
12098 * Mark this field as valid
12100 markValid : function()
12102 if(!this.el || this.preventMark){ // not rendered
12106 this.el.removeClass([this.invalidClass, this.validClass]);
12107 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12109 var feedback = this.el.select('.form-control-feedback', true).first();
12112 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12115 if(this.disabled || this.allowBlank){
12119 var label = this.el.select('label', true).first();
12120 var icon = this.el.select('i.fa-star', true).first();
12125 if (Roo.bootstrap.version == 3) {
12126 this.el.addClass(this.validClass);
12128 this.inputEl().addClass('is-valid');
12132 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12134 var feedback = this.el.select('.form-control-feedback', true).first();
12137 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12138 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12143 this.fireEvent('valid', this);
12147 * Mark this field as invalid
12148 * @param {String} msg The validation message
12150 markInvalid : function(msg)
12152 if(!this.el || this.preventMark){ // not rendered
12156 this.el.removeClass([this.invalidClass, this.validClass]);
12157 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12159 var feedback = this.el.select('.form-control-feedback', true).first();
12162 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12165 if(this.disabled || this.allowBlank){
12169 var label = this.el.select('label', true).first();
12170 var icon = this.el.select('i.fa-star', true).first();
12172 if(!this.getValue().length && label && !icon){
12173 this.el.createChild({
12175 cls : 'text-danger fa fa-lg fa-star',
12176 tooltip : 'This field is required',
12177 style : 'margin-right:5px;'
12181 if (Roo.bootstrap.version == 3) {
12182 this.el.addClass(this.invalidClass);
12184 this.inputEl().addClass('is-invalid');
12187 // fixme ... this may be depricated need to test..
12188 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12190 var feedback = this.el.select('.form-control-feedback', true).first();
12193 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12195 if(this.getValue().length || this.forceFeedback){
12196 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12203 this.fireEvent('invalid', this, msg);
12211 * trigger field - base class for combo..
12216 * @class Roo.bootstrap.TriggerField
12217 * @extends Roo.bootstrap.Input
12218 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12219 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12220 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12221 * for which you can provide a custom implementation. For example:
12223 var trigger = new Roo.bootstrap.TriggerField();
12224 trigger.onTriggerClick = myTriggerFn;
12225 trigger.applyTo('my-field');
12228 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12229 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12230 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12231 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12232 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12235 * Create a new TriggerField.
12236 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12237 * to the base TextField)
12239 Roo.bootstrap.TriggerField = function(config){
12240 this.mimicing = false;
12241 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12244 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12246 * @cfg {String} triggerClass A CSS class to apply to the trigger
12249 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12254 * @cfg {Boolean} removable (true|false) special filter default false
12258 /** @cfg {Boolean} grow @hide */
12259 /** @cfg {Number} growMin @hide */
12260 /** @cfg {Number} growMax @hide */
12266 autoSize: Roo.emptyFn,
12270 deferHeight : true,
12273 actionMode : 'wrap',
12278 getAutoCreate : function(){
12280 var align = this.labelAlign || this.parentLabelAlign();
12285 cls: 'form-group' //input-group
12292 type : this.inputType,
12293 cls : 'form-control',
12294 autocomplete: 'new-password',
12295 placeholder : this.placeholder || ''
12299 input.name = this.name;
12302 input.cls += ' input-' + this.size;
12305 if (this.disabled) {
12306 input.disabled=true;
12309 var inputblock = input;
12311 if(this.hasFeedback && !this.allowBlank){
12315 cls: 'glyphicon form-control-feedback'
12318 if(this.removable && !this.editable ){
12320 cls : 'has-feedback',
12326 cls : 'roo-combo-removable-btn close'
12333 cls : 'has-feedback',
12342 if(this.removable && !this.editable ){
12344 cls : 'roo-removable',
12350 cls : 'roo-combo-removable-btn close'
12357 if (this.before || this.after) {
12360 cls : 'input-group',
12364 inputblock.cn.push({
12366 cls : 'input-group-addon input-group-prepend input-group-text',
12371 inputblock.cn.push(input);
12373 if(this.hasFeedback && !this.allowBlank){
12374 inputblock.cls += ' has-feedback';
12375 inputblock.cn.push(feedback);
12379 inputblock.cn.push({
12381 cls : 'input-group-addon input-group-append input-group-text',
12390 var ibwrap = inputblock;
12395 cls: 'roo-select2-choices',
12399 cls: 'roo-select2-search-field',
12411 cls: 'roo-select2-container input-group',
12416 cls: 'form-hidden-field'
12422 if(!this.multiple && this.showToggleBtn){
12428 if (this.caret != false) {
12431 cls: 'fa fa-' + this.caret
12438 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12440 Roo.bootstrap.version == 3 ? caret : '',
12443 cls: 'combobox-clear',
12457 combobox.cls += ' roo-select2-container-multi';
12461 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12462 tooltip : 'This field is required'
12464 if (Roo.bootstrap.version == 4) {
12467 style : 'display:none'
12472 if (align ==='left' && this.fieldLabel.length) {
12474 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12481 cls : 'control-label',
12482 html : this.fieldLabel
12494 var labelCfg = cfg.cn[1];
12495 var contentCfg = cfg.cn[2];
12497 if(this.indicatorpos == 'right'){
12502 cls : 'control-label',
12506 html : this.fieldLabel
12520 labelCfg = cfg.cn[0];
12521 contentCfg = cfg.cn[1];
12524 if(this.labelWidth > 12){
12525 labelCfg.style = "width: " + this.labelWidth + 'px';
12528 if(this.labelWidth < 13 && this.labelmd == 0){
12529 this.labelmd = this.labelWidth;
12532 if(this.labellg > 0){
12533 labelCfg.cls += ' col-lg-' + this.labellg;
12534 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12537 if(this.labelmd > 0){
12538 labelCfg.cls += ' col-md-' + this.labelmd;
12539 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12542 if(this.labelsm > 0){
12543 labelCfg.cls += ' col-sm-' + this.labelsm;
12544 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12547 if(this.labelxs > 0){
12548 labelCfg.cls += ' col-xs-' + this.labelxs;
12549 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12552 } else if ( this.fieldLabel.length) {
12553 // Roo.log(" label");
12558 //cls : 'input-group-addon',
12559 html : this.fieldLabel
12567 if(this.indicatorpos == 'right'){
12575 html : this.fieldLabel
12589 // Roo.log(" no label && no align");
12596 ['xs','sm','md','lg'].map(function(size){
12597 if (settings[size]) {
12598 cfg.cls += ' col-' + size + '-' + settings[size];
12609 onResize : function(w, h){
12610 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12611 // if(typeof w == 'number'){
12612 // var x = w - this.trigger.getWidth();
12613 // this.inputEl().setWidth(this.adjustWidth('input', x));
12614 // this.trigger.setStyle('left', x+'px');
12619 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12622 getResizeEl : function(){
12623 return this.inputEl();
12627 getPositionEl : function(){
12628 return this.inputEl();
12632 alignErrorIcon : function(){
12633 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12637 initEvents : function(){
12641 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12642 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12643 if(!this.multiple && this.showToggleBtn){
12644 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12645 if(this.hideTrigger){
12646 this.trigger.setDisplayed(false);
12648 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12652 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12655 if(this.removable && !this.editable && !this.tickable){
12656 var close = this.closeTriggerEl();
12659 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12660 close.on('click', this.removeBtnClick, this, close);
12664 //this.trigger.addClassOnOver('x-form-trigger-over');
12665 //this.trigger.addClassOnClick('x-form-trigger-click');
12668 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12672 closeTriggerEl : function()
12674 var close = this.el.select('.roo-combo-removable-btn', true).first();
12675 return close ? close : false;
12678 removeBtnClick : function(e, h, el)
12680 e.preventDefault();
12682 if(this.fireEvent("remove", this) !== false){
12684 this.fireEvent("afterremove", this)
12688 createList : function()
12690 this.list = Roo.get(document.body).createChild({
12691 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12692 cls: 'typeahead typeahead-long dropdown-menu shadow',
12693 style: 'display:none'
12696 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12701 initTrigger : function(){
12706 onDestroy : function(){
12708 this.trigger.removeAllListeners();
12709 // this.trigger.remove();
12712 // this.wrap.remove();
12714 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12718 onFocus : function(){
12719 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12721 if(!this.mimicing){
12722 this.wrap.addClass('x-trigger-wrap-focus');
12723 this.mimicing = true;
12724 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12725 if(this.monitorTab){
12726 this.el.on("keydown", this.checkTab, this);
12733 checkTab : function(e){
12734 if(e.getKey() == e.TAB){
12735 this.triggerBlur();
12740 onBlur : function(){
12745 mimicBlur : function(e, t){
12747 if(!this.wrap.contains(t) && this.validateBlur()){
12748 this.triggerBlur();
12754 triggerBlur : function(){
12755 this.mimicing = false;
12756 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12757 if(this.monitorTab){
12758 this.el.un("keydown", this.checkTab, this);
12760 //this.wrap.removeClass('x-trigger-wrap-focus');
12761 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12765 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12766 validateBlur : function(e, t){
12771 onDisable : function(){
12772 this.inputEl().dom.disabled = true;
12773 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12775 // this.wrap.addClass('x-item-disabled');
12780 onEnable : function(){
12781 this.inputEl().dom.disabled = false;
12782 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12784 // this.el.removeClass('x-item-disabled');
12789 onShow : function(){
12790 var ae = this.getActionEl();
12793 ae.dom.style.display = '';
12794 ae.dom.style.visibility = 'visible';
12800 onHide : function(){
12801 var ae = this.getActionEl();
12802 ae.dom.style.display = 'none';
12806 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12807 * by an implementing function.
12809 * @param {EventObject} e
12811 onTriggerClick : Roo.emptyFn
12819 * @class Roo.bootstrap.CardUploader
12820 * @extends Roo.bootstrap.Button
12821 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12822 * @cfg {Number} errorTimeout default 3000
12823 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12824 * @cfg {Array} html The button text.
12828 * Create a new CardUploader
12829 * @param {Object} config The config object
12832 Roo.bootstrap.CardUploader = function(config){
12836 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12839 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12847 * When a image is clicked on - and needs to display a slideshow or similar..
12848 * @param {Roo.bootstrap.Card} this
12849 * @param {Object} The image information data
12855 * When a the download link is clicked
12856 * @param {Roo.bootstrap.Card} this
12857 * @param {Object} The image information data contains
12864 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12867 errorTimeout : 3000,
12871 fileCollection : false,
12874 getAutoCreate : function()
12878 cls :'form-group' ,
12883 //cls : 'input-group-addon',
12884 html : this.fieldLabel
12892 value : this.value,
12893 cls : 'd-none form-control'
12898 multiple : 'multiple',
12900 cls : 'd-none roo-card-upload-selector'
12904 cls : 'roo-card-uploader-button-container w-100 mb-2'
12907 cls : 'card-columns roo-card-uploader-container'
12917 getChildContainer : function() /// what children are added to.
12919 return this.containerEl;
12922 getButtonContainer : function() /// what children are added to.
12924 return this.el.select(".roo-card-uploader-button-container").first();
12927 initEvents : function()
12930 Roo.bootstrap.Input.prototype.initEvents.call(this);
12934 xns: Roo.bootstrap,
12937 container_method : 'getButtonContainer' ,
12938 html : this.html, // fix changable?
12941 'click' : function(btn, e) {
12950 this.urlAPI = (window.createObjectURL && window) ||
12951 (window.URL && URL.revokeObjectURL && URL) ||
12952 (window.webkitURL && webkitURL);
12957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12959 this.selectorEl.on('change', this.onFileSelected, this);
12962 this.images.forEach(function(img) {
12965 this.images = false;
12967 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12973 onClick : function(e)
12975 e.preventDefault();
12977 this.selectorEl.dom.click();
12981 onFileSelected : function(e)
12983 e.preventDefault();
12985 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12989 Roo.each(this.selectorEl.dom.files, function(file){
12990 this.addFile(file);
12999 addFile : function(file)
13002 if(typeof(file) === 'string'){
13003 throw "Add file by name?"; // should not happen
13007 if(!file || !this.urlAPI){
13017 var url = _this.urlAPI.createObjectURL( file);
13020 id : Roo.bootstrap.CardUploader.ID--,
13021 is_uploaded : false,
13025 mimetype : file.type,
13033 * addCard - add an Attachment to the uploader
13034 * @param data - the data about the image to upload
13038 title : "Title of file",
13039 is_uploaded : false,
13040 src : "http://.....",
13041 srcfile : { the File upload object },
13042 mimetype : file.type,
13045 .. any other data...
13051 addCard : function (data)
13053 // hidden input element?
13054 // if the file is not an image...
13055 //then we need to use something other that and header_image
13060 xns : Roo.bootstrap,
13061 xtype : 'CardFooter',
13064 xns : Roo.bootstrap,
13070 xns : Roo.bootstrap,
13072 html : String.format("<small>{0}</small>", data.title),
13073 cls : 'col-10 text-left',
13078 click : function() {
13080 t.fireEvent( "download", t, data );
13086 xns : Roo.bootstrap,
13088 style: 'max-height: 28px; ',
13094 click : function() {
13095 t.removeCard(data.id)
13107 var cn = this.addxtype(
13110 xns : Roo.bootstrap,
13113 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13114 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13115 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13120 initEvents : function() {
13121 Roo.bootstrap.Card.prototype.initEvents.call(this);
13123 this.imgEl = this.el.select('.card-img-top').first();
13125 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13126 this.imgEl.set({ 'pointer' : 'cursor' });
13129 this.getCardFooter().addClass('p-1');
13136 // dont' really need ot update items.
13137 // this.items.push(cn);
13138 this.fileCollection.add(cn);
13140 if (!data.srcfile) {
13141 this.updateInput();
13146 var reader = new FileReader();
13147 reader.addEventListener("load", function() {
13148 data.srcdata = reader.result;
13151 reader.readAsDataURL(data.srcfile);
13156 removeCard : function(id)
13159 var card = this.fileCollection.get(id);
13160 card.data.is_deleted = 1;
13161 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13162 //this.fileCollection.remove(card);
13163 //this.items = this.items.filter(function(e) { return e != card });
13164 // dont' really need ot update items.
13165 card.el.dom.parentNode.removeChild(card.el.dom);
13166 this.updateInput();
13172 this.fileCollection.each(function(card) {
13173 if (card.el.dom && card.el.dom.parentNode) {
13174 card.el.dom.parentNode.removeChild(card.el.dom);
13177 this.fileCollection.clear();
13178 this.updateInput();
13181 updateInput : function()
13184 this.fileCollection.each(function(e) {
13188 this.inputEl().dom.value = JSON.stringify(data);
13198 Roo.bootstrap.CardUploader.ID = -1;/*
13200 * Ext JS Library 1.1.1
13201 * Copyright(c) 2006-2007, Ext JS, LLC.
13203 * Originally Released Under LGPL - original licence link has changed is not relivant.
13206 * <script type="text/javascript">
13211 * @class Roo.data.SortTypes
13213 * Defines the default sorting (casting?) comparison functions used when sorting data.
13215 Roo.data.SortTypes = {
13217 * Default sort that does nothing
13218 * @param {Mixed} s The value being converted
13219 * @return {Mixed} The comparison value
13221 none : function(s){
13226 * The regular expression used to strip tags
13230 stripTagsRE : /<\/?[^>]+>/gi,
13233 * Strips all HTML tags to sort on text only
13234 * @param {Mixed} s The value being converted
13235 * @return {String} The comparison value
13237 asText : function(s){
13238 return String(s).replace(this.stripTagsRE, "");
13242 * Strips all HTML tags to sort on text only - Case insensitive
13243 * @param {Mixed} s The value being converted
13244 * @return {String} The comparison value
13246 asUCText : function(s){
13247 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13251 * Case insensitive string
13252 * @param {Mixed} s The value being converted
13253 * @return {String} The comparison value
13255 asUCString : function(s) {
13256 return String(s).toUpperCase();
13261 * @param {Mixed} s The value being converted
13262 * @return {Number} The comparison value
13264 asDate : function(s) {
13268 if(s instanceof Date){
13269 return s.getTime();
13271 return Date.parse(String(s));
13276 * @param {Mixed} s The value being converted
13277 * @return {Float} The comparison value
13279 asFloat : function(s) {
13280 var val = parseFloat(String(s).replace(/,/g, ""));
13289 * @param {Mixed} s The value being converted
13290 * @return {Number} The comparison value
13292 asInt : function(s) {
13293 var val = parseInt(String(s).replace(/,/g, ""));
13301 * Ext JS Library 1.1.1
13302 * Copyright(c) 2006-2007, Ext JS, LLC.
13304 * Originally Released Under LGPL - original licence link has changed is not relivant.
13307 * <script type="text/javascript">
13311 * @class Roo.data.Record
13312 * Instances of this class encapsulate both record <em>definition</em> information, and record
13313 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13314 * to access Records cached in an {@link Roo.data.Store} object.<br>
13316 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13317 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13320 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13322 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13323 * {@link #create}. The parameters are the same.
13324 * @param {Array} data An associative Array of data values keyed by the field name.
13325 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13326 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13327 * not specified an integer id is generated.
13329 Roo.data.Record = function(data, id){
13330 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13335 * Generate a constructor for a specific record layout.
13336 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13337 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13338 * Each field definition object may contain the following properties: <ul>
13339 * <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,
13340 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13341 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13342 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13343 * is being used, then this is a string containing the javascript expression to reference the data relative to
13344 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13345 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13346 * this may be omitted.</p></li>
13347 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13348 * <ul><li>auto (Default, implies no conversion)</li>
13353 * <li>date</li></ul></p></li>
13354 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13355 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13356 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13357 * by the Reader into an object that will be stored in the Record. It is passed the
13358 * following parameters:<ul>
13359 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13361 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13363 * <br>usage:<br><pre><code>
13364 var TopicRecord = Roo.data.Record.create(
13365 {name: 'title', mapping: 'topic_title'},
13366 {name: 'author', mapping: 'username'},
13367 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13368 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13369 {name: 'lastPoster', mapping: 'user2'},
13370 {name: 'excerpt', mapping: 'post_text'}
13373 var myNewRecord = new TopicRecord({
13374 title: 'Do my job please',
13377 lastPost: new Date(),
13378 lastPoster: 'Animal',
13379 excerpt: 'No way dude!'
13381 myStore.add(myNewRecord);
13386 Roo.data.Record.create = function(o){
13387 var f = function(){
13388 f.superclass.constructor.apply(this, arguments);
13390 Roo.extend(f, Roo.data.Record);
13391 var p = f.prototype;
13392 p.fields = new Roo.util.MixedCollection(false, function(field){
13395 for(var i = 0, len = o.length; i < len; i++){
13396 p.fields.add(new Roo.data.Field(o[i]));
13398 f.getField = function(name){
13399 return p.fields.get(name);
13404 Roo.data.Record.AUTO_ID = 1000;
13405 Roo.data.Record.EDIT = 'edit';
13406 Roo.data.Record.REJECT = 'reject';
13407 Roo.data.Record.COMMIT = 'commit';
13409 Roo.data.Record.prototype = {
13411 * Readonly flag - true if this record has been modified.
13420 join : function(store){
13421 this.store = store;
13425 * Set the named field to the specified value.
13426 * @param {String} name The name of the field to set.
13427 * @param {Object} value The value to set the field to.
13429 set : function(name, value){
13430 if(this.data[name] == value){
13434 if(!this.modified){
13435 this.modified = {};
13437 if(typeof this.modified[name] == 'undefined'){
13438 this.modified[name] = this.data[name];
13440 this.data[name] = value;
13441 if(!this.editing && this.store){
13442 this.store.afterEdit(this);
13447 * Get the value of the named field.
13448 * @param {String} name The name of the field to get the value of.
13449 * @return {Object} The value of the field.
13451 get : function(name){
13452 return this.data[name];
13456 beginEdit : function(){
13457 this.editing = true;
13458 this.modified = {};
13462 cancelEdit : function(){
13463 this.editing = false;
13464 delete this.modified;
13468 endEdit : function(){
13469 this.editing = false;
13470 if(this.dirty && this.store){
13471 this.store.afterEdit(this);
13476 * Usually called by the {@link Roo.data.Store} which owns the Record.
13477 * Rejects all changes made to the Record since either creation, or the last commit operation.
13478 * Modified fields are reverted to their original values.
13480 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13481 * of reject operations.
13483 reject : function(){
13484 var m = this.modified;
13486 if(typeof m[n] != "function"){
13487 this.data[n] = m[n];
13490 this.dirty = false;
13491 delete this.modified;
13492 this.editing = false;
13494 this.store.afterReject(this);
13499 * Usually called by the {@link Roo.data.Store} which owns the Record.
13500 * Commits all changes made to the Record since either creation, or the last commit operation.
13502 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13503 * of commit operations.
13505 commit : function(){
13506 this.dirty = false;
13507 delete this.modified;
13508 this.editing = false;
13510 this.store.afterCommit(this);
13515 hasError : function(){
13516 return this.error != null;
13520 clearError : function(){
13525 * Creates a copy of this record.
13526 * @param {String} id (optional) A new record id if you don't want to use this record's id
13529 copy : function(newId) {
13530 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13534 * Ext JS Library 1.1.1
13535 * Copyright(c) 2006-2007, Ext JS, LLC.
13537 * Originally Released Under LGPL - original licence link has changed is not relivant.
13540 * <script type="text/javascript">
13546 * @class Roo.data.Store
13547 * @extends Roo.util.Observable
13548 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13549 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13551 * 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
13552 * has no knowledge of the format of the data returned by the Proxy.<br>
13554 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13555 * instances from the data object. These records are cached and made available through accessor functions.
13557 * Creates a new Store.
13558 * @param {Object} config A config object containing the objects needed for the Store to access data,
13559 * and read the data into Records.
13561 Roo.data.Store = function(config){
13562 this.data = new Roo.util.MixedCollection(false);
13563 this.data.getKey = function(o){
13566 this.baseParams = {};
13568 this.paramNames = {
13573 "multisort" : "_multisort"
13576 if(config && config.data){
13577 this.inlineData = config.data;
13578 delete config.data;
13581 Roo.apply(this, config);
13583 if(this.reader){ // reader passed
13584 this.reader = Roo.factory(this.reader, Roo.data);
13585 this.reader.xmodule = this.xmodule || false;
13586 if(!this.recordType){
13587 this.recordType = this.reader.recordType;
13589 if(this.reader.onMetaChange){
13590 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13594 if(this.recordType){
13595 this.fields = this.recordType.prototype.fields;
13597 this.modified = [];
13601 * @event datachanged
13602 * Fires when the data cache has changed, and a widget which is using this Store
13603 * as a Record cache should refresh its view.
13604 * @param {Store} this
13606 datachanged : true,
13608 * @event metachange
13609 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13610 * @param {Store} this
13611 * @param {Object} meta The JSON metadata
13616 * Fires when Records have been added to the Store
13617 * @param {Store} this
13618 * @param {Roo.data.Record[]} records The array of Records added
13619 * @param {Number} index The index at which the record(s) were added
13624 * Fires when a Record has been removed from the Store
13625 * @param {Store} this
13626 * @param {Roo.data.Record} record The Record that was removed
13627 * @param {Number} index The index at which the record was removed
13632 * Fires when a Record has been updated
13633 * @param {Store} this
13634 * @param {Roo.data.Record} record The Record that was updated
13635 * @param {String} operation The update operation being performed. Value may be one of:
13637 Roo.data.Record.EDIT
13638 Roo.data.Record.REJECT
13639 Roo.data.Record.COMMIT
13645 * Fires when the data cache has been cleared.
13646 * @param {Store} this
13650 * @event beforeload
13651 * Fires before a request is made for a new data object. If the beforeload handler returns false
13652 * the load action will be canceled.
13653 * @param {Store} this
13654 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13658 * @event beforeloadadd
13659 * Fires after a new set of Records has been loaded.
13660 * @param {Store} this
13661 * @param {Roo.data.Record[]} records The Records that were loaded
13662 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13664 beforeloadadd : true,
13667 * Fires after a new set of Records has been loaded, before they are added to the store.
13668 * @param {Store} this
13669 * @param {Roo.data.Record[]} records The Records that were loaded
13670 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13671 * @params {Object} return from reader
13675 * @event loadexception
13676 * Fires if an exception occurs in the Proxy during loading.
13677 * Called with the signature of the Proxy's "loadexception" event.
13678 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13681 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13682 * @param {Object} load options
13683 * @param {Object} jsonData from your request (normally this contains the Exception)
13685 loadexception : true
13689 this.proxy = Roo.factory(this.proxy, Roo.data);
13690 this.proxy.xmodule = this.xmodule || false;
13691 this.relayEvents(this.proxy, ["loadexception"]);
13693 this.sortToggle = {};
13694 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13696 Roo.data.Store.superclass.constructor.call(this);
13698 if(this.inlineData){
13699 this.loadData(this.inlineData);
13700 delete this.inlineData;
13704 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13706 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13707 * without a remote query - used by combo/forms at present.
13711 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13714 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13717 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13718 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13721 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13722 * on any HTTP request
13725 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13728 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13732 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13733 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13735 remoteSort : false,
13738 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13739 * loaded or when a record is removed. (defaults to false).
13741 pruneModifiedRecords : false,
13744 lastOptions : null,
13747 * Add Records to the Store and fires the add event.
13748 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13750 add : function(records){
13751 records = [].concat(records);
13752 for(var i = 0, len = records.length; i < len; i++){
13753 records[i].join(this);
13755 var index = this.data.length;
13756 this.data.addAll(records);
13757 this.fireEvent("add", this, records, index);
13761 * Remove a Record from the Store and fires the remove event.
13762 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13764 remove : function(record){
13765 var index = this.data.indexOf(record);
13766 this.data.removeAt(index);
13768 if(this.pruneModifiedRecords){
13769 this.modified.remove(record);
13771 this.fireEvent("remove", this, record, index);
13775 * Remove all Records from the Store and fires the clear event.
13777 removeAll : function(){
13779 if(this.pruneModifiedRecords){
13780 this.modified = [];
13782 this.fireEvent("clear", this);
13786 * Inserts Records to the Store at the given index and fires the add event.
13787 * @param {Number} index The start index at which to insert the passed Records.
13788 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13790 insert : function(index, records){
13791 records = [].concat(records);
13792 for(var i = 0, len = records.length; i < len; i++){
13793 this.data.insert(index, records[i]);
13794 records[i].join(this);
13796 this.fireEvent("add", this, records, index);
13800 * Get the index within the cache of the passed Record.
13801 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13802 * @return {Number} The index of the passed Record. Returns -1 if not found.
13804 indexOf : function(record){
13805 return this.data.indexOf(record);
13809 * Get the index within the cache of the Record with the passed id.
13810 * @param {String} id The id of the Record to find.
13811 * @return {Number} The index of the Record. Returns -1 if not found.
13813 indexOfId : function(id){
13814 return this.data.indexOfKey(id);
13818 * Get the Record with the specified id.
13819 * @param {String} id The id of the Record to find.
13820 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13822 getById : function(id){
13823 return this.data.key(id);
13827 * Get the Record at the specified index.
13828 * @param {Number} index The index of the Record to find.
13829 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13831 getAt : function(index){
13832 return this.data.itemAt(index);
13836 * Returns a range of Records between specified indices.
13837 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13838 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13839 * @return {Roo.data.Record[]} An array of Records
13841 getRange : function(start, end){
13842 return this.data.getRange(start, end);
13846 storeOptions : function(o){
13847 o = Roo.apply({}, o);
13850 this.lastOptions = o;
13854 * Loads the Record cache from the configured Proxy using the configured Reader.
13856 * If using remote paging, then the first load call must specify the <em>start</em>
13857 * and <em>limit</em> properties in the options.params property to establish the initial
13858 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13860 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13861 * and this call will return before the new data has been loaded. Perform any post-processing
13862 * in a callback function, or in a "load" event handler.</strong>
13864 * @param {Object} options An object containing properties which control loading options:<ul>
13865 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13866 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13867 * passed the following arguments:<ul>
13868 * <li>r : Roo.data.Record[]</li>
13869 * <li>options: Options object from the load call</li>
13870 * <li>success: Boolean success indicator</li></ul></li>
13871 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13872 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13875 load : function(options){
13876 options = options || {};
13877 if(this.fireEvent("beforeload", this, options) !== false){
13878 this.storeOptions(options);
13879 var p = Roo.apply(options.params || {}, this.baseParams);
13880 // if meta was not loaded from remote source.. try requesting it.
13881 if (!this.reader.metaFromRemote) {
13882 p._requestMeta = 1;
13884 if(this.sortInfo && this.remoteSort){
13885 var pn = this.paramNames;
13886 p[pn["sort"]] = this.sortInfo.field;
13887 p[pn["dir"]] = this.sortInfo.direction;
13889 if (this.multiSort) {
13890 var pn = this.paramNames;
13891 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13894 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13899 * Reloads the Record cache from the configured Proxy using the configured Reader and
13900 * the options from the last load operation performed.
13901 * @param {Object} options (optional) An object containing properties which may override the options
13902 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13903 * the most recently used options are reused).
13905 reload : function(options){
13906 this.load(Roo.applyIf(options||{}, this.lastOptions));
13910 // Called as a callback by the Reader during a load operation.
13911 loadRecords : function(o, options, success){
13912 if(!o || success === false){
13913 if(success !== false){
13914 this.fireEvent("load", this, [], options, o);
13916 if(options.callback){
13917 options.callback.call(options.scope || this, [], options, false);
13921 // if data returned failure - throw an exception.
13922 if (o.success === false) {
13923 // show a message if no listener is registered.
13924 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13925 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13927 // loadmask wil be hooked into this..
13928 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13931 var r = o.records, t = o.totalRecords || r.length;
13933 this.fireEvent("beforeloadadd", this, r, options, o);
13935 if(!options || options.add !== true){
13936 if(this.pruneModifiedRecords){
13937 this.modified = [];
13939 for(var i = 0, len = r.length; i < len; i++){
13943 this.data = this.snapshot;
13944 delete this.snapshot;
13947 this.data.addAll(r);
13948 this.totalLength = t;
13950 this.fireEvent("datachanged", this);
13952 this.totalLength = Math.max(t, this.data.length+r.length);
13956 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13958 var e = new Roo.data.Record({});
13960 e.set(this.parent.displayField, this.parent.emptyTitle);
13961 e.set(this.parent.valueField, '');
13966 this.fireEvent("load", this, r, options, o);
13967 if(options.callback){
13968 options.callback.call(options.scope || this, r, options, true);
13974 * Loads data from a passed data block. A Reader which understands the format of the data
13975 * must have been configured in the constructor.
13976 * @param {Object} data The data block from which to read the Records. The format of the data expected
13977 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13978 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13980 loadData : function(o, append){
13981 var r = this.reader.readRecords(o);
13982 this.loadRecords(r, {add: append}, true);
13986 * using 'cn' the nested child reader read the child array into it's child stores.
13987 * @param {Object} rec The record with a 'children array
13989 loadDataFromChildren : function(rec)
13991 this.loadData(this.reader.toLoadData(rec));
13996 * Gets the number of cached records.
13998 * <em>If using paging, this may not be the total size of the dataset. If the data object
13999 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14000 * the data set size</em>
14002 getCount : function(){
14003 return this.data.length || 0;
14007 * Gets the total number of records in the dataset as returned by the server.
14009 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14010 * the dataset size</em>
14012 getTotalCount : function(){
14013 return this.totalLength || 0;
14017 * Returns the sort state of the Store as an object with two properties:
14019 field {String} The name of the field by which the Records are sorted
14020 direction {String} The sort order, "ASC" or "DESC"
14023 getSortState : function(){
14024 return this.sortInfo;
14028 applySort : function(){
14029 if(this.sortInfo && !this.remoteSort){
14030 var s = this.sortInfo, f = s.field;
14031 var st = this.fields.get(f).sortType;
14032 var fn = function(r1, r2){
14033 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14034 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14036 this.data.sort(s.direction, fn);
14037 if(this.snapshot && this.snapshot != this.data){
14038 this.snapshot.sort(s.direction, fn);
14044 * Sets the default sort column and order to be used by the next load operation.
14045 * @param {String} fieldName The name of the field to sort by.
14046 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14048 setDefaultSort : function(field, dir){
14049 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14053 * Sort the Records.
14054 * If remote sorting is used, the sort is performed on the server, and the cache is
14055 * reloaded. If local sorting is used, the cache is sorted internally.
14056 * @param {String} fieldName The name of the field to sort by.
14057 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14059 sort : function(fieldName, dir){
14060 var f = this.fields.get(fieldName);
14062 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14064 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14065 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14070 this.sortToggle[f.name] = dir;
14071 this.sortInfo = {field: f.name, direction: dir};
14072 if(!this.remoteSort){
14074 this.fireEvent("datachanged", this);
14076 this.load(this.lastOptions);
14081 * Calls the specified function for each of the Records in the cache.
14082 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14083 * Returning <em>false</em> aborts and exits the iteration.
14084 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14086 each : function(fn, scope){
14087 this.data.each(fn, scope);
14091 * Gets all records modified since the last commit. Modified records are persisted across load operations
14092 * (e.g., during paging).
14093 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14095 getModifiedRecords : function(){
14096 return this.modified;
14100 createFilterFn : function(property, value, anyMatch){
14101 if(!value.exec){ // not a regex
14102 value = String(value);
14103 if(value.length == 0){
14106 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14108 return function(r){
14109 return value.test(r.data[property]);
14114 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14115 * @param {String} property A field on your records
14116 * @param {Number} start The record index to start at (defaults to 0)
14117 * @param {Number} end The last record index to include (defaults to length - 1)
14118 * @return {Number} The sum
14120 sum : function(property, start, end){
14121 var rs = this.data.items, v = 0;
14122 start = start || 0;
14123 end = (end || end === 0) ? end : rs.length-1;
14125 for(var i = start; i <= end; i++){
14126 v += (rs[i].data[property] || 0);
14132 * Filter the records by a specified property.
14133 * @param {String} field A field on your records
14134 * @param {String/RegExp} value Either a string that the field
14135 * should start with or a RegExp to test against the field
14136 * @param {Boolean} anyMatch True to match any part not just the beginning
14138 filter : function(property, value, anyMatch){
14139 var fn = this.createFilterFn(property, value, anyMatch);
14140 return fn ? this.filterBy(fn) : this.clearFilter();
14144 * Filter by a function. The specified function will be called with each
14145 * record in this data source. If the function returns true the record is included,
14146 * otherwise it is filtered.
14147 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14148 * @param {Object} scope (optional) The scope of the function (defaults to this)
14150 filterBy : function(fn, scope){
14151 this.snapshot = this.snapshot || this.data;
14152 this.data = this.queryBy(fn, scope||this);
14153 this.fireEvent("datachanged", this);
14157 * Query the records by a specified property.
14158 * @param {String} field A field on your records
14159 * @param {String/RegExp} value Either a string that the field
14160 * should start with or a RegExp to test against the field
14161 * @param {Boolean} anyMatch True to match any part not just the beginning
14162 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14164 query : function(property, value, anyMatch){
14165 var fn = this.createFilterFn(property, value, anyMatch);
14166 return fn ? this.queryBy(fn) : this.data.clone();
14170 * Query by a function. The specified function will be called with each
14171 * record in this data source. If the function returns true the record is included
14173 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14174 * @param {Object} scope (optional) The scope of the function (defaults to this)
14175 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14177 queryBy : function(fn, scope){
14178 var data = this.snapshot || this.data;
14179 return data.filterBy(fn, scope||this);
14183 * Collects unique values for a particular dataIndex from this store.
14184 * @param {String} dataIndex The property to collect
14185 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14186 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14187 * @return {Array} An array of the unique values
14189 collect : function(dataIndex, allowNull, bypassFilter){
14190 var d = (bypassFilter === true && this.snapshot) ?
14191 this.snapshot.items : this.data.items;
14192 var v, sv, r = [], l = {};
14193 for(var i = 0, len = d.length; i < len; i++){
14194 v = d[i].data[dataIndex];
14196 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14205 * Revert to a view of the Record cache with no filtering applied.
14206 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14208 clearFilter : function(suppressEvent){
14209 if(this.snapshot && this.snapshot != this.data){
14210 this.data = this.snapshot;
14211 delete this.snapshot;
14212 if(suppressEvent !== true){
14213 this.fireEvent("datachanged", this);
14219 afterEdit : function(record){
14220 if(this.modified.indexOf(record) == -1){
14221 this.modified.push(record);
14223 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14227 afterReject : function(record){
14228 this.modified.remove(record);
14229 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14233 afterCommit : function(record){
14234 this.modified.remove(record);
14235 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14239 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14240 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14242 commitChanges : function(){
14243 var m = this.modified.slice(0);
14244 this.modified = [];
14245 for(var i = 0, len = m.length; i < len; i++){
14251 * Cancel outstanding changes on all changed records.
14253 rejectChanges : function(){
14254 var m = this.modified.slice(0);
14255 this.modified = [];
14256 for(var i = 0, len = m.length; i < len; i++){
14261 onMetaChange : function(meta, rtype, o){
14262 this.recordType = rtype;
14263 this.fields = rtype.prototype.fields;
14264 delete this.snapshot;
14265 this.sortInfo = meta.sortInfo || this.sortInfo;
14266 this.modified = [];
14267 this.fireEvent('metachange', this, this.reader.meta);
14270 moveIndex : function(data, type)
14272 var index = this.indexOf(data);
14274 var newIndex = index + type;
14278 this.insert(newIndex, data);
14283 * Ext JS Library 1.1.1
14284 * Copyright(c) 2006-2007, Ext JS, LLC.
14286 * Originally Released Under LGPL - original licence link has changed is not relivant.
14289 * <script type="text/javascript">
14293 * @class Roo.data.SimpleStore
14294 * @extends Roo.data.Store
14295 * Small helper class to make creating Stores from Array data easier.
14296 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14297 * @cfg {Array} fields An array of field definition objects, or field name strings.
14298 * @cfg {Object} an existing reader (eg. copied from another store)
14299 * @cfg {Array} data The multi-dimensional array of data
14301 * @param {Object} config
14303 Roo.data.SimpleStore = function(config)
14305 Roo.data.SimpleStore.superclass.constructor.call(this, {
14307 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14310 Roo.data.Record.create(config.fields)
14312 proxy : new Roo.data.MemoryProxy(config.data)
14316 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14318 * Ext JS Library 1.1.1
14319 * Copyright(c) 2006-2007, Ext JS, LLC.
14321 * Originally Released Under LGPL - original licence link has changed is not relivant.
14324 * <script type="text/javascript">
14329 * @extends Roo.data.Store
14330 * @class Roo.data.JsonStore
14331 * Small helper class to make creating Stores for JSON data easier. <br/>
14333 var store = new Roo.data.JsonStore({
14334 url: 'get-images.php',
14336 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14339 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14340 * JsonReader and HttpProxy (unless inline data is provided).</b>
14341 * @cfg {Array} fields An array of field definition objects, or field name strings.
14343 * @param {Object} config
14345 Roo.data.JsonStore = function(c){
14346 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14347 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14348 reader: new Roo.data.JsonReader(c, c.fields)
14351 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14353 * Ext JS Library 1.1.1
14354 * Copyright(c) 2006-2007, Ext JS, LLC.
14356 * Originally Released Under LGPL - original licence link has changed is not relivant.
14359 * <script type="text/javascript">
14363 Roo.data.Field = function(config){
14364 if(typeof config == "string"){
14365 config = {name: config};
14367 Roo.apply(this, config);
14370 this.type = "auto";
14373 var st = Roo.data.SortTypes;
14374 // named sortTypes are supported, here we look them up
14375 if(typeof this.sortType == "string"){
14376 this.sortType = st[this.sortType];
14379 // set default sortType for strings and dates
14380 if(!this.sortType){
14383 this.sortType = st.asUCString;
14386 this.sortType = st.asDate;
14389 this.sortType = st.none;
14394 var stripRe = /[\$,%]/g;
14396 // prebuilt conversion function for this field, instead of
14397 // switching every time we're reading a value
14399 var cv, dateFormat = this.dateFormat;
14404 cv = function(v){ return v; };
14407 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14411 return v !== undefined && v !== null && v !== '' ?
14412 parseInt(String(v).replace(stripRe, ""), 10) : '';
14417 return v !== undefined && v !== null && v !== '' ?
14418 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14423 cv = function(v){ return v === true || v === "true" || v == 1; };
14430 if(v instanceof Date){
14434 if(dateFormat == "timestamp"){
14435 return new Date(v*1000);
14437 return Date.parseDate(v, dateFormat);
14439 var parsed = Date.parse(v);
14440 return parsed ? new Date(parsed) : null;
14449 Roo.data.Field.prototype = {
14457 * Ext JS Library 1.1.1
14458 * Copyright(c) 2006-2007, Ext JS, LLC.
14460 * Originally Released Under LGPL - original licence link has changed is not relivant.
14463 * <script type="text/javascript">
14466 // Base class for reading structured data from a data source. This class is intended to be
14467 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14470 * @class Roo.data.DataReader
14471 * Base class for reading structured data from a data source. This class is intended to be
14472 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14475 Roo.data.DataReader = function(meta, recordType){
14479 this.recordType = recordType instanceof Array ?
14480 Roo.data.Record.create(recordType) : recordType;
14483 Roo.data.DataReader.prototype = {
14486 readerType : 'Data',
14488 * Create an empty record
14489 * @param {Object} data (optional) - overlay some values
14490 * @return {Roo.data.Record} record created.
14492 newRow : function(d) {
14494 this.recordType.prototype.fields.each(function(c) {
14496 case 'int' : da[c.name] = 0; break;
14497 case 'date' : da[c.name] = new Date(); break;
14498 case 'float' : da[c.name] = 0.0; break;
14499 case 'boolean' : da[c.name] = false; break;
14500 default : da[c.name] = ""; break;
14504 return new this.recordType(Roo.apply(da, d));
14510 * Ext JS Library 1.1.1
14511 * Copyright(c) 2006-2007, Ext JS, LLC.
14513 * Originally Released Under LGPL - original licence link has changed is not relivant.
14516 * <script type="text/javascript">
14520 * @class Roo.data.DataProxy
14521 * @extends Roo.data.Observable
14522 * This class is an abstract base class for implementations which provide retrieval of
14523 * unformatted data objects.<br>
14525 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14526 * (of the appropriate type which knows how to parse the data object) to provide a block of
14527 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14529 * Custom implementations must implement the load method as described in
14530 * {@link Roo.data.HttpProxy#load}.
14532 Roo.data.DataProxy = function(){
14535 * @event beforeload
14536 * Fires before a network request is made to retrieve a data object.
14537 * @param {Object} This DataProxy object.
14538 * @param {Object} params The params parameter to the load function.
14543 * Fires before the load method's callback is called.
14544 * @param {Object} This DataProxy object.
14545 * @param {Object} o The data object.
14546 * @param {Object} arg The callback argument object passed to the load function.
14550 * @event loadexception
14551 * Fires if an Exception occurs during data retrieval.
14552 * @param {Object} This DataProxy object.
14553 * @param {Object} o The data object.
14554 * @param {Object} arg The callback argument object passed to the load function.
14555 * @param {Object} e The Exception.
14557 loadexception : true
14559 Roo.data.DataProxy.superclass.constructor.call(this);
14562 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14565 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14569 * Ext JS Library 1.1.1
14570 * Copyright(c) 2006-2007, Ext JS, LLC.
14572 * Originally Released Under LGPL - original licence link has changed is not relivant.
14575 * <script type="text/javascript">
14578 * @class Roo.data.MemoryProxy
14579 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14580 * to the Reader when its load method is called.
14582 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14584 Roo.data.MemoryProxy = function(data){
14588 Roo.data.MemoryProxy.superclass.constructor.call(this);
14592 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14595 * Load data from the requested source (in this case an in-memory
14596 * data object passed to the constructor), read the data object into
14597 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14598 * process that block using the passed callback.
14599 * @param {Object} params This parameter is not used by the MemoryProxy class.
14600 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14601 * object into a block of Roo.data.Records.
14602 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14603 * The function must be passed <ul>
14604 * <li>The Record block object</li>
14605 * <li>The "arg" argument from the load function</li>
14606 * <li>A boolean success indicator</li>
14608 * @param {Object} scope The scope in which to call the callback
14609 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14611 load : function(params, reader, callback, scope, arg){
14612 params = params || {};
14615 result = reader.readRecords(params.data ? params.data :this.data);
14617 this.fireEvent("loadexception", this, arg, null, e);
14618 callback.call(scope, null, arg, false);
14621 callback.call(scope, result, arg, true);
14625 update : function(params, records){
14630 * Ext JS Library 1.1.1
14631 * Copyright(c) 2006-2007, Ext JS, LLC.
14633 * Originally Released Under LGPL - original licence link has changed is not relivant.
14636 * <script type="text/javascript">
14639 * @class Roo.data.HttpProxy
14640 * @extends Roo.data.DataProxy
14641 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14642 * configured to reference a certain URL.<br><br>
14644 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14645 * from which the running page was served.<br><br>
14647 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14649 * Be aware that to enable the browser to parse an XML document, the server must set
14650 * the Content-Type header in the HTTP response to "text/xml".
14652 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14653 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14654 * will be used to make the request.
14656 Roo.data.HttpProxy = function(conn){
14657 Roo.data.HttpProxy.superclass.constructor.call(this);
14658 // is conn a conn config or a real conn?
14660 this.useAjax = !conn || !conn.events;
14664 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14665 // thse are take from connection...
14668 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14671 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14672 * extra parameters to each request made by this object. (defaults to undefined)
14675 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14676 * to each request made by this object. (defaults to undefined)
14679 * @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)
14682 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14685 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14691 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14695 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14696 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14697 * a finer-grained basis than the DataProxy events.
14699 getConnection : function(){
14700 return this.useAjax ? Roo.Ajax : this.conn;
14704 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14705 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14706 * process that block using the passed callback.
14707 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14708 * for the request to the remote server.
14709 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14710 * object into a block of Roo.data.Records.
14711 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14712 * The function must be passed <ul>
14713 * <li>The Record block object</li>
14714 * <li>The "arg" argument from the load function</li>
14715 * <li>A boolean success indicator</li>
14717 * @param {Object} scope The scope in which to call the callback
14718 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14720 load : function(params, reader, callback, scope, arg){
14721 if(this.fireEvent("beforeload", this, params) !== false){
14723 params : params || {},
14725 callback : callback,
14730 callback : this.loadResponse,
14734 Roo.applyIf(o, this.conn);
14735 if(this.activeRequest){
14736 Roo.Ajax.abort(this.activeRequest);
14738 this.activeRequest = Roo.Ajax.request(o);
14740 this.conn.request(o);
14743 callback.call(scope||this, null, arg, false);
14748 loadResponse : function(o, success, response){
14749 delete this.activeRequest;
14751 this.fireEvent("loadexception", this, o, response);
14752 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14757 result = o.reader.read(response);
14759 this.fireEvent("loadexception", this, o, response, e);
14760 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14764 this.fireEvent("load", this, o, o.request.arg);
14765 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14769 update : function(dataSet){
14774 updateResponse : function(dataSet){
14779 * Ext JS Library 1.1.1
14780 * Copyright(c) 2006-2007, Ext JS, LLC.
14782 * Originally Released Under LGPL - original licence link has changed is not relivant.
14785 * <script type="text/javascript">
14789 * @class Roo.data.ScriptTagProxy
14790 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14791 * other than the originating domain of the running page.<br><br>
14793 * <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
14794 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14796 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14797 * source code that is used as the source inside a <script> tag.<br><br>
14799 * In order for the browser to process the returned data, the server must wrap the data object
14800 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14801 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14802 * depending on whether the callback name was passed:
14805 boolean scriptTag = false;
14806 String cb = request.getParameter("callback");
14809 response.setContentType("text/javascript");
14811 response.setContentType("application/x-json");
14813 Writer out = response.getWriter();
14815 out.write(cb + "(");
14817 out.print(dataBlock.toJsonString());
14824 * @param {Object} config A configuration object.
14826 Roo.data.ScriptTagProxy = function(config){
14827 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14828 Roo.apply(this, config);
14829 this.head = document.getElementsByTagName("head")[0];
14832 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14834 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14836 * @cfg {String} url The URL from which to request the data object.
14839 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14843 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14844 * the server the name of the callback function set up by the load call to process the returned data object.
14845 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14846 * javascript output which calls this named function passing the data object as its only parameter.
14848 callbackParam : "callback",
14850 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14851 * name to the request.
14856 * Load data from the configured URL, read the data object into
14857 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14858 * process that block using the passed callback.
14859 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14860 * for the request to the remote server.
14861 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14862 * object into a block of Roo.data.Records.
14863 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14864 * The function must be passed <ul>
14865 * <li>The Record block object</li>
14866 * <li>The "arg" argument from the load function</li>
14867 * <li>A boolean success indicator</li>
14869 * @param {Object} scope The scope in which to call the callback
14870 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14872 load : function(params, reader, callback, scope, arg){
14873 if(this.fireEvent("beforeload", this, params) !== false){
14875 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14877 var url = this.url;
14878 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14880 url += "&_dc=" + (new Date().getTime());
14882 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14885 cb : "stcCallback"+transId,
14886 scriptId : "stcScript"+transId,
14890 callback : callback,
14896 window[trans.cb] = function(o){
14897 conn.handleResponse(o, trans);
14900 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14902 if(this.autoAbort !== false){
14906 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14908 var script = document.createElement("script");
14909 script.setAttribute("src", url);
14910 script.setAttribute("type", "text/javascript");
14911 script.setAttribute("id", trans.scriptId);
14912 this.head.appendChild(script);
14914 this.trans = trans;
14916 callback.call(scope||this, null, arg, false);
14921 isLoading : function(){
14922 return this.trans ? true : false;
14926 * Abort the current server request.
14928 abort : function(){
14929 if(this.isLoading()){
14930 this.destroyTrans(this.trans);
14935 destroyTrans : function(trans, isLoaded){
14936 this.head.removeChild(document.getElementById(trans.scriptId));
14937 clearTimeout(trans.timeoutId);
14939 window[trans.cb] = undefined;
14941 delete window[trans.cb];
14944 // if hasn't been loaded, wait for load to remove it to prevent script error
14945 window[trans.cb] = function(){
14946 window[trans.cb] = undefined;
14948 delete window[trans.cb];
14955 handleResponse : function(o, trans){
14956 this.trans = false;
14957 this.destroyTrans(trans, true);
14960 result = trans.reader.readRecords(o);
14962 this.fireEvent("loadexception", this, o, trans.arg, e);
14963 trans.callback.call(trans.scope||window, null, trans.arg, false);
14966 this.fireEvent("load", this, o, trans.arg);
14967 trans.callback.call(trans.scope||window, result, trans.arg, true);
14971 handleFailure : function(trans){
14972 this.trans = false;
14973 this.destroyTrans(trans, false);
14974 this.fireEvent("loadexception", this, null, trans.arg);
14975 trans.callback.call(trans.scope||window, null, trans.arg, false);
14979 * Ext JS Library 1.1.1
14980 * Copyright(c) 2006-2007, Ext JS, LLC.
14982 * Originally Released Under LGPL - original licence link has changed is not relivant.
14985 * <script type="text/javascript">
14989 * @class Roo.data.JsonReader
14990 * @extends Roo.data.DataReader
14991 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14992 * based on mappings in a provided Roo.data.Record constructor.
14994 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14995 * in the reply previously.
15000 var RecordDef = Roo.data.Record.create([
15001 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15002 {name: 'occupation'} // This field will use "occupation" as the mapping.
15004 var myReader = new Roo.data.JsonReader({
15005 totalProperty: "results", // The property which contains the total dataset size (optional)
15006 root: "rows", // The property which contains an Array of row objects
15007 id: "id" // The property within each row object that provides an ID for the record (optional)
15011 * This would consume a JSON file like this:
15013 { 'results': 2, 'rows': [
15014 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15015 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15018 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15019 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15020 * paged from the remote server.
15021 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15022 * @cfg {String} root name of the property which contains the Array of row objects.
15023 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15024 * @cfg {Array} fields Array of field definition objects
15026 * Create a new JsonReader
15027 * @param {Object} meta Metadata configuration options
15028 * @param {Object} recordType Either an Array of field definition objects,
15029 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15031 Roo.data.JsonReader = function(meta, recordType){
15034 // set some defaults:
15035 Roo.applyIf(meta, {
15036 totalProperty: 'total',
15037 successProperty : 'success',
15042 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15044 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15046 readerType : 'Json',
15049 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15050 * Used by Store query builder to append _requestMeta to params.
15053 metaFromRemote : false,
15055 * This method is only used by a DataProxy which has retrieved data from a remote server.
15056 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15057 * @return {Object} data A data block which is used by an Roo.data.Store object as
15058 * a cache of Roo.data.Records.
15060 read : function(response){
15061 var json = response.responseText;
15063 var o = /* eval:var:o */ eval("("+json+")");
15065 throw {message: "JsonReader.read: Json object not found"};
15071 this.metaFromRemote = true;
15072 this.meta = o.metaData;
15073 this.recordType = Roo.data.Record.create(o.metaData.fields);
15074 this.onMetaChange(this.meta, this.recordType, o);
15076 return this.readRecords(o);
15079 // private function a store will implement
15080 onMetaChange : function(meta, recordType, o){
15087 simpleAccess: function(obj, subsc) {
15094 getJsonAccessor: function(){
15096 return function(expr) {
15098 return(re.test(expr))
15099 ? new Function("obj", "return obj." + expr)
15104 return Roo.emptyFn;
15109 * Create a data block containing Roo.data.Records from an XML document.
15110 * @param {Object} o An object which contains an Array of row objects in the property specified
15111 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15112 * which contains the total size of the dataset.
15113 * @return {Object} data A data block which is used by an Roo.data.Store object as
15114 * a cache of Roo.data.Records.
15116 readRecords : function(o){
15118 * After any data loads, the raw JSON data is available for further custom processing.
15122 var s = this.meta, Record = this.recordType,
15123 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15125 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15127 if(s.totalProperty) {
15128 this.getTotal = this.getJsonAccessor(s.totalProperty);
15130 if(s.successProperty) {
15131 this.getSuccess = this.getJsonAccessor(s.successProperty);
15133 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15135 var g = this.getJsonAccessor(s.id);
15136 this.getId = function(rec) {
15138 return (r === undefined || r === "") ? null : r;
15141 this.getId = function(){return null;};
15144 for(var jj = 0; jj < fl; jj++){
15146 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15147 this.ef[jj] = this.getJsonAccessor(map);
15151 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15152 if(s.totalProperty){
15153 var vt = parseInt(this.getTotal(o), 10);
15158 if(s.successProperty){
15159 var vs = this.getSuccess(o);
15160 if(vs === false || vs === 'false'){
15165 for(var i = 0; i < c; i++){
15168 var id = this.getId(n);
15169 for(var j = 0; j < fl; j++){
15171 var v = this.ef[j](n);
15173 Roo.log('missing convert for ' + f.name);
15177 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15179 var record = new Record(values, id);
15181 records[i] = record;
15187 totalRecords : totalRecords
15190 // used when loading children.. @see loadDataFromChildren
15191 toLoadData: function(rec)
15193 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15194 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15195 return { data : data, total : data.length };
15200 * Ext JS Library 1.1.1
15201 * Copyright(c) 2006-2007, Ext JS, LLC.
15203 * Originally Released Under LGPL - original licence link has changed is not relivant.
15206 * <script type="text/javascript">
15210 * @class Roo.data.ArrayReader
15211 * @extends Roo.data.DataReader
15212 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15213 * Each element of that Array represents a row of data fields. The
15214 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15215 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15219 var RecordDef = Roo.data.Record.create([
15220 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15221 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15223 var myReader = new Roo.data.ArrayReader({
15224 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15228 * This would consume an Array like this:
15230 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15234 * Create a new JsonReader
15235 * @param {Object} meta Metadata configuration options.
15236 * @param {Object|Array} recordType Either an Array of field definition objects
15238 * @cfg {Array} fields Array of field definition objects
15239 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15240 * as specified to {@link Roo.data.Record#create},
15241 * or an {@link Roo.data.Record} object
15244 * created using {@link Roo.data.Record#create}.
15246 Roo.data.ArrayReader = function(meta, recordType)
15248 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15251 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15254 * Create a data block containing Roo.data.Records from an XML document.
15255 * @param {Object} o An Array of row objects which represents the dataset.
15256 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15257 * a cache of Roo.data.Records.
15259 readRecords : function(o)
15261 var sid = this.meta ? this.meta.id : null;
15262 var recordType = this.recordType, fields = recordType.prototype.fields;
15265 for(var i = 0; i < root.length; i++){
15268 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15269 for(var j = 0, jlen = fields.length; j < jlen; j++){
15270 var f = fields.items[j];
15271 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15272 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15274 values[f.name] = v;
15276 var record = new recordType(values, id);
15278 records[records.length] = record;
15282 totalRecords : records.length
15285 // used when loading children.. @see loadDataFromChildren
15286 toLoadData: function(rec)
15288 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15289 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15300 * @class Roo.bootstrap.ComboBox
15301 * @extends Roo.bootstrap.TriggerField
15302 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15303 * @cfg {Boolean} append (true|false) default false
15304 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15305 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15306 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15307 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15308 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15309 * @cfg {Boolean} animate default true
15310 * @cfg {Boolean} emptyResultText only for touch device
15311 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15312 * @cfg {String} emptyTitle default ''
15313 * @cfg {Number} width fixed with? experimental
15315 * Create a new ComboBox.
15316 * @param {Object} config Configuration options
15318 Roo.bootstrap.ComboBox = function(config){
15319 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15323 * Fires when the dropdown list is expanded
15324 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 * Fires when the dropdown list is collapsed
15330 * @param {Roo.bootstrap.ComboBox} combo This combo box
15334 * @event beforeselect
15335 * Fires before a list item is selected. Return false to cancel the selection.
15336 * @param {Roo.bootstrap.ComboBox} combo This combo box
15337 * @param {Roo.data.Record} record The data record returned from the underlying store
15338 * @param {Number} index The index of the selected item in the dropdown list
15340 'beforeselect' : true,
15343 * Fires when a list item is selected
15344 * @param {Roo.bootstrap.ComboBox} combo This combo box
15345 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15346 * @param {Number} index The index of the selected item in the dropdown list
15350 * @event beforequery
15351 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15352 * The event object passed has these properties:
15353 * @param {Roo.bootstrap.ComboBox} combo This combo box
15354 * @param {String} query The query
15355 * @param {Boolean} forceAll true to force "all" query
15356 * @param {Boolean} cancel true to cancel the query
15357 * @param {Object} e The query event object
15359 'beforequery': true,
15362 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15363 * @param {Roo.bootstrap.ComboBox} combo This combo box
15368 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15369 * @param {Roo.bootstrap.ComboBox} combo This combo box
15370 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15375 * Fires when the remove value from the combobox array
15376 * @param {Roo.bootstrap.ComboBox} combo This combo box
15380 * @event afterremove
15381 * Fires when the remove value from the combobox array
15382 * @param {Roo.bootstrap.ComboBox} combo This combo box
15384 'afterremove' : true,
15386 * @event specialfilter
15387 * Fires when specialfilter
15388 * @param {Roo.bootstrap.ComboBox} combo This combo box
15390 'specialfilter' : true,
15393 * Fires when tick the element
15394 * @param {Roo.bootstrap.ComboBox} combo This combo box
15398 * @event touchviewdisplay
15399 * Fires when touch view require special display (default is using displayField)
15400 * @param {Roo.bootstrap.ComboBox} combo This combo box
15401 * @param {Object} cfg set html .
15403 'touchviewdisplay' : true
15408 this.tickItems = [];
15410 this.selectedIndex = -1;
15411 if(this.mode == 'local'){
15412 if(config.queryDelay === undefined){
15413 this.queryDelay = 10;
15415 if(config.minChars === undefined){
15421 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15424 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15425 * rendering into an Roo.Editor, defaults to false)
15428 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15429 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15432 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15435 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15436 * the dropdown list (defaults to undefined, with no header element)
15440 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15444 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15446 listWidth: undefined,
15448 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15449 * mode = 'remote' or 'text' if mode = 'local')
15451 displayField: undefined,
15454 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15455 * mode = 'remote' or 'value' if mode = 'local').
15456 * Note: use of a valueField requires the user make a selection
15457 * in order for a value to be mapped.
15459 valueField: undefined,
15461 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15466 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15467 * field's data value (defaults to the underlying DOM element's name)
15469 hiddenName: undefined,
15471 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15475 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15477 selectedClass: 'active',
15480 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15484 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15485 * anchor positions (defaults to 'tl-bl')
15487 listAlign: 'tl-bl?',
15489 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15493 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15494 * query specified by the allQuery config option (defaults to 'query')
15496 triggerAction: 'query',
15498 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15499 * (defaults to 4, does not apply if editable = false)
15503 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15504 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15508 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15509 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15513 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15514 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15518 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15519 * when editable = true (defaults to false)
15521 selectOnFocus:false,
15523 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15525 queryParam: 'query',
15527 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15528 * when mode = 'remote' (defaults to 'Loading...')
15530 loadingText: 'Loading...',
15532 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15536 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15540 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15541 * traditional select (defaults to true)
15545 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15549 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15553 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15554 * listWidth has a higher value)
15558 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15559 * allow the user to set arbitrary text into the field (defaults to false)
15561 forceSelection:false,
15563 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15564 * if typeAhead = true (defaults to 250)
15566 typeAheadDelay : 250,
15568 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15569 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15571 valueNotFoundText : undefined,
15573 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15575 blockFocus : false,
15578 * @cfg {Boolean} disableClear Disable showing of clear button.
15580 disableClear : false,
15582 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15584 alwaysQuery : false,
15587 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15592 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15594 invalidClass : "has-warning",
15597 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15599 validClass : "has-success",
15602 * @cfg {Boolean} specialFilter (true|false) special filter default false
15604 specialFilter : false,
15607 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15609 mobileTouchView : true,
15612 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15614 useNativeIOS : false,
15617 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15619 mobile_restrict_height : false,
15621 ios_options : false,
15633 btnPosition : 'right',
15634 triggerList : true,
15635 showToggleBtn : true,
15637 emptyResultText: 'Empty',
15638 triggerText : 'Select',
15642 // element that contains real text value.. (when hidden is used..)
15644 getAutoCreate : function()
15649 * Render classic select for iso
15652 if(Roo.isIOS && this.useNativeIOS){
15653 cfg = this.getAutoCreateNativeIOS();
15661 if(Roo.isTouch && this.mobileTouchView){
15662 cfg = this.getAutoCreateTouchView();
15669 if(!this.tickable){
15670 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15675 * ComboBox with tickable selections
15678 var align = this.labelAlign || this.parentLabelAlign();
15681 cls : 'form-group roo-combobox-tickable' //input-group
15684 var btn_text_select = '';
15685 var btn_text_done = '';
15686 var btn_text_cancel = '';
15688 if (this.btn_text_show) {
15689 btn_text_select = 'Select';
15690 btn_text_done = 'Done';
15691 btn_text_cancel = 'Cancel';
15696 cls : 'tickable-buttons',
15701 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15702 //html : this.triggerText
15703 html: btn_text_select
15709 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15711 html: btn_text_done
15717 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15719 html: btn_text_cancel
15725 buttons.cn.unshift({
15727 cls: 'roo-select2-search-field-input'
15733 Roo.each(buttons.cn, function(c){
15735 c.cls += ' btn-' + _this.size;
15738 if (_this.disabled) {
15745 style : 'display: contents',
15750 cls: 'form-hidden-field'
15754 cls: 'roo-select2-choices',
15758 cls: 'roo-select2-search-field',
15769 cls: 'roo-select2-container input-group roo-select2-container-multi',
15775 // cls: 'typeahead typeahead-long dropdown-menu',
15776 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15781 if(this.hasFeedback && !this.allowBlank){
15785 cls: 'glyphicon form-control-feedback'
15788 combobox.cn.push(feedback);
15795 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15796 tooltip : 'This field is required'
15798 if (Roo.bootstrap.version == 4) {
15801 style : 'display:none'
15804 if (align ==='left' && this.fieldLabel.length) {
15806 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15813 cls : 'control-label col-form-label',
15814 html : this.fieldLabel
15826 var labelCfg = cfg.cn[1];
15827 var contentCfg = cfg.cn[2];
15830 if(this.indicatorpos == 'right'){
15836 cls : 'control-label col-form-label',
15840 html : this.fieldLabel
15856 labelCfg = cfg.cn[0];
15857 contentCfg = cfg.cn[1];
15861 if(this.labelWidth > 12){
15862 labelCfg.style = "width: " + this.labelWidth + 'px';
15864 if(this.width * 1 > 0){
15865 contentCfg.style = "width: " + this.width + 'px';
15867 if(this.labelWidth < 13 && this.labelmd == 0){
15868 this.labelmd = this.labelWidth;
15871 if(this.labellg > 0){
15872 labelCfg.cls += ' col-lg-' + this.labellg;
15873 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15876 if(this.labelmd > 0){
15877 labelCfg.cls += ' col-md-' + this.labelmd;
15878 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15881 if(this.labelsm > 0){
15882 labelCfg.cls += ' col-sm-' + this.labelsm;
15883 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15886 if(this.labelxs > 0){
15887 labelCfg.cls += ' col-xs-' + this.labelxs;
15888 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15892 } else if ( this.fieldLabel.length) {
15893 // Roo.log(" label");
15898 //cls : 'input-group-addon',
15899 html : this.fieldLabel
15904 if(this.indicatorpos == 'right'){
15908 //cls : 'input-group-addon',
15909 html : this.fieldLabel
15919 // Roo.log(" no label && no align");
15926 ['xs','sm','md','lg'].map(function(size){
15927 if (settings[size]) {
15928 cfg.cls += ' col-' + size + '-' + settings[size];
15936 _initEventsCalled : false,
15939 initEvents: function()
15941 if (this._initEventsCalled) { // as we call render... prevent looping...
15944 this._initEventsCalled = true;
15947 throw "can not find store for combo";
15950 this.indicator = this.indicatorEl();
15952 this.store = Roo.factory(this.store, Roo.data);
15953 this.store.parent = this;
15955 // if we are building from html. then this element is so complex, that we can not really
15956 // use the rendered HTML.
15957 // so we have to trash and replace the previous code.
15958 if (Roo.XComponent.build_from_html) {
15959 // remove this element....
15960 var e = this.el.dom, k=0;
15961 while (e ) { e = e.previousSibling; ++k;}
15966 this.rendered = false;
15968 this.render(this.parent().getChildContainer(true), k);
15971 if(Roo.isIOS && this.useNativeIOS){
15972 this.initIOSView();
15980 if(Roo.isTouch && this.mobileTouchView){
15981 this.initTouchView();
15986 this.initTickableEvents();
15990 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15992 if(this.hiddenName){
15994 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15996 this.hiddenField.dom.value =
15997 this.hiddenValue !== undefined ? this.hiddenValue :
15998 this.value !== undefined ? this.value : '';
16000 // prevent input submission
16001 this.el.dom.removeAttribute('name');
16002 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16007 // this.el.dom.setAttribute('autocomplete', 'off');
16010 var cls = 'x-combo-list';
16012 //this.list = new Roo.Layer({
16013 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16019 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16020 _this.list.setWidth(lw);
16023 this.list.on('mouseover', this.onViewOver, this);
16024 this.list.on('mousemove', this.onViewMove, this);
16025 this.list.on('scroll', this.onViewScroll, this);
16028 this.list.swallowEvent('mousewheel');
16029 this.assetHeight = 0;
16032 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16033 this.assetHeight += this.header.getHeight();
16036 this.innerList = this.list.createChild({cls:cls+'-inner'});
16037 this.innerList.on('mouseover', this.onViewOver, this);
16038 this.innerList.on('mousemove', this.onViewMove, this);
16039 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16041 if(this.allowBlank && !this.pageSize && !this.disableClear){
16042 this.footer = this.list.createChild({cls:cls+'-ft'});
16043 this.pageTb = new Roo.Toolbar(this.footer);
16047 this.footer = this.list.createChild({cls:cls+'-ft'});
16048 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16049 {pageSize: this.pageSize});
16053 if (this.pageTb && this.allowBlank && !this.disableClear) {
16055 this.pageTb.add(new Roo.Toolbar.Fill(), {
16056 cls: 'x-btn-icon x-btn-clear',
16058 handler: function()
16061 _this.clearValue();
16062 _this.onSelect(false, -1);
16067 this.assetHeight += this.footer.getHeight();
16072 this.tpl = Roo.bootstrap.version == 4 ?
16073 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16074 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16077 this.view = new Roo.View(this.list, this.tpl, {
16078 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16080 //this.view.wrapEl.setDisplayed(false);
16081 this.view.on('click', this.onViewClick, this);
16084 this.store.on('beforeload', this.onBeforeLoad, this);
16085 this.store.on('load', this.onLoad, this);
16086 this.store.on('loadexception', this.onLoadException, this);
16088 if(this.resizable){
16089 this.resizer = new Roo.Resizable(this.list, {
16090 pinned:true, handles:'se'
16092 this.resizer.on('resize', function(r, w, h){
16093 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16094 this.listWidth = w;
16095 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16096 this.restrictHeight();
16098 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16101 if(!this.editable){
16102 this.editable = true;
16103 this.setEditable(false);
16108 if (typeof(this.events.add.listeners) != 'undefined') {
16110 this.addicon = this.wrap.createChild(
16111 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16113 this.addicon.on('click', function(e) {
16114 this.fireEvent('add', this);
16117 if (typeof(this.events.edit.listeners) != 'undefined') {
16119 this.editicon = this.wrap.createChild(
16120 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16121 if (this.addicon) {
16122 this.editicon.setStyle('margin-left', '40px');
16124 this.editicon.on('click', function(e) {
16126 // we fire even if inothing is selected..
16127 this.fireEvent('edit', this, this.lastData );
16133 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16134 "up" : function(e){
16135 this.inKeyMode = true;
16139 "down" : function(e){
16140 if(!this.isExpanded()){
16141 this.onTriggerClick();
16143 this.inKeyMode = true;
16148 "enter" : function(e){
16149 // this.onViewClick();
16153 if(this.fireEvent("specialkey", this, e)){
16154 this.onViewClick(false);
16160 "esc" : function(e){
16164 "tab" : function(e){
16167 if(this.fireEvent("specialkey", this, e)){
16168 this.onViewClick(false);
16176 doRelay : function(foo, bar, hname){
16177 if(hname == 'down' || this.scope.isExpanded()){
16178 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16187 this.queryDelay = Math.max(this.queryDelay || 10,
16188 this.mode == 'local' ? 10 : 250);
16191 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16193 if(this.typeAhead){
16194 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16196 if(this.editable !== false){
16197 this.inputEl().on("keyup", this.onKeyUp, this);
16199 if(this.forceSelection){
16200 this.inputEl().on('blur', this.doForce, this);
16204 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16205 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16209 initTickableEvents: function()
16213 if(this.hiddenName){
16215 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16217 this.hiddenField.dom.value =
16218 this.hiddenValue !== undefined ? this.hiddenValue :
16219 this.value !== undefined ? this.value : '';
16221 // prevent input submission
16222 this.el.dom.removeAttribute('name');
16223 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16228 // this.list = this.el.select('ul.dropdown-menu',true).first();
16230 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16231 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16232 if(this.triggerList){
16233 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16236 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16237 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16239 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16240 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16242 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16243 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16245 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16246 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16247 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16250 this.cancelBtn.hide();
16255 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16256 _this.list.setWidth(lw);
16259 this.list.on('mouseover', this.onViewOver, this);
16260 this.list.on('mousemove', this.onViewMove, this);
16262 this.list.on('scroll', this.onViewScroll, this);
16265 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16266 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16269 this.view = new Roo.View(this.list, this.tpl, {
16274 selectedClass: this.selectedClass
16277 //this.view.wrapEl.setDisplayed(false);
16278 this.view.on('click', this.onViewClick, this);
16282 this.store.on('beforeload', this.onBeforeLoad, this);
16283 this.store.on('load', this.onLoad, this);
16284 this.store.on('loadexception', this.onLoadException, this);
16287 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16288 "up" : function(e){
16289 this.inKeyMode = true;
16293 "down" : function(e){
16294 this.inKeyMode = true;
16298 "enter" : function(e){
16299 if(this.fireEvent("specialkey", this, e)){
16300 this.onViewClick(false);
16306 "esc" : function(e){
16307 this.onTickableFooterButtonClick(e, false, false);
16310 "tab" : function(e){
16311 this.fireEvent("specialkey", this, e);
16313 this.onTickableFooterButtonClick(e, false, false);
16320 doRelay : function(e, fn, key){
16321 if(this.scope.isExpanded()){
16322 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16331 this.queryDelay = Math.max(this.queryDelay || 10,
16332 this.mode == 'local' ? 10 : 250);
16335 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16337 if(this.typeAhead){
16338 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16341 if(this.editable !== false){
16342 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16345 this.indicator = this.indicatorEl();
16347 if(this.indicator){
16348 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16349 this.indicator.hide();
16354 onDestroy : function(){
16356 this.view.setStore(null);
16357 this.view.el.removeAllListeners();
16358 this.view.el.remove();
16359 this.view.purgeListeners();
16362 this.list.dom.innerHTML = '';
16366 this.store.un('beforeload', this.onBeforeLoad, this);
16367 this.store.un('load', this.onLoad, this);
16368 this.store.un('loadexception', this.onLoadException, this);
16370 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16374 fireKey : function(e){
16375 if(e.isNavKeyPress() && !this.list.isVisible()){
16376 this.fireEvent("specialkey", this, e);
16381 onResize: function(w, h)
16385 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16387 // if(typeof w != 'number'){
16388 // // we do not handle it!?!?
16391 // var tw = this.trigger.getWidth();
16392 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16393 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16395 // this.inputEl().setWidth( this.adjustWidth('input', x));
16397 // //this.trigger.setStyle('left', x+'px');
16399 // if(this.list && this.listWidth === undefined){
16400 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16401 // this.list.setWidth(lw);
16402 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16410 * Allow or prevent the user from directly editing the field text. If false is passed,
16411 * the user will only be able to select from the items defined in the dropdown list. This method
16412 * is the runtime equivalent of setting the 'editable' config option at config time.
16413 * @param {Boolean} value True to allow the user to directly edit the field text
16415 setEditable : function(value){
16416 if(value == this.editable){
16419 this.editable = value;
16421 this.inputEl().dom.setAttribute('readOnly', true);
16422 this.inputEl().on('mousedown', this.onTriggerClick, this);
16423 this.inputEl().addClass('x-combo-noedit');
16425 this.inputEl().dom.removeAttribute('readOnly');
16426 this.inputEl().un('mousedown', this.onTriggerClick, this);
16427 this.inputEl().removeClass('x-combo-noedit');
16433 onBeforeLoad : function(combo,opts){
16434 if(!this.hasFocus){
16438 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16440 this.restrictHeight();
16441 this.selectedIndex = -1;
16445 onLoad : function(){
16447 this.hasQuery = false;
16449 if(!this.hasFocus){
16453 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16454 this.loading.hide();
16457 if(this.store.getCount() > 0){
16460 this.restrictHeight();
16461 if(this.lastQuery == this.allQuery){
16462 if(this.editable && !this.tickable){
16463 this.inputEl().dom.select();
16467 !this.selectByValue(this.value, true) &&
16470 !this.store.lastOptions ||
16471 typeof(this.store.lastOptions.add) == 'undefined' ||
16472 this.store.lastOptions.add != true
16475 this.select(0, true);
16478 if(this.autoFocus){
16481 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16482 this.taTask.delay(this.typeAheadDelay);
16486 this.onEmptyResults();
16492 onLoadException : function()
16494 this.hasQuery = false;
16496 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16497 this.loading.hide();
16500 if(this.tickable && this.editable){
16505 // only causes errors at present
16506 //Roo.log(this.store.reader.jsonData);
16507 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16509 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16515 onTypeAhead : function(){
16516 if(this.store.getCount() > 0){
16517 var r = this.store.getAt(0);
16518 var newValue = r.data[this.displayField];
16519 var len = newValue.length;
16520 var selStart = this.getRawValue().length;
16522 if(selStart != len){
16523 this.setRawValue(newValue);
16524 this.selectText(selStart, newValue.length);
16530 onSelect : function(record, index){
16532 if(this.fireEvent('beforeselect', this, record, index) !== false){
16534 this.setFromData(index > -1 ? record.data : false);
16537 this.fireEvent('select', this, record, index);
16542 * Returns the currently selected field value or empty string if no value is set.
16543 * @return {String} value The selected value
16545 getValue : function()
16547 if(Roo.isIOS && this.useNativeIOS){
16548 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16552 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16555 if(this.valueField){
16556 return typeof this.value != 'undefined' ? this.value : '';
16558 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16562 getRawValue : function()
16564 if(Roo.isIOS && this.useNativeIOS){
16565 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16568 var v = this.inputEl().getValue();
16574 * Clears any text/value currently set in the field
16576 clearValue : function(){
16578 if(this.hiddenField){
16579 this.hiddenField.dom.value = '';
16582 this.setRawValue('');
16583 this.lastSelectionText = '';
16584 this.lastData = false;
16586 var close = this.closeTriggerEl();
16597 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16598 * will be displayed in the field. If the value does not match the data value of an existing item,
16599 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16600 * Otherwise the field will be blank (although the value will still be set).
16601 * @param {String} value The value to match
16603 setValue : function(v)
16605 if(Roo.isIOS && this.useNativeIOS){
16606 this.setIOSValue(v);
16616 if(this.valueField){
16617 var r = this.findRecord(this.valueField, v);
16619 text = r.data[this.displayField];
16620 }else if(this.valueNotFoundText !== undefined){
16621 text = this.valueNotFoundText;
16624 this.lastSelectionText = text;
16625 if(this.hiddenField){
16626 this.hiddenField.dom.value = v;
16628 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16631 var close = this.closeTriggerEl();
16634 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16640 * @property {Object} the last set data for the element
16645 * Sets the value of the field based on a object which is related to the record format for the store.
16646 * @param {Object} value the value to set as. or false on reset?
16648 setFromData : function(o){
16655 var dv = ''; // display value
16656 var vv = ''; // value value..
16658 if (this.displayField) {
16659 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16661 // this is an error condition!!!
16662 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16665 if(this.valueField){
16666 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16669 var close = this.closeTriggerEl();
16672 if(dv.length || vv * 1 > 0){
16674 this.blockFocus=true;
16680 if(this.hiddenField){
16681 this.hiddenField.dom.value = vv;
16683 this.lastSelectionText = dv;
16684 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16688 // no hidden field.. - we store the value in 'value', but still display
16689 // display field!!!!
16690 this.lastSelectionText = dv;
16691 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16698 reset : function(){
16699 // overridden so that last data is reset..
16706 this.setValue(this.originalValue);
16707 //this.clearInvalid();
16708 this.lastData = false;
16710 this.view.clearSelections();
16716 findRecord : function(prop, value){
16718 if(this.store.getCount() > 0){
16719 this.store.each(function(r){
16720 if(r.data[prop] == value){
16730 getName: function()
16732 // returns hidden if it's set..
16733 if (!this.rendered) {return ''};
16734 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16738 onViewMove : function(e, t){
16739 this.inKeyMode = false;
16743 onViewOver : function(e, t){
16744 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16747 var item = this.view.findItemFromChild(t);
16750 var index = this.view.indexOf(item);
16751 this.select(index, false);
16756 onViewClick : function(view, doFocus, el, e)
16758 var index = this.view.getSelectedIndexes()[0];
16760 var r = this.store.getAt(index);
16764 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16771 Roo.each(this.tickItems, function(v,k){
16773 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16775 _this.tickItems.splice(k, 1);
16777 if(typeof(e) == 'undefined' && view == false){
16778 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16790 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16791 this.tickItems.push(r.data);
16794 if(typeof(e) == 'undefined' && view == false){
16795 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16802 this.onSelect(r, index);
16804 if(doFocus !== false && !this.blockFocus){
16805 this.inputEl().focus();
16810 restrictHeight : function(){
16811 //this.innerList.dom.style.height = '';
16812 //var inner = this.innerList.dom;
16813 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16814 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16815 //this.list.beginUpdate();
16816 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16817 this.list.alignTo(this.inputEl(), this.listAlign);
16818 this.list.alignTo(this.inputEl(), this.listAlign);
16819 //this.list.endUpdate();
16823 onEmptyResults : function(){
16825 if(this.tickable && this.editable){
16826 this.hasFocus = false;
16827 this.restrictHeight();
16835 * Returns true if the dropdown list is expanded, else false.
16837 isExpanded : function(){
16838 return this.list.isVisible();
16842 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16843 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16844 * @param {String} value The data value of the item to select
16845 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16846 * selected item if it is not currently in view (defaults to true)
16847 * @return {Boolean} True if the value matched an item in the list, else false
16849 selectByValue : function(v, scrollIntoView){
16850 if(v !== undefined && v !== null){
16851 var r = this.findRecord(this.valueField || this.displayField, v);
16853 this.select(this.store.indexOf(r), scrollIntoView);
16861 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16862 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16863 * @param {Number} index The zero-based index of the list item to select
16864 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16865 * selected item if it is not currently in view (defaults to true)
16867 select : function(index, scrollIntoView){
16868 this.selectedIndex = index;
16869 this.view.select(index);
16870 if(scrollIntoView !== false){
16871 var el = this.view.getNode(index);
16873 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16876 this.list.scrollChildIntoView(el, false);
16882 selectNext : function(){
16883 var ct = this.store.getCount();
16885 if(this.selectedIndex == -1){
16887 }else if(this.selectedIndex < ct-1){
16888 this.select(this.selectedIndex+1);
16894 selectPrev : function(){
16895 var ct = this.store.getCount();
16897 if(this.selectedIndex == -1){
16899 }else if(this.selectedIndex != 0){
16900 this.select(this.selectedIndex-1);
16906 onKeyUp : function(e){
16907 if(this.editable !== false && !e.isSpecialKey()){
16908 this.lastKey = e.getKey();
16909 this.dqTask.delay(this.queryDelay);
16914 validateBlur : function(){
16915 return !this.list || !this.list.isVisible();
16919 initQuery : function(){
16921 var v = this.getRawValue();
16923 if(this.tickable && this.editable){
16924 v = this.tickableInputEl().getValue();
16931 doForce : function(){
16932 if(this.inputEl().dom.value.length > 0){
16933 this.inputEl().dom.value =
16934 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16940 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16941 * query allowing the query action to be canceled if needed.
16942 * @param {String} query The SQL query to execute
16943 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16944 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16945 * saved in the current store (defaults to false)
16947 doQuery : function(q, forceAll){
16949 if(q === undefined || q === null){
16954 forceAll: forceAll,
16958 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16963 forceAll = qe.forceAll;
16964 if(forceAll === true || (q.length >= this.minChars)){
16966 this.hasQuery = true;
16968 if(this.lastQuery != q || this.alwaysQuery){
16969 this.lastQuery = q;
16970 if(this.mode == 'local'){
16971 this.selectedIndex = -1;
16973 this.store.clearFilter();
16976 if(this.specialFilter){
16977 this.fireEvent('specialfilter', this);
16982 this.store.filter(this.displayField, q);
16985 this.store.fireEvent("datachanged", this.store);
16992 this.store.baseParams[this.queryParam] = q;
16994 var options = {params : this.getParams(q)};
16997 options.add = true;
16998 options.params.start = this.page * this.pageSize;
17001 this.store.load(options);
17004 * this code will make the page width larger, at the beginning, the list not align correctly,
17005 * we should expand the list on onLoad
17006 * so command out it
17011 this.selectedIndex = -1;
17016 this.loadNext = false;
17020 getParams : function(q){
17022 //p[this.queryParam] = q;
17026 p.limit = this.pageSize;
17032 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17034 collapse : function(){
17035 if(!this.isExpanded()){
17041 this.hasFocus = false;
17045 this.cancelBtn.hide();
17046 this.trigger.show();
17049 this.tickableInputEl().dom.value = '';
17050 this.tickableInputEl().blur();
17055 Roo.get(document).un('mousedown', this.collapseIf, this);
17056 Roo.get(document).un('mousewheel', this.collapseIf, this);
17057 if (!this.editable) {
17058 Roo.get(document).un('keydown', this.listKeyPress, this);
17060 this.fireEvent('collapse', this);
17066 collapseIf : function(e){
17067 var in_combo = e.within(this.el);
17068 var in_list = e.within(this.list);
17069 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17071 if (in_combo || in_list || is_list) {
17072 //e.stopPropagation();
17077 this.onTickableFooterButtonClick(e, false, false);
17085 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17087 expand : function(){
17089 if(this.isExpanded() || !this.hasFocus){
17093 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17094 this.list.setWidth(lw);
17100 this.restrictHeight();
17104 this.tickItems = Roo.apply([], this.item);
17107 this.cancelBtn.show();
17108 this.trigger.hide();
17111 this.tickableInputEl().focus();
17116 Roo.get(document).on('mousedown', this.collapseIf, this);
17117 Roo.get(document).on('mousewheel', this.collapseIf, this);
17118 if (!this.editable) {
17119 Roo.get(document).on('keydown', this.listKeyPress, this);
17122 this.fireEvent('expand', this);
17126 // Implements the default empty TriggerField.onTriggerClick function
17127 onTriggerClick : function(e)
17129 Roo.log('trigger click');
17131 if(this.disabled || !this.triggerList){
17136 this.loadNext = false;
17138 if(this.isExpanded()){
17140 if (!this.blockFocus) {
17141 this.inputEl().focus();
17145 this.hasFocus = true;
17146 if(this.triggerAction == 'all') {
17147 this.doQuery(this.allQuery, true);
17149 this.doQuery(this.getRawValue());
17151 if (!this.blockFocus) {
17152 this.inputEl().focus();
17157 onTickableTriggerClick : function(e)
17164 this.loadNext = false;
17165 this.hasFocus = true;
17167 if(this.triggerAction == 'all') {
17168 this.doQuery(this.allQuery, true);
17170 this.doQuery(this.getRawValue());
17174 onSearchFieldClick : function(e)
17176 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17177 this.onTickableFooterButtonClick(e, false, false);
17181 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17186 this.loadNext = false;
17187 this.hasFocus = true;
17189 if(this.triggerAction == 'all') {
17190 this.doQuery(this.allQuery, true);
17192 this.doQuery(this.getRawValue());
17196 listKeyPress : function(e)
17198 //Roo.log('listkeypress');
17199 // scroll to first matching element based on key pres..
17200 if (e.isSpecialKey()) {
17203 var k = String.fromCharCode(e.getKey()).toUpperCase();
17206 var csel = this.view.getSelectedNodes();
17207 var cselitem = false;
17209 var ix = this.view.indexOf(csel[0]);
17210 cselitem = this.store.getAt(ix);
17211 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17217 this.store.each(function(v) {
17219 // start at existing selection.
17220 if (cselitem.id == v.id) {
17226 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17227 match = this.store.indexOf(v);
17233 if (match === false) {
17234 return true; // no more action?
17237 this.view.select(match);
17238 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17239 sn.scrollIntoView(sn.dom.parentNode, false);
17242 onViewScroll : function(e, t){
17244 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){
17248 this.hasQuery = true;
17250 this.loading = this.list.select('.loading', true).first();
17252 if(this.loading === null){
17253 this.list.createChild({
17255 cls: 'loading roo-select2-more-results roo-select2-active',
17256 html: 'Loading more results...'
17259 this.loading = this.list.select('.loading', true).first();
17261 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17263 this.loading.hide();
17266 this.loading.show();
17271 this.loadNext = true;
17273 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17278 addItem : function(o)
17280 var dv = ''; // display value
17282 if (this.displayField) {
17283 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17285 // this is an error condition!!!
17286 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17293 var choice = this.choices.createChild({
17295 cls: 'roo-select2-search-choice',
17304 cls: 'roo-select2-search-choice-close fa fa-times',
17309 }, this.searchField);
17311 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17313 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17321 this.inputEl().dom.value = '';
17326 onRemoveItem : function(e, _self, o)
17328 e.preventDefault();
17330 this.lastItem = Roo.apply([], this.item);
17332 var index = this.item.indexOf(o.data) * 1;
17335 Roo.log('not this item?!');
17339 this.item.splice(index, 1);
17344 this.fireEvent('remove', this, e);
17350 syncValue : function()
17352 if(!this.item.length){
17359 Roo.each(this.item, function(i){
17360 if(_this.valueField){
17361 value.push(i[_this.valueField]);
17368 this.value = value.join(',');
17370 if(this.hiddenField){
17371 this.hiddenField.dom.value = this.value;
17374 this.store.fireEvent("datachanged", this.store);
17379 clearItem : function()
17381 if(!this.multiple){
17387 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17395 if(this.tickable && !Roo.isTouch){
17396 this.view.refresh();
17400 inputEl: function ()
17402 if(Roo.isIOS && this.useNativeIOS){
17403 return this.el.select('select.roo-ios-select', true).first();
17406 if(Roo.isTouch && this.mobileTouchView){
17407 return this.el.select('input.form-control',true).first();
17411 return this.searchField;
17414 return this.el.select('input.form-control',true).first();
17417 onTickableFooterButtonClick : function(e, btn, el)
17419 e.preventDefault();
17421 this.lastItem = Roo.apply([], this.item);
17423 if(btn && btn.name == 'cancel'){
17424 this.tickItems = Roo.apply([], this.item);
17433 Roo.each(this.tickItems, function(o){
17441 validate : function()
17443 if(this.getVisibilityEl().hasClass('hidden')){
17447 var v = this.getRawValue();
17450 v = this.getValue();
17453 if(this.disabled || this.allowBlank || v.length){
17458 this.markInvalid();
17462 tickableInputEl : function()
17464 if(!this.tickable || !this.editable){
17465 return this.inputEl();
17468 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17472 getAutoCreateTouchView : function()
17477 cls: 'form-group' //input-group
17483 type : this.inputType,
17484 cls : 'form-control x-combo-noedit',
17485 autocomplete: 'new-password',
17486 placeholder : this.placeholder || '',
17491 input.name = this.name;
17495 input.cls += ' input-' + this.size;
17498 if (this.disabled) {
17499 input.disabled = true;
17503 cls : 'roo-combobox-wrap',
17510 inputblock.cls += ' input-group';
17512 inputblock.cn.unshift({
17514 cls : 'input-group-addon input-group-prepend input-group-text',
17519 if(this.removable && !this.multiple){
17520 inputblock.cls += ' roo-removable';
17522 inputblock.cn.push({
17525 cls : 'roo-combo-removable-btn close'
17529 if(this.hasFeedback && !this.allowBlank){
17531 inputblock.cls += ' has-feedback';
17533 inputblock.cn.push({
17535 cls: 'glyphicon form-control-feedback'
17542 inputblock.cls += (this.before) ? '' : ' input-group';
17544 inputblock.cn.push({
17546 cls : 'input-group-addon input-group-append input-group-text',
17552 var ibwrap = inputblock;
17557 cls: 'roo-select2-choices',
17561 cls: 'roo-select2-search-field',
17574 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17579 cls: 'form-hidden-field'
17585 if(!this.multiple && this.showToggleBtn){
17591 if (this.caret != false) {
17594 cls: 'fa fa-' + this.caret
17601 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17603 Roo.bootstrap.version == 3 ? caret : '',
17606 cls: 'combobox-clear',
17620 combobox.cls += ' roo-select2-container-multi';
17623 var required = this.allowBlank ? {
17625 style: 'display: none'
17628 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17629 tooltip : 'This field is required'
17632 var align = this.labelAlign || this.parentLabelAlign();
17634 if (align ==='left' && this.fieldLabel.length) {
17640 cls : 'control-label col-form-label',
17641 html : this.fieldLabel
17645 cls : 'roo-combobox-wrap ',
17652 var labelCfg = cfg.cn[1];
17653 var contentCfg = cfg.cn[2];
17656 if(this.indicatorpos == 'right'){
17661 cls : 'control-label col-form-label',
17665 html : this.fieldLabel
17671 cls : "roo-combobox-wrap ",
17679 labelCfg = cfg.cn[0];
17680 contentCfg = cfg.cn[1];
17685 if(this.labelWidth > 12){
17686 labelCfg.style = "width: " + this.labelWidth + 'px';
17689 if(this.labelWidth < 13 && this.labelmd == 0){
17690 this.labelmd = this.labelWidth;
17693 if(this.labellg > 0){
17694 labelCfg.cls += ' col-lg-' + this.labellg;
17695 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17698 if(this.labelmd > 0){
17699 labelCfg.cls += ' col-md-' + this.labelmd;
17700 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17703 if(this.labelsm > 0){
17704 labelCfg.cls += ' col-sm-' + this.labelsm;
17705 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17708 if(this.labelxs > 0){
17709 labelCfg.cls += ' col-xs-' + this.labelxs;
17710 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17714 } else if ( this.fieldLabel.length) {
17719 cls : 'control-label',
17720 html : this.fieldLabel
17731 if(this.indicatorpos == 'right'){
17735 cls : 'control-label',
17736 html : this.fieldLabel,
17754 var settings = this;
17756 ['xs','sm','md','lg'].map(function(size){
17757 if (settings[size]) {
17758 cfg.cls += ' col-' + size + '-' + settings[size];
17765 initTouchView : function()
17767 this.renderTouchView();
17769 this.touchViewEl.on('scroll', function(){
17770 this.el.dom.scrollTop = 0;
17773 this.originalValue = this.getValue();
17775 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17777 this.inputEl().on("click", this.showTouchView, this);
17778 if (this.triggerEl) {
17779 this.triggerEl.on("click", this.showTouchView, this);
17783 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17784 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17786 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17788 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17789 this.store.on('load', this.onTouchViewLoad, this);
17790 this.store.on('loadexception', this.onTouchViewLoadException, this);
17792 if(this.hiddenName){
17794 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17796 this.hiddenField.dom.value =
17797 this.hiddenValue !== undefined ? this.hiddenValue :
17798 this.value !== undefined ? this.value : '';
17800 this.el.dom.removeAttribute('name');
17801 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17805 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17806 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17809 if(this.removable && !this.multiple){
17810 var close = this.closeTriggerEl();
17812 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17813 close.on('click', this.removeBtnClick, this, close);
17817 * fix the bug in Safari iOS8
17819 this.inputEl().on("focus", function(e){
17820 document.activeElement.blur();
17823 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17830 renderTouchView : function()
17832 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17833 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17835 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17836 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17838 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17839 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17840 this.touchViewBodyEl.setStyle('overflow', 'auto');
17842 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17843 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17845 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17846 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17850 showTouchView : function()
17856 this.touchViewHeaderEl.hide();
17858 if(this.modalTitle.length){
17859 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17860 this.touchViewHeaderEl.show();
17863 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17864 this.touchViewEl.show();
17866 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17868 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17869 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17871 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17873 if(this.modalTitle.length){
17874 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17877 this.touchViewBodyEl.setHeight(bodyHeight);
17881 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17883 this.touchViewEl.addClass(['in','show']);
17886 if(this._touchViewMask){
17887 Roo.get(document.body).addClass("x-body-masked");
17888 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17889 this._touchViewMask.setStyle('z-index', 10000);
17890 this._touchViewMask.addClass('show');
17893 this.doTouchViewQuery();
17897 hideTouchView : function()
17899 this.touchViewEl.removeClass(['in','show']);
17903 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17905 this.touchViewEl.setStyle('display', 'none');
17908 if(this._touchViewMask){
17909 this._touchViewMask.removeClass('show');
17910 Roo.get(document.body).removeClass("x-body-masked");
17914 setTouchViewValue : function()
17921 Roo.each(this.tickItems, function(o){
17926 this.hideTouchView();
17929 doTouchViewQuery : function()
17938 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17942 if(!this.alwaysQuery || this.mode == 'local'){
17943 this.onTouchViewLoad();
17950 onTouchViewBeforeLoad : function(combo,opts)
17956 onTouchViewLoad : function()
17958 if(this.store.getCount() < 1){
17959 this.onTouchViewEmptyResults();
17963 this.clearTouchView();
17965 var rawValue = this.getRawValue();
17967 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17969 this.tickItems = [];
17971 this.store.data.each(function(d, rowIndex){
17972 var row = this.touchViewListGroup.createChild(template);
17974 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17975 row.addClass(d.data.cls);
17978 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17981 html : d.data[this.displayField]
17984 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17985 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17988 row.removeClass('selected');
17989 if(!this.multiple && this.valueField &&
17990 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17993 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17994 row.addClass('selected');
17997 if(this.multiple && this.valueField &&
17998 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18002 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18003 this.tickItems.push(d.data);
18006 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18010 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18012 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18014 if(this.modalTitle.length){
18015 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18018 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18020 if(this.mobile_restrict_height && listHeight < bodyHeight){
18021 this.touchViewBodyEl.setHeight(listHeight);
18026 if(firstChecked && listHeight > bodyHeight){
18027 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18032 onTouchViewLoadException : function()
18034 this.hideTouchView();
18037 onTouchViewEmptyResults : function()
18039 this.clearTouchView();
18041 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18043 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18047 clearTouchView : function()
18049 this.touchViewListGroup.dom.innerHTML = '';
18052 onTouchViewClick : function(e, el, o)
18054 e.preventDefault();
18057 var rowIndex = o.rowIndex;
18059 var r = this.store.getAt(rowIndex);
18061 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18063 if(!this.multiple){
18064 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18065 c.dom.removeAttribute('checked');
18068 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18070 this.setFromData(r.data);
18072 var close = this.closeTriggerEl();
18078 this.hideTouchView();
18080 this.fireEvent('select', this, r, rowIndex);
18085 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18086 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18087 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18091 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18092 this.addItem(r.data);
18093 this.tickItems.push(r.data);
18097 getAutoCreateNativeIOS : function()
18100 cls: 'form-group' //input-group,
18105 cls : 'roo-ios-select'
18109 combobox.name = this.name;
18112 if (this.disabled) {
18113 combobox.disabled = true;
18116 var settings = this;
18118 ['xs','sm','md','lg'].map(function(size){
18119 if (settings[size]) {
18120 cfg.cls += ' col-' + size + '-' + settings[size];
18130 initIOSView : function()
18132 this.store.on('load', this.onIOSViewLoad, this);
18137 onIOSViewLoad : function()
18139 if(this.store.getCount() < 1){
18143 this.clearIOSView();
18145 if(this.allowBlank) {
18147 var default_text = '-- SELECT --';
18149 if(this.placeholder.length){
18150 default_text = this.placeholder;
18153 if(this.emptyTitle.length){
18154 default_text += ' - ' + this.emptyTitle + ' -';
18157 var opt = this.inputEl().createChild({
18160 html : default_text
18164 o[this.valueField] = 0;
18165 o[this.displayField] = default_text;
18167 this.ios_options.push({
18174 this.store.data.each(function(d, rowIndex){
18178 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18179 html = d.data[this.displayField];
18184 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18185 value = d.data[this.valueField];
18194 if(this.value == d.data[this.valueField]){
18195 option['selected'] = true;
18198 var opt = this.inputEl().createChild(option);
18200 this.ios_options.push({
18207 this.inputEl().on('change', function(){
18208 this.fireEvent('select', this);
18213 clearIOSView: function()
18215 this.inputEl().dom.innerHTML = '';
18217 this.ios_options = [];
18220 setIOSValue: function(v)
18224 if(!this.ios_options){
18228 Roo.each(this.ios_options, function(opts){
18230 opts.el.dom.removeAttribute('selected');
18232 if(opts.data[this.valueField] != v){
18236 opts.el.dom.setAttribute('selected', true);
18242 * @cfg {Boolean} grow
18246 * @cfg {Number} growMin
18250 * @cfg {Number} growMax
18259 Roo.apply(Roo.bootstrap.ComboBox, {
18263 cls: 'modal-header',
18285 cls: 'list-group-item',
18289 cls: 'roo-combobox-list-group-item-value'
18293 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18307 listItemCheckbox : {
18309 cls: 'list-group-item',
18313 cls: 'roo-combobox-list-group-item-value'
18317 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18333 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18338 cls: 'modal-footer',
18346 cls: 'col-xs-6 text-left',
18349 cls: 'btn btn-danger roo-touch-view-cancel',
18355 cls: 'col-xs-6 text-right',
18358 cls: 'btn btn-success roo-touch-view-ok',
18369 Roo.apply(Roo.bootstrap.ComboBox, {
18371 touchViewTemplate : {
18373 cls: 'modal fade roo-combobox-touch-view',
18377 cls: 'modal-dialog',
18378 style : 'position:fixed', // we have to fix position....
18382 cls: 'modal-content',
18384 Roo.bootstrap.ComboBox.header,
18385 Roo.bootstrap.ComboBox.body,
18386 Roo.bootstrap.ComboBox.footer
18395 * Ext JS Library 1.1.1
18396 * Copyright(c) 2006-2007, Ext JS, LLC.
18398 * Originally Released Under LGPL - original licence link has changed is not relivant.
18401 * <script type="text/javascript">
18406 * @extends Roo.util.Observable
18407 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18408 * This class also supports single and multi selection modes. <br>
18409 * Create a data model bound view:
18411 var store = new Roo.data.Store(...);
18413 var view = new Roo.View({
18415 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18417 singleSelect: true,
18418 selectedClass: "ydataview-selected",
18422 // listen for node click?
18423 view.on("click", function(vw, index, node, e){
18424 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18428 dataModel.load("foobar.xml");
18430 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18432 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18433 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18435 * Note: old style constructor is still suported (container, template, config)
18438 * Create a new View
18439 * @param {Object} config The config object
18442 Roo.View = function(config, depreciated_tpl, depreciated_config){
18444 this.parent = false;
18446 if (typeof(depreciated_tpl) == 'undefined') {
18447 // new way.. - universal constructor.
18448 Roo.apply(this, config);
18449 this.el = Roo.get(this.el);
18452 this.el = Roo.get(config);
18453 this.tpl = depreciated_tpl;
18454 Roo.apply(this, depreciated_config);
18456 this.wrapEl = this.el.wrap().wrap();
18457 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18460 if(typeof(this.tpl) == "string"){
18461 this.tpl = new Roo.Template(this.tpl);
18463 // support xtype ctors..
18464 this.tpl = new Roo.factory(this.tpl, Roo);
18468 this.tpl.compile();
18473 * @event beforeclick
18474 * Fires before a click is processed. Returns false to cancel the default action.
18475 * @param {Roo.View} this
18476 * @param {Number} index The index of the target node
18477 * @param {HTMLElement} node The target node
18478 * @param {Roo.EventObject} e The raw event object
18480 "beforeclick" : true,
18483 * Fires when a template node is clicked.
18484 * @param {Roo.View} this
18485 * @param {Number} index The index of the target node
18486 * @param {HTMLElement} node The target node
18487 * @param {Roo.EventObject} e The raw event object
18492 * Fires when a template node is double clicked.
18493 * @param {Roo.View} this
18494 * @param {Number} index The index of the target node
18495 * @param {HTMLElement} node The target node
18496 * @param {Roo.EventObject} e The raw event object
18500 * @event contextmenu
18501 * Fires when a template node is right clicked.
18502 * @param {Roo.View} this
18503 * @param {Number} index The index of the target node
18504 * @param {HTMLElement} node The target node
18505 * @param {Roo.EventObject} e The raw event object
18507 "contextmenu" : true,
18509 * @event selectionchange
18510 * Fires when the selected nodes change.
18511 * @param {Roo.View} this
18512 * @param {Array} selections Array of the selected nodes
18514 "selectionchange" : true,
18517 * @event beforeselect
18518 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18519 * @param {Roo.View} this
18520 * @param {HTMLElement} node The node to be selected
18521 * @param {Array} selections Array of currently selected nodes
18523 "beforeselect" : true,
18525 * @event preparedata
18526 * Fires on every row to render, to allow you to change the data.
18527 * @param {Roo.View} this
18528 * @param {Object} data to be rendered (change this)
18530 "preparedata" : true
18538 "click": this.onClick,
18539 "dblclick": this.onDblClick,
18540 "contextmenu": this.onContextMenu,
18544 this.selections = [];
18546 this.cmp = new Roo.CompositeElementLite([]);
18548 this.store = Roo.factory(this.store, Roo.data);
18549 this.setStore(this.store, true);
18552 if ( this.footer && this.footer.xtype) {
18554 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18556 this.footer.dataSource = this.store;
18557 this.footer.container = fctr;
18558 this.footer = Roo.factory(this.footer, Roo);
18559 fctr.insertFirst(this.el);
18561 // this is a bit insane - as the paging toolbar seems to detach the el..
18562 // dom.parentNode.parentNode.parentNode
18563 // they get detached?
18567 Roo.View.superclass.constructor.call(this);
18572 Roo.extend(Roo.View, Roo.util.Observable, {
18575 * @cfg {Roo.data.Store} store Data store to load data from.
18580 * @cfg {String|Roo.Element} el The container element.
18585 * @cfg {String|Roo.Template} tpl The template used by this View
18589 * @cfg {String} dataName the named area of the template to use as the data area
18590 * Works with domtemplates roo-name="name"
18594 * @cfg {String} selectedClass The css class to add to selected nodes
18596 selectedClass : "x-view-selected",
18598 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18603 * @cfg {String} text to display on mask (default Loading)
18607 * @cfg {Boolean} multiSelect Allow multiple selection
18609 multiSelect : false,
18611 * @cfg {Boolean} singleSelect Allow single selection
18613 singleSelect: false,
18616 * @cfg {Boolean} toggleSelect - selecting
18618 toggleSelect : false,
18621 * @cfg {Boolean} tickable - selecting
18626 * Returns the element this view is bound to.
18627 * @return {Roo.Element}
18629 getEl : function(){
18630 return this.wrapEl;
18636 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18638 refresh : function(){
18639 //Roo.log('refresh');
18642 // if we are using something like 'domtemplate', then
18643 // the what gets used is:
18644 // t.applySubtemplate(NAME, data, wrapping data..)
18645 // the outer template then get' applied with
18646 // the store 'extra data'
18647 // and the body get's added to the
18648 // roo-name="data" node?
18649 // <span class='roo-tpl-{name}'></span> ?????
18653 this.clearSelections();
18654 this.el.update("");
18656 var records = this.store.getRange();
18657 if(records.length < 1) {
18659 // is this valid?? = should it render a template??
18661 this.el.update(this.emptyText);
18665 if (this.dataName) {
18666 this.el.update(t.apply(this.store.meta)); //????
18667 el = this.el.child('.roo-tpl-' + this.dataName);
18670 for(var i = 0, len = records.length; i < len; i++){
18671 var data = this.prepareData(records[i].data, i, records[i]);
18672 this.fireEvent("preparedata", this, data, i, records[i]);
18674 var d = Roo.apply({}, data);
18677 Roo.apply(d, {'roo-id' : Roo.id()});
18681 Roo.each(this.parent.item, function(item){
18682 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18685 Roo.apply(d, {'roo-data-checked' : 'checked'});
18689 html[html.length] = Roo.util.Format.trim(
18691 t.applySubtemplate(this.dataName, d, this.store.meta) :
18698 el.update(html.join(""));
18699 this.nodes = el.dom.childNodes;
18700 this.updateIndexes(0);
18705 * Function to override to reformat the data that is sent to
18706 * the template for each node.
18707 * DEPRICATED - use the preparedata event handler.
18708 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18709 * a JSON object for an UpdateManager bound view).
18711 prepareData : function(data, index, record)
18713 this.fireEvent("preparedata", this, data, index, record);
18717 onUpdate : function(ds, record){
18718 // Roo.log('on update');
18719 this.clearSelections();
18720 var index = this.store.indexOf(record);
18721 var n = this.nodes[index];
18722 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18723 n.parentNode.removeChild(n);
18724 this.updateIndexes(index, index);
18730 onAdd : function(ds, records, index)
18732 //Roo.log(['on Add', ds, records, index] );
18733 this.clearSelections();
18734 if(this.nodes.length == 0){
18738 var n = this.nodes[index];
18739 for(var i = 0, len = records.length; i < len; i++){
18740 var d = this.prepareData(records[i].data, i, records[i]);
18742 this.tpl.insertBefore(n, d);
18745 this.tpl.append(this.el, d);
18748 this.updateIndexes(index);
18751 onRemove : function(ds, record, index){
18752 // Roo.log('onRemove');
18753 this.clearSelections();
18754 var el = this.dataName ?
18755 this.el.child('.roo-tpl-' + this.dataName) :
18758 el.dom.removeChild(this.nodes[index]);
18759 this.updateIndexes(index);
18763 * Refresh an individual node.
18764 * @param {Number} index
18766 refreshNode : function(index){
18767 this.onUpdate(this.store, this.store.getAt(index));
18770 updateIndexes : function(startIndex, endIndex){
18771 var ns = this.nodes;
18772 startIndex = startIndex || 0;
18773 endIndex = endIndex || ns.length - 1;
18774 for(var i = startIndex; i <= endIndex; i++){
18775 ns[i].nodeIndex = i;
18780 * Changes the data store this view uses and refresh the view.
18781 * @param {Store} store
18783 setStore : function(store, initial){
18784 if(!initial && this.store){
18785 this.store.un("datachanged", this.refresh);
18786 this.store.un("add", this.onAdd);
18787 this.store.un("remove", this.onRemove);
18788 this.store.un("update", this.onUpdate);
18789 this.store.un("clear", this.refresh);
18790 this.store.un("beforeload", this.onBeforeLoad);
18791 this.store.un("load", this.onLoad);
18792 this.store.un("loadexception", this.onLoad);
18796 store.on("datachanged", this.refresh, this);
18797 store.on("add", this.onAdd, this);
18798 store.on("remove", this.onRemove, this);
18799 store.on("update", this.onUpdate, this);
18800 store.on("clear", this.refresh, this);
18801 store.on("beforeload", this.onBeforeLoad, this);
18802 store.on("load", this.onLoad, this);
18803 store.on("loadexception", this.onLoad, this);
18811 * onbeforeLoad - masks the loading area.
18814 onBeforeLoad : function(store,opts)
18816 //Roo.log('onBeforeLoad');
18818 this.el.update("");
18820 this.el.mask(this.mask ? this.mask : "Loading" );
18822 onLoad : function ()
18829 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18830 * @param {HTMLElement} node
18831 * @return {HTMLElement} The template node
18833 findItemFromChild : function(node){
18834 var el = this.dataName ?
18835 this.el.child('.roo-tpl-' + this.dataName,true) :
18838 if(!node || node.parentNode == el){
18841 var p = node.parentNode;
18842 while(p && p != el){
18843 if(p.parentNode == el){
18852 onClick : function(e){
18853 var item = this.findItemFromChild(e.getTarget());
18855 var index = this.indexOf(item);
18856 if(this.onItemClick(item, index, e) !== false){
18857 this.fireEvent("click", this, index, item, e);
18860 this.clearSelections();
18865 onContextMenu : function(e){
18866 var item = this.findItemFromChild(e.getTarget());
18868 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18873 onDblClick : function(e){
18874 var item = this.findItemFromChild(e.getTarget());
18876 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18880 onItemClick : function(item, index, e)
18882 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18885 if (this.toggleSelect) {
18886 var m = this.isSelected(item) ? 'unselect' : 'select';
18889 _t[m](item, true, false);
18892 if(this.multiSelect || this.singleSelect){
18893 if(this.multiSelect && e.shiftKey && this.lastSelection){
18894 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18896 this.select(item, this.multiSelect && e.ctrlKey);
18897 this.lastSelection = item;
18900 if(!this.tickable){
18901 e.preventDefault();
18909 * Get the number of selected nodes.
18912 getSelectionCount : function(){
18913 return this.selections.length;
18917 * Get the currently selected nodes.
18918 * @return {Array} An array of HTMLElements
18920 getSelectedNodes : function(){
18921 return this.selections;
18925 * Get the indexes of the selected nodes.
18928 getSelectedIndexes : function(){
18929 var indexes = [], s = this.selections;
18930 for(var i = 0, len = s.length; i < len; i++){
18931 indexes.push(s[i].nodeIndex);
18937 * Clear all selections
18938 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18940 clearSelections : function(suppressEvent){
18941 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18942 this.cmp.elements = this.selections;
18943 this.cmp.removeClass(this.selectedClass);
18944 this.selections = [];
18945 if(!suppressEvent){
18946 this.fireEvent("selectionchange", this, this.selections);
18952 * Returns true if the passed node is selected
18953 * @param {HTMLElement/Number} node The node or node index
18954 * @return {Boolean}
18956 isSelected : function(node){
18957 var s = this.selections;
18961 node = this.getNode(node);
18962 return s.indexOf(node) !== -1;
18967 * @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
18968 * @param {Boolean} keepExisting (optional) true to keep existing selections
18969 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18971 select : function(nodeInfo, keepExisting, suppressEvent){
18972 if(nodeInfo instanceof Array){
18974 this.clearSelections(true);
18976 for(var i = 0, len = nodeInfo.length; i < len; i++){
18977 this.select(nodeInfo[i], true, true);
18981 var node = this.getNode(nodeInfo);
18982 if(!node || this.isSelected(node)){
18983 return; // already selected.
18986 this.clearSelections(true);
18989 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18990 Roo.fly(node).addClass(this.selectedClass);
18991 this.selections.push(node);
18992 if(!suppressEvent){
18993 this.fireEvent("selectionchange", this, this.selections);
19001 * @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
19002 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19003 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19005 unselect : function(nodeInfo, keepExisting, suppressEvent)
19007 if(nodeInfo instanceof Array){
19008 Roo.each(this.selections, function(s) {
19009 this.unselect(s, nodeInfo);
19013 var node = this.getNode(nodeInfo);
19014 if(!node || !this.isSelected(node)){
19015 //Roo.log("not selected");
19016 return; // not selected.
19020 Roo.each(this.selections, function(s) {
19022 Roo.fly(node).removeClass(this.selectedClass);
19029 this.selections= ns;
19030 this.fireEvent("selectionchange", this, this.selections);
19034 * Gets a template node.
19035 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19036 * @return {HTMLElement} The node or null if it wasn't found
19038 getNode : function(nodeInfo){
19039 if(typeof nodeInfo == "string"){
19040 return document.getElementById(nodeInfo);
19041 }else if(typeof nodeInfo == "number"){
19042 return this.nodes[nodeInfo];
19048 * Gets a range template nodes.
19049 * @param {Number} startIndex
19050 * @param {Number} endIndex
19051 * @return {Array} An array of nodes
19053 getNodes : function(start, end){
19054 var ns = this.nodes;
19055 start = start || 0;
19056 end = typeof end == "undefined" ? ns.length - 1 : end;
19059 for(var i = start; i <= end; i++){
19063 for(var i = start; i >= end; i--){
19071 * Finds the index of the passed node
19072 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19073 * @return {Number} The index of the node or -1
19075 indexOf : function(node){
19076 node = this.getNode(node);
19077 if(typeof node.nodeIndex == "number"){
19078 return node.nodeIndex;
19080 var ns = this.nodes;
19081 for(var i = 0, len = ns.length; i < len; i++){
19092 * based on jquery fullcalendar
19096 Roo.bootstrap = Roo.bootstrap || {};
19098 * @class Roo.bootstrap.Calendar
19099 * @extends Roo.bootstrap.Component
19100 * Bootstrap Calendar class
19101 * @cfg {Boolean} loadMask (true|false) default false
19102 * @cfg {Object} header generate the user specific header of the calendar, default false
19105 * Create a new Container
19106 * @param {Object} config The config object
19111 Roo.bootstrap.Calendar = function(config){
19112 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19116 * Fires when a date is selected
19117 * @param {DatePicker} this
19118 * @param {Date} date The selected date
19122 * @event monthchange
19123 * Fires when the displayed month changes
19124 * @param {DatePicker} this
19125 * @param {Date} date The selected month
19127 'monthchange': true,
19129 * @event evententer
19130 * Fires when mouse over an event
19131 * @param {Calendar} this
19132 * @param {event} Event
19134 'evententer': true,
19136 * @event eventleave
19137 * Fires when the mouse leaves an
19138 * @param {Calendar} this
19141 'eventleave': true,
19143 * @event eventclick
19144 * Fires when the mouse click an
19145 * @param {Calendar} this
19154 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19157 * @cfg {Number} startDay
19158 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19166 getAutoCreate : function(){
19169 var fc_button = function(name, corner, style, content ) {
19170 return Roo.apply({},{
19172 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19174 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19177 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19188 style : 'width:100%',
19195 cls : 'fc-header-left',
19197 fc_button('prev', 'left', 'arrow', '‹' ),
19198 fc_button('next', 'right', 'arrow', '›' ),
19199 { tag: 'span', cls: 'fc-header-space' },
19200 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19208 cls : 'fc-header-center',
19212 cls: 'fc-header-title',
19215 html : 'month / year'
19223 cls : 'fc-header-right',
19225 /* fc_button('month', 'left', '', 'month' ),
19226 fc_button('week', '', '', 'week' ),
19227 fc_button('day', 'right', '', 'day' )
19239 header = this.header;
19242 var cal_heads = function() {
19244 // fixme - handle this.
19246 for (var i =0; i < Date.dayNames.length; i++) {
19247 var d = Date.dayNames[i];
19250 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19251 html : d.substring(0,3)
19255 ret[0].cls += ' fc-first';
19256 ret[6].cls += ' fc-last';
19259 var cal_cell = function(n) {
19262 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19267 cls: 'fc-day-number',
19271 cls: 'fc-day-content',
19275 style: 'position: relative;' // height: 17px;
19287 var cal_rows = function() {
19290 for (var r = 0; r < 6; r++) {
19297 for (var i =0; i < Date.dayNames.length; i++) {
19298 var d = Date.dayNames[i];
19299 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19302 row.cn[0].cls+=' fc-first';
19303 row.cn[0].cn[0].style = 'min-height:90px';
19304 row.cn[6].cls+=' fc-last';
19308 ret[0].cls += ' fc-first';
19309 ret[4].cls += ' fc-prev-last';
19310 ret[5].cls += ' fc-last';
19317 cls: 'fc-border-separate',
19318 style : 'width:100%',
19326 cls : 'fc-first fc-last',
19344 cls : 'fc-content',
19345 style : "position: relative;",
19348 cls : 'fc-view fc-view-month fc-grid',
19349 style : 'position: relative',
19350 unselectable : 'on',
19353 cls : 'fc-event-container',
19354 style : 'position:absolute;z-index:8;top:0;left:0;'
19372 initEvents : function()
19375 throw "can not find store for calendar";
19381 style: "text-align:center",
19385 style: "background-color:white;width:50%;margin:250 auto",
19389 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19400 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19402 var size = this.el.select('.fc-content', true).first().getSize();
19403 this.maskEl.setSize(size.width, size.height);
19404 this.maskEl.enableDisplayMode("block");
19405 if(!this.loadMask){
19406 this.maskEl.hide();
19409 this.store = Roo.factory(this.store, Roo.data);
19410 this.store.on('load', this.onLoad, this);
19411 this.store.on('beforeload', this.onBeforeLoad, this);
19415 this.cells = this.el.select('.fc-day',true);
19416 //Roo.log(this.cells);
19417 this.textNodes = this.el.query('.fc-day-number');
19418 this.cells.addClassOnOver('fc-state-hover');
19420 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19421 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19422 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19423 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19425 this.on('monthchange', this.onMonthChange, this);
19427 this.update(new Date().clearTime());
19430 resize : function() {
19431 var sz = this.el.getSize();
19433 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19434 this.el.select('.fc-day-content div',true).setHeight(34);
19439 showPrevMonth : function(e){
19440 this.update(this.activeDate.add("mo", -1));
19442 showToday : function(e){
19443 this.update(new Date().clearTime());
19446 showNextMonth : function(e){
19447 this.update(this.activeDate.add("mo", 1));
19451 showPrevYear : function(){
19452 this.update(this.activeDate.add("y", -1));
19456 showNextYear : function(){
19457 this.update(this.activeDate.add("y", 1));
19462 update : function(date)
19464 var vd = this.activeDate;
19465 this.activeDate = date;
19466 // if(vd && this.el){
19467 // var t = date.getTime();
19468 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19469 // Roo.log('using add remove');
19471 // this.fireEvent('monthchange', this, date);
19473 // this.cells.removeClass("fc-state-highlight");
19474 // this.cells.each(function(c){
19475 // if(c.dateValue == t){
19476 // c.addClass("fc-state-highlight");
19477 // setTimeout(function(){
19478 // try{c.dom.firstChild.focus();}catch(e){}
19488 var days = date.getDaysInMonth();
19490 var firstOfMonth = date.getFirstDateOfMonth();
19491 var startingPos = firstOfMonth.getDay()-this.startDay;
19493 if(startingPos < this.startDay){
19497 var pm = date.add(Date.MONTH, -1);
19498 var prevStart = pm.getDaysInMonth()-startingPos;
19500 this.cells = this.el.select('.fc-day',true);
19501 this.textNodes = this.el.query('.fc-day-number');
19502 this.cells.addClassOnOver('fc-state-hover');
19504 var cells = this.cells.elements;
19505 var textEls = this.textNodes;
19507 Roo.each(cells, function(cell){
19508 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19511 days += startingPos;
19513 // convert everything to numbers so it's fast
19514 var day = 86400000;
19515 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19518 //Roo.log(prevStart);
19520 var today = new Date().clearTime().getTime();
19521 var sel = date.clearTime().getTime();
19522 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19523 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19524 var ddMatch = this.disabledDatesRE;
19525 var ddText = this.disabledDatesText;
19526 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19527 var ddaysText = this.disabledDaysText;
19528 var format = this.format;
19530 var setCellClass = function(cal, cell){
19534 //Roo.log('set Cell Class');
19536 var t = d.getTime();
19540 cell.dateValue = t;
19542 cell.className += " fc-today";
19543 cell.className += " fc-state-highlight";
19544 cell.title = cal.todayText;
19547 // disable highlight in other month..
19548 //cell.className += " fc-state-highlight";
19553 cell.className = " fc-state-disabled";
19554 cell.title = cal.minText;
19558 cell.className = " fc-state-disabled";
19559 cell.title = cal.maxText;
19563 if(ddays.indexOf(d.getDay()) != -1){
19564 cell.title = ddaysText;
19565 cell.className = " fc-state-disabled";
19568 if(ddMatch && format){
19569 var fvalue = d.dateFormat(format);
19570 if(ddMatch.test(fvalue)){
19571 cell.title = ddText.replace("%0", fvalue);
19572 cell.className = " fc-state-disabled";
19576 if (!cell.initialClassName) {
19577 cell.initialClassName = cell.dom.className;
19580 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19585 for(; i < startingPos; i++) {
19586 textEls[i].innerHTML = (++prevStart);
19587 d.setDate(d.getDate()+1);
19589 cells[i].className = "fc-past fc-other-month";
19590 setCellClass(this, cells[i]);
19595 for(; i < days; i++){
19596 intDay = i - startingPos + 1;
19597 textEls[i].innerHTML = (intDay);
19598 d.setDate(d.getDate()+1);
19600 cells[i].className = ''; // "x-date-active";
19601 setCellClass(this, cells[i]);
19605 for(; i < 42; i++) {
19606 textEls[i].innerHTML = (++extraDays);
19607 d.setDate(d.getDate()+1);
19609 cells[i].className = "fc-future fc-other-month";
19610 setCellClass(this, cells[i]);
19613 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19615 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19617 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19618 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19620 if(totalRows != 6){
19621 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19622 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19625 this.fireEvent('monthchange', this, date);
19629 if(!this.internalRender){
19630 var main = this.el.dom.firstChild;
19631 var w = main.offsetWidth;
19632 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19633 Roo.fly(main).setWidth(w);
19634 this.internalRender = true;
19635 // opera does not respect the auto grow header center column
19636 // then, after it gets a width opera refuses to recalculate
19637 // without a second pass
19638 if(Roo.isOpera && !this.secondPass){
19639 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19640 this.secondPass = true;
19641 this.update.defer(10, this, [date]);
19648 findCell : function(dt) {
19649 dt = dt.clearTime().getTime();
19651 this.cells.each(function(c){
19652 //Roo.log("check " +c.dateValue + '?=' + dt);
19653 if(c.dateValue == dt){
19663 findCells : function(ev) {
19664 var s = ev.start.clone().clearTime().getTime();
19666 var e= ev.end.clone().clearTime().getTime();
19669 this.cells.each(function(c){
19670 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19672 if(c.dateValue > e){
19675 if(c.dateValue < s){
19684 // findBestRow: function(cells)
19688 // for (var i =0 ; i < cells.length;i++) {
19689 // ret = Math.max(cells[i].rows || 0,ret);
19696 addItem : function(ev)
19698 // look for vertical location slot in
19699 var cells = this.findCells(ev);
19701 // ev.row = this.findBestRow(cells);
19703 // work out the location.
19707 for(var i =0; i < cells.length; i++) {
19709 cells[i].row = cells[0].row;
19712 cells[i].row = cells[i].row + 1;
19722 if (crow.start.getY() == cells[i].getY()) {
19724 crow.end = cells[i];
19741 cells[0].events.push(ev);
19743 this.calevents.push(ev);
19746 clearEvents: function() {
19748 if(!this.calevents){
19752 Roo.each(this.cells.elements, function(c){
19758 Roo.each(this.calevents, function(e) {
19759 Roo.each(e.els, function(el) {
19760 el.un('mouseenter' ,this.onEventEnter, this);
19761 el.un('mouseleave' ,this.onEventLeave, this);
19766 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19772 renderEvents: function()
19776 this.cells.each(function(c) {
19785 if(c.row != c.events.length){
19786 r = 4 - (4 - (c.row - c.events.length));
19789 c.events = ev.slice(0, r);
19790 c.more = ev.slice(r);
19792 if(c.more.length && c.more.length == 1){
19793 c.events.push(c.more.pop());
19796 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19800 this.cells.each(function(c) {
19802 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19805 for (var e = 0; e < c.events.length; e++){
19806 var ev = c.events[e];
19807 var rows = ev.rows;
19809 for(var i = 0; i < rows.length; i++) {
19811 // how many rows should it span..
19814 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19815 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19817 unselectable : "on",
19820 cls: 'fc-event-inner',
19824 // cls: 'fc-event-time',
19825 // html : cells.length > 1 ? '' : ev.time
19829 cls: 'fc-event-title',
19830 html : String.format('{0}', ev.title)
19837 cls: 'ui-resizable-handle ui-resizable-e',
19838 html : '  '
19845 cfg.cls += ' fc-event-start';
19847 if ((i+1) == rows.length) {
19848 cfg.cls += ' fc-event-end';
19851 var ctr = _this.el.select('.fc-event-container',true).first();
19852 var cg = ctr.createChild(cfg);
19854 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19855 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19857 var r = (c.more.length) ? 1 : 0;
19858 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19859 cg.setWidth(ebox.right - sbox.x -2);
19861 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19862 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19863 cg.on('click', _this.onEventClick, _this, ev);
19874 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19875 style : 'position: absolute',
19876 unselectable : "on",
19879 cls: 'fc-event-inner',
19883 cls: 'fc-event-title',
19891 cls: 'ui-resizable-handle ui-resizable-e',
19892 html : '  '
19898 var ctr = _this.el.select('.fc-event-container',true).first();
19899 var cg = ctr.createChild(cfg);
19901 var sbox = c.select('.fc-day-content',true).first().getBox();
19902 var ebox = c.select('.fc-day-content',true).first().getBox();
19904 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19905 cg.setWidth(ebox.right - sbox.x -2);
19907 cg.on('click', _this.onMoreEventClick, _this, c.more);
19917 onEventEnter: function (e, el,event,d) {
19918 this.fireEvent('evententer', this, el, event);
19921 onEventLeave: function (e, el,event,d) {
19922 this.fireEvent('eventleave', this, el, event);
19925 onEventClick: function (e, el,event,d) {
19926 this.fireEvent('eventclick', this, el, event);
19929 onMonthChange: function () {
19933 onMoreEventClick: function(e, el, more)
19937 this.calpopover.placement = 'right';
19938 this.calpopover.setTitle('More');
19940 this.calpopover.setContent('');
19942 var ctr = this.calpopover.el.select('.popover-content', true).first();
19944 Roo.each(more, function(m){
19946 cls : 'fc-event-hori fc-event-draggable',
19949 var cg = ctr.createChild(cfg);
19951 cg.on('click', _this.onEventClick, _this, m);
19954 this.calpopover.show(el);
19959 onLoad: function ()
19961 this.calevents = [];
19964 if(this.store.getCount() > 0){
19965 this.store.data.each(function(d){
19968 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19969 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19970 time : d.data.start_time,
19971 title : d.data.title,
19972 description : d.data.description,
19973 venue : d.data.venue
19978 this.renderEvents();
19980 if(this.calevents.length && this.loadMask){
19981 this.maskEl.hide();
19985 onBeforeLoad: function()
19987 this.clearEvents();
19989 this.maskEl.show();
20003 * @class Roo.bootstrap.Popover
20004 * @extends Roo.bootstrap.Component
20005 * Bootstrap Popover class
20006 * @cfg {String} html contents of the popover (or false to use children..)
20007 * @cfg {String} title of popover (or false to hide)
20008 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20009 * @cfg {String} trigger click || hover (or false to trigger manually)
20010 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20011 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20012 * - if false and it has a 'parent' then it will be automatically added to that element
20013 * - if string - Roo.get will be called
20014 * @cfg {Number} delay - delay before showing
20017 * Create a new Popover
20018 * @param {Object} config The config object
20021 Roo.bootstrap.Popover = function(config){
20022 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20028 * After the popover show
20030 * @param {Roo.bootstrap.Popover} this
20035 * After the popover hide
20037 * @param {Roo.bootstrap.Popover} this
20043 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20048 placement : 'right',
20049 trigger : 'hover', // hover
20055 can_build_overlaid : false,
20057 maskEl : false, // the mask element
20060 alignEl : false, // when show is called with an element - this get's stored.
20062 getChildContainer : function()
20064 return this.contentEl;
20067 getPopoverHeader : function()
20069 this.title = true; // flag not to hide it..
20070 this.headerEl.addClass('p-0');
20071 return this.headerEl
20075 getAutoCreate : function(){
20078 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20079 style: 'display:block',
20085 cls : 'popover-inner ',
20089 cls: 'popover-title popover-header',
20090 html : this.title === false ? '' : this.title
20093 cls : 'popover-content popover-body ' + (this.cls || ''),
20094 html : this.html || ''
20105 * @param {string} the title
20107 setTitle: function(str)
20111 this.headerEl.dom.innerHTML = str;
20116 * @param {string} the body content
20118 setContent: function(str)
20121 if (this.contentEl) {
20122 this.contentEl.dom.innerHTML = str;
20126 // as it get's added to the bottom of the page.
20127 onRender : function(ct, position)
20129 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20134 var cfg = Roo.apply({}, this.getAutoCreate());
20138 cfg.cls += ' ' + this.cls;
20141 cfg.style = this.style;
20143 //Roo.log("adding to ");
20144 this.el = Roo.get(document.body).createChild(cfg, position);
20145 // Roo.log(this.el);
20148 this.contentEl = this.el.select('.popover-content',true).first();
20149 this.headerEl = this.el.select('.popover-title',true).first();
20152 if(typeof(this.items) != 'undefined'){
20153 var items = this.items;
20156 for(var i =0;i < items.length;i++) {
20157 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20161 this.items = nitems;
20163 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20164 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20171 resizeMask : function()
20173 this.maskEl.setSize(
20174 Roo.lib.Dom.getViewWidth(true),
20175 Roo.lib.Dom.getViewHeight(true)
20179 initEvents : function()
20183 Roo.bootstrap.Popover.register(this);
20186 this.arrowEl = this.el.select('.arrow',true).first();
20187 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20188 this.el.enableDisplayMode('block');
20192 if (this.over === false && !this.parent()) {
20195 if (this.triggers === false) {
20200 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20201 var triggers = this.trigger ? this.trigger.split(' ') : [];
20202 Roo.each(triggers, function(trigger) {
20204 if (trigger == 'click') {
20205 on_el.on('click', this.toggle, this);
20206 } else if (trigger != 'manual') {
20207 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20208 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20210 on_el.on(eventIn ,this.enter, this);
20211 on_el.on(eventOut, this.leave, this);
20221 toggle : function () {
20222 this.hoverState == 'in' ? this.leave() : this.enter();
20225 enter : function () {
20227 clearTimeout(this.timeout);
20229 this.hoverState = 'in';
20231 if (!this.delay || !this.delay.show) {
20236 this.timeout = setTimeout(function () {
20237 if (_t.hoverState == 'in') {
20240 }, this.delay.show)
20243 leave : function() {
20244 clearTimeout(this.timeout);
20246 this.hoverState = 'out';
20248 if (!this.delay || !this.delay.hide) {
20253 this.timeout = setTimeout(function () {
20254 if (_t.hoverState == 'out') {
20257 }, this.delay.hide)
20261 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20262 * @param {string} (left|right|top|bottom) position
20264 show : function (on_el, placement)
20266 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20267 on_el = on_el || false; // default to false
20270 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20271 on_el = this.parent().el;
20272 } else if (this.over) {
20273 on_el = Roo.get(this.over);
20278 this.alignEl = Roo.get( on_el );
20281 this.render(document.body);
20287 if (this.title === false) {
20288 this.headerEl.hide();
20293 this.el.dom.style.display = 'block';
20296 if (this.alignEl) {
20297 this.updatePosition(this.placement, true);
20300 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20301 var es = this.el.getSize();
20302 var x = Roo.lib.Dom.getViewWidth()/2;
20303 var y = Roo.lib.Dom.getViewHeight()/2;
20304 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20309 //var arrow = this.el.select('.arrow',true).first();
20310 //arrow.set(align[2],
20312 this.el.addClass('in');
20316 this.hoverState = 'in';
20319 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20320 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20321 this.maskEl.dom.style.display = 'block';
20322 this.maskEl.addClass('show');
20324 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20326 this.fireEvent('show', this);
20330 * fire this manually after loading a grid in the table for example
20331 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20332 * @param {Boolean} try and move it if we cant get right position.
20334 updatePosition : function(placement, try_move)
20336 // allow for calling with no parameters
20337 placement = placement ? placement : this.placement;
20338 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20340 this.el.removeClass([
20341 'fade','top','bottom', 'left', 'right','in',
20342 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20344 this.el.addClass(placement + ' bs-popover-' + placement);
20346 if (!this.alignEl ) {
20350 switch (placement) {
20352 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20353 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20354 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20355 //normal display... or moved up/down.
20356 this.el.setXY(offset);
20357 var xy = this.alignEl.getAnchorXY('tr', false);
20359 this.arrowEl.setXY(xy);
20362 // continue through...
20363 return this.updatePosition('left', false);
20367 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20368 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-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('tl', false);
20373 xy[0]-=10;xy[1]+=5; // << fix me
20374 this.arrowEl.setXY(xy);
20378 return this.updatePosition('right', false);
20381 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20382 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20383 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20384 //normal display... or moved up/down.
20385 this.el.setXY(offset);
20386 var xy = this.alignEl.getAnchorXY('t', false);
20387 xy[1]-=10; // << fix me
20388 this.arrowEl.setXY(xy);
20392 return this.updatePosition('bottom', false);
20395 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20396 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20397 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20398 //normal display... or moved up/down.
20399 this.el.setXY(offset);
20400 var xy = this.alignEl.getAnchorXY('b', false);
20401 xy[1]+=2; // << fix me
20402 this.arrowEl.setXY(xy);
20406 return this.updatePosition('top', false);
20417 this.el.setXY([0,0]);
20418 this.el.removeClass('in');
20420 this.hoverState = null;
20421 this.maskEl.hide(); // always..
20422 this.fireEvent('hide', this);
20428 Roo.apply(Roo.bootstrap.Popover, {
20431 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20432 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20433 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20434 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20439 clickHander : false,
20443 onMouseDown : function(e)
20445 if (this.popups.length && !e.getTarget(".roo-popover")) {
20446 /// what is nothing is showing..
20455 register : function(popup)
20457 if (!Roo.bootstrap.Popover.clickHandler) {
20458 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20460 // hide other popups.
20461 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20462 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20463 this.hideAll(); //<< why?
20464 //this.popups.push(popup);
20466 hideAll : function()
20468 this.popups.forEach(function(p) {
20472 onShow : function() {
20473 Roo.bootstrap.Popover.popups.push(this);
20475 onHide : function() {
20476 Roo.bootstrap.Popover.popups.remove(this);
20482 * Card header - holder for the card header elements.
20487 * @class Roo.bootstrap.PopoverNav
20488 * @extends Roo.bootstrap.NavGroup
20489 * Bootstrap Popover header navigation class
20491 * Create a new Popover Header Navigation
20492 * @param {Object} config The config object
20495 Roo.bootstrap.PopoverNav = function(config){
20496 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20499 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20502 container_method : 'getPopoverHeader'
20520 * @class Roo.bootstrap.Progress
20521 * @extends Roo.bootstrap.Component
20522 * Bootstrap Progress class
20523 * @cfg {Boolean} striped striped of the progress bar
20524 * @cfg {Boolean} active animated of the progress bar
20528 * Create a new Progress
20529 * @param {Object} config The config object
20532 Roo.bootstrap.Progress = function(config){
20533 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20536 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20541 getAutoCreate : function(){
20549 cfg.cls += ' progress-striped';
20553 cfg.cls += ' active';
20572 * @class Roo.bootstrap.ProgressBar
20573 * @extends Roo.bootstrap.Component
20574 * Bootstrap ProgressBar class
20575 * @cfg {Number} aria_valuenow aria-value now
20576 * @cfg {Number} aria_valuemin aria-value min
20577 * @cfg {Number} aria_valuemax aria-value max
20578 * @cfg {String} label label for the progress bar
20579 * @cfg {String} panel (success | info | warning | danger )
20580 * @cfg {String} role role of the progress bar
20581 * @cfg {String} sr_only text
20585 * Create a new ProgressBar
20586 * @param {Object} config The config object
20589 Roo.bootstrap.ProgressBar = function(config){
20590 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20593 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20597 aria_valuemax : 100,
20603 getAutoCreate : function()
20608 cls: 'progress-bar',
20609 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20621 cfg.role = this.role;
20624 if(this.aria_valuenow){
20625 cfg['aria-valuenow'] = this.aria_valuenow;
20628 if(this.aria_valuemin){
20629 cfg['aria-valuemin'] = this.aria_valuemin;
20632 if(this.aria_valuemax){
20633 cfg['aria-valuemax'] = this.aria_valuemax;
20636 if(this.label && !this.sr_only){
20637 cfg.html = this.label;
20641 cfg.cls += ' progress-bar-' + this.panel;
20647 update : function(aria_valuenow)
20649 this.aria_valuenow = aria_valuenow;
20651 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20666 * @class Roo.bootstrap.TabGroup
20667 * @extends Roo.bootstrap.Column
20668 * Bootstrap Column class
20669 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20670 * @cfg {Boolean} carousel true to make the group behave like a carousel
20671 * @cfg {Boolean} bullets show bullets for the panels
20672 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20673 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20674 * @cfg {Boolean} showarrow (true|false) show arrow default true
20677 * Create a new TabGroup
20678 * @param {Object} config The config object
20681 Roo.bootstrap.TabGroup = function(config){
20682 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20684 this.navId = Roo.id();
20687 Roo.bootstrap.TabGroup.register(this);
20691 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20694 transition : false,
20699 slideOnTouch : false,
20702 getAutoCreate : function()
20704 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20706 cfg.cls += ' tab-content';
20708 if (this.carousel) {
20709 cfg.cls += ' carousel slide';
20712 cls : 'carousel-inner',
20716 if(this.bullets && !Roo.isTouch){
20719 cls : 'carousel-bullets',
20723 if(this.bullets_cls){
20724 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20731 cfg.cn[0].cn.push(bullets);
20734 if(this.showarrow){
20735 cfg.cn[0].cn.push({
20737 class : 'carousel-arrow',
20741 class : 'carousel-prev',
20745 class : 'fa fa-chevron-left'
20751 class : 'carousel-next',
20755 class : 'fa fa-chevron-right'
20768 initEvents: function()
20770 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20771 // this.el.on("touchstart", this.onTouchStart, this);
20774 if(this.autoslide){
20777 this.slideFn = window.setInterval(function() {
20778 _this.showPanelNext();
20782 if(this.showarrow){
20783 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20784 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20790 // onTouchStart : function(e, el, o)
20792 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20796 // this.showPanelNext();
20800 getChildContainer : function()
20802 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20806 * register a Navigation item
20807 * @param {Roo.bootstrap.NavItem} the navitem to add
20809 register : function(item)
20811 this.tabs.push( item);
20812 item.navId = this.navId; // not really needed..
20817 getActivePanel : function()
20820 Roo.each(this.tabs, function(t) {
20830 getPanelByName : function(n)
20833 Roo.each(this.tabs, function(t) {
20834 if (t.tabId == n) {
20842 indexOfPanel : function(p)
20845 Roo.each(this.tabs, function(t,i) {
20846 if (t.tabId == p.tabId) {
20855 * show a specific panel
20856 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20857 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20859 showPanel : function (pan)
20861 if(this.transition || typeof(pan) == 'undefined'){
20862 Roo.log("waiting for the transitionend");
20866 if (typeof(pan) == 'number') {
20867 pan = this.tabs[pan];
20870 if (typeof(pan) == 'string') {
20871 pan = this.getPanelByName(pan);
20874 var cur = this.getActivePanel();
20877 Roo.log('pan or acitve pan is undefined');
20881 if (pan.tabId == this.getActivePanel().tabId) {
20885 if (false === cur.fireEvent('beforedeactivate')) {
20889 if(this.bullets > 0 && !Roo.isTouch){
20890 this.setActiveBullet(this.indexOfPanel(pan));
20893 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20895 //class="carousel-item carousel-item-next carousel-item-left"
20897 this.transition = true;
20898 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20899 var lr = dir == 'next' ? 'left' : 'right';
20900 pan.el.addClass(dir); // or prev
20901 pan.el.addClass('carousel-item-' + dir); // or prev
20902 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20903 cur.el.addClass(lr); // or right
20904 pan.el.addClass(lr);
20905 cur.el.addClass('carousel-item-' +lr); // or right
20906 pan.el.addClass('carousel-item-' +lr);
20910 cur.el.on('transitionend', function() {
20911 Roo.log("trans end?");
20913 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20914 pan.setActive(true);
20916 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20917 cur.setActive(false);
20919 _this.transition = false;
20921 }, this, { single: true } );
20926 cur.setActive(false);
20927 pan.setActive(true);
20932 showPanelNext : function()
20934 var i = this.indexOfPanel(this.getActivePanel());
20936 if (i >= this.tabs.length - 1 && !this.autoslide) {
20940 if (i >= this.tabs.length - 1 && this.autoslide) {
20944 this.showPanel(this.tabs[i+1]);
20947 showPanelPrev : function()
20949 var i = this.indexOfPanel(this.getActivePanel());
20951 if (i < 1 && !this.autoslide) {
20955 if (i < 1 && this.autoslide) {
20956 i = this.tabs.length;
20959 this.showPanel(this.tabs[i-1]);
20963 addBullet: function()
20965 if(!this.bullets || Roo.isTouch){
20968 var ctr = this.el.select('.carousel-bullets',true).first();
20969 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20970 var bullet = ctr.createChild({
20971 cls : 'bullet bullet-' + i
20972 },ctr.dom.lastChild);
20977 bullet.on('click', (function(e, el, o, ii, t){
20979 e.preventDefault();
20981 this.showPanel(ii);
20983 if(this.autoslide && this.slideFn){
20984 clearInterval(this.slideFn);
20985 this.slideFn = window.setInterval(function() {
20986 _this.showPanelNext();
20990 }).createDelegate(this, [i, bullet], true));
20995 setActiveBullet : function(i)
21001 Roo.each(this.el.select('.bullet', true).elements, function(el){
21002 el.removeClass('selected');
21005 var bullet = this.el.select('.bullet-' + i, true).first();
21011 bullet.addClass('selected');
21022 Roo.apply(Roo.bootstrap.TabGroup, {
21026 * register a Navigation Group
21027 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21029 register : function(navgrp)
21031 this.groups[navgrp.navId] = navgrp;
21035 * fetch a Navigation Group based on the navigation ID
21036 * if one does not exist , it will get created.
21037 * @param {string} the navgroup to add
21038 * @returns {Roo.bootstrap.NavGroup} the navgroup
21040 get: function(navId) {
21041 if (typeof(this.groups[navId]) == 'undefined') {
21042 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21044 return this.groups[navId] ;
21059 * @class Roo.bootstrap.TabPanel
21060 * @extends Roo.bootstrap.Component
21061 * Bootstrap TabPanel class
21062 * @cfg {Boolean} active panel active
21063 * @cfg {String} html panel content
21064 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21065 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21066 * @cfg {String} href click to link..
21067 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21071 * Create a new TabPanel
21072 * @param {Object} config The config object
21075 Roo.bootstrap.TabPanel = function(config){
21076 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21080 * Fires when the active status changes
21081 * @param {Roo.bootstrap.TabPanel} this
21082 * @param {Boolean} state the new state
21087 * @event beforedeactivate
21088 * Fires before a tab is de-activated - can be used to do validation on a form.
21089 * @param {Roo.bootstrap.TabPanel} this
21090 * @return {Boolean} false if there is an error
21093 'beforedeactivate': true
21096 this.tabId = this.tabId || Roo.id();
21100 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21107 touchSlide : false,
21108 getAutoCreate : function(){
21113 // item is needed for carousel - not sure if it has any effect otherwise
21114 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21115 html: this.html || ''
21119 cfg.cls += ' active';
21123 cfg.tabId = this.tabId;
21131 initEvents: function()
21133 var p = this.parent();
21135 this.navId = this.navId || p.navId;
21137 if (typeof(this.navId) != 'undefined') {
21138 // not really needed.. but just in case.. parent should be a NavGroup.
21139 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21143 var i = tg.tabs.length - 1;
21145 if(this.active && tg.bullets > 0 && i < tg.bullets){
21146 tg.setActiveBullet(i);
21150 this.el.on('click', this.onClick, this);
21152 if(Roo.isTouch && this.touchSlide){
21153 this.el.on("touchstart", this.onTouchStart, this);
21154 this.el.on("touchmove", this.onTouchMove, this);
21155 this.el.on("touchend", this.onTouchEnd, this);
21160 onRender : function(ct, position)
21162 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21165 setActive : function(state)
21167 Roo.log("panel - set active " + this.tabId + "=" + state);
21169 this.active = state;
21171 this.el.removeClass('active');
21173 } else if (!this.el.hasClass('active')) {
21174 this.el.addClass('active');
21177 this.fireEvent('changed', this, state);
21180 onClick : function(e)
21182 e.preventDefault();
21184 if(!this.href.length){
21188 window.location.href = this.href;
21197 onTouchStart : function(e)
21199 this.swiping = false;
21201 this.startX = e.browserEvent.touches[0].clientX;
21202 this.startY = e.browserEvent.touches[0].clientY;
21205 onTouchMove : function(e)
21207 this.swiping = true;
21209 this.endX = e.browserEvent.touches[0].clientX;
21210 this.endY = e.browserEvent.touches[0].clientY;
21213 onTouchEnd : function(e)
21220 var tabGroup = this.parent();
21222 if(this.endX > this.startX){ // swiping right
21223 tabGroup.showPanelPrev();
21227 if(this.startX > this.endX){ // swiping left
21228 tabGroup.showPanelNext();
21247 * @class Roo.bootstrap.DateField
21248 * @extends Roo.bootstrap.Input
21249 * Bootstrap DateField class
21250 * @cfg {Number} weekStart default 0
21251 * @cfg {String} viewMode default empty, (months|years)
21252 * @cfg {String} minViewMode default empty, (months|years)
21253 * @cfg {Number} startDate default -Infinity
21254 * @cfg {Number} endDate default Infinity
21255 * @cfg {Boolean} todayHighlight default false
21256 * @cfg {Boolean} todayBtn default false
21257 * @cfg {Boolean} calendarWeeks default false
21258 * @cfg {Object} daysOfWeekDisabled default empty
21259 * @cfg {Boolean} singleMode default false (true | false)
21261 * @cfg {Boolean} keyboardNavigation default true
21262 * @cfg {String} language default en
21265 * Create a new DateField
21266 * @param {Object} config The config object
21269 Roo.bootstrap.DateField = function(config){
21270 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21274 * Fires when this field show.
21275 * @param {Roo.bootstrap.DateField} this
21276 * @param {Mixed} date The date value
21281 * Fires when this field hide.
21282 * @param {Roo.bootstrap.DateField} this
21283 * @param {Mixed} date The date value
21288 * Fires when select a date.
21289 * @param {Roo.bootstrap.DateField} this
21290 * @param {Mixed} date The date value
21294 * @event beforeselect
21295 * Fires when before select a date.
21296 * @param {Roo.bootstrap.DateField} this
21297 * @param {Mixed} date The date value
21299 beforeselect : true
21303 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21306 * @cfg {String} format
21307 * The default date format string which can be overriden for localization support. The format must be
21308 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21312 * @cfg {String} altFormats
21313 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21314 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21316 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21324 todayHighlight : false,
21330 keyboardNavigation: true,
21332 calendarWeeks: false,
21334 startDate: -Infinity,
21338 daysOfWeekDisabled: [],
21342 singleMode : false,
21344 UTCDate: function()
21346 return new Date(Date.UTC.apply(Date, arguments));
21349 UTCToday: function()
21351 var today = new Date();
21352 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21355 getDate: function() {
21356 var d = this.getUTCDate();
21357 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21360 getUTCDate: function() {
21364 setDate: function(d) {
21365 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21368 setUTCDate: function(d) {
21370 this.setValue(this.formatDate(this.date));
21373 onRender: function(ct, position)
21376 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21378 this.language = this.language || 'en';
21379 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21380 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21382 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21383 this.format = this.format || 'm/d/y';
21384 this.isInline = false;
21385 this.isInput = true;
21386 this.component = this.el.select('.add-on', true).first() || false;
21387 this.component = (this.component && this.component.length === 0) ? false : this.component;
21388 this.hasInput = this.component && this.inputEl().length;
21390 if (typeof(this.minViewMode === 'string')) {
21391 switch (this.minViewMode) {
21393 this.minViewMode = 1;
21396 this.minViewMode = 2;
21399 this.minViewMode = 0;
21404 if (typeof(this.viewMode === 'string')) {
21405 switch (this.viewMode) {
21418 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21420 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21422 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21424 this.picker().on('mousedown', this.onMousedown, this);
21425 this.picker().on('click', this.onClick, this);
21427 this.picker().addClass('datepicker-dropdown');
21429 this.startViewMode = this.viewMode;
21431 if(this.singleMode){
21432 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21433 v.setVisibilityMode(Roo.Element.DISPLAY);
21437 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21438 v.setStyle('width', '189px');
21442 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21443 if(!this.calendarWeeks){
21448 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21449 v.attr('colspan', function(i, val){
21450 return parseInt(val) + 1;
21455 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21457 this.setStartDate(this.startDate);
21458 this.setEndDate(this.endDate);
21460 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21467 if(this.isInline) {
21472 picker : function()
21474 return this.pickerEl;
21475 // return this.el.select('.datepicker', true).first();
21478 fillDow: function()
21480 var dowCnt = this.weekStart;
21489 if(this.calendarWeeks){
21497 while (dowCnt < this.weekStart + 7) {
21501 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21505 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21508 fillMonths: function()
21511 var months = this.picker().select('>.datepicker-months td', true).first();
21513 months.dom.innerHTML = '';
21519 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21522 months.createChild(month);
21529 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;
21531 if (this.date < this.startDate) {
21532 this.viewDate = new Date(this.startDate);
21533 } else if (this.date > this.endDate) {
21534 this.viewDate = new Date(this.endDate);
21536 this.viewDate = new Date(this.date);
21544 var d = new Date(this.viewDate),
21545 year = d.getUTCFullYear(),
21546 month = d.getUTCMonth(),
21547 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21548 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21549 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21550 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21551 currentDate = this.date && this.date.valueOf(),
21552 today = this.UTCToday();
21554 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21556 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21558 // this.picker.select('>tfoot th.today').
21559 // .text(dates[this.language].today)
21560 // .toggle(this.todayBtn !== false);
21562 this.updateNavArrows();
21565 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21567 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21569 prevMonth.setUTCDate(day);
21571 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21573 var nextMonth = new Date(prevMonth);
21575 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21577 nextMonth = nextMonth.valueOf();
21579 var fillMonths = false;
21581 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21583 while(prevMonth.valueOf() <= nextMonth) {
21586 if (prevMonth.getUTCDay() === this.weekStart) {
21588 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21596 if(this.calendarWeeks){
21597 // ISO 8601: First week contains first thursday.
21598 // ISO also states week starts on Monday, but we can be more abstract here.
21600 // Start of current week: based on weekstart/current date
21601 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21602 // Thursday of this week
21603 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21604 // First Thursday of year, year from thursday
21605 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21606 // Calendar week: ms between thursdays, div ms per day, div 7 days
21607 calWeek = (th - yth) / 864e5 / 7 + 1;
21609 fillMonths.cn.push({
21617 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21619 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21622 if (this.todayHighlight &&
21623 prevMonth.getUTCFullYear() == today.getFullYear() &&
21624 prevMonth.getUTCMonth() == today.getMonth() &&
21625 prevMonth.getUTCDate() == today.getDate()) {
21626 clsName += ' today';
21629 if (currentDate && prevMonth.valueOf() === currentDate) {
21630 clsName += ' active';
21633 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21634 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21635 clsName += ' disabled';
21638 fillMonths.cn.push({
21640 cls: 'day ' + clsName,
21641 html: prevMonth.getDate()
21644 prevMonth.setDate(prevMonth.getDate()+1);
21647 var currentYear = this.date && this.date.getUTCFullYear();
21648 var currentMonth = this.date && this.date.getUTCMonth();
21650 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21652 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21653 v.removeClass('active');
21655 if(currentYear === year && k === currentMonth){
21656 v.addClass('active');
21659 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21660 v.addClass('disabled');
21666 year = parseInt(year/10, 10) * 10;
21668 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21670 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21673 for (var i = -1; i < 11; i++) {
21674 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21676 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21684 showMode: function(dir)
21687 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21690 Roo.each(this.picker().select('>div',true).elements, function(v){
21691 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21694 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21699 if(this.isInline) {
21703 this.picker().removeClass(['bottom', 'top']);
21705 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21707 * place to the top of element!
21711 this.picker().addClass('top');
21712 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21717 this.picker().addClass('bottom');
21719 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21722 parseDate : function(value)
21724 if(!value || value instanceof Date){
21727 var v = Date.parseDate(value, this.format);
21728 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21729 v = Date.parseDate(value, 'Y-m-d');
21731 if(!v && this.altFormats){
21732 if(!this.altFormatsArray){
21733 this.altFormatsArray = this.altFormats.split("|");
21735 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21736 v = Date.parseDate(value, this.altFormatsArray[i]);
21742 formatDate : function(date, fmt)
21744 return (!date || !(date instanceof Date)) ?
21745 date : date.dateFormat(fmt || this.format);
21748 onFocus : function()
21750 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21754 onBlur : function()
21756 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21758 var d = this.inputEl().getValue();
21765 showPopup : function()
21767 this.picker().show();
21771 this.fireEvent('showpopup', this, this.date);
21774 hidePopup : function()
21776 if(this.isInline) {
21779 this.picker().hide();
21780 this.viewMode = this.startViewMode;
21783 this.fireEvent('hidepopup', this, this.date);
21787 onMousedown: function(e)
21789 e.stopPropagation();
21790 e.preventDefault();
21795 Roo.bootstrap.DateField.superclass.keyup.call(this);
21799 setValue: function(v)
21801 if(this.fireEvent('beforeselect', this, v) !== false){
21802 var d = new Date(this.parseDate(v) ).clearTime();
21804 if(isNaN(d.getTime())){
21805 this.date = this.viewDate = '';
21806 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21810 v = this.formatDate(d);
21812 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21814 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21818 this.fireEvent('select', this, this.date);
21822 getValue: function()
21824 return this.formatDate(this.date);
21827 fireKey: function(e)
21829 if (!this.picker().isVisible()){
21830 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21836 var dateChanged = false,
21838 newDate, newViewDate;
21843 e.preventDefault();
21847 if (!this.keyboardNavigation) {
21850 dir = e.keyCode == 37 ? -1 : 1;
21853 newDate = this.moveYear(this.date, dir);
21854 newViewDate = this.moveYear(this.viewDate, dir);
21855 } else if (e.shiftKey){
21856 newDate = this.moveMonth(this.date, dir);
21857 newViewDate = this.moveMonth(this.viewDate, dir);
21859 newDate = new Date(this.date);
21860 newDate.setUTCDate(this.date.getUTCDate() + dir);
21861 newViewDate = new Date(this.viewDate);
21862 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21864 if (this.dateWithinRange(newDate)){
21865 this.date = newDate;
21866 this.viewDate = newViewDate;
21867 this.setValue(this.formatDate(this.date));
21869 e.preventDefault();
21870 dateChanged = true;
21875 if (!this.keyboardNavigation) {
21878 dir = e.keyCode == 38 ? -1 : 1;
21880 newDate = this.moveYear(this.date, dir);
21881 newViewDate = this.moveYear(this.viewDate, dir);
21882 } else if (e.shiftKey){
21883 newDate = this.moveMonth(this.date, dir);
21884 newViewDate = this.moveMonth(this.viewDate, dir);
21886 newDate = new Date(this.date);
21887 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21888 newViewDate = new Date(this.viewDate);
21889 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21891 if (this.dateWithinRange(newDate)){
21892 this.date = newDate;
21893 this.viewDate = newViewDate;
21894 this.setValue(this.formatDate(this.date));
21896 e.preventDefault();
21897 dateChanged = true;
21901 this.setValue(this.formatDate(this.date));
21903 e.preventDefault();
21906 this.setValue(this.formatDate(this.date));
21920 onClick: function(e)
21922 e.stopPropagation();
21923 e.preventDefault();
21925 var target = e.getTarget();
21927 if(target.nodeName.toLowerCase() === 'i'){
21928 target = Roo.get(target).dom.parentNode;
21931 var nodeName = target.nodeName;
21932 var className = target.className;
21933 var html = target.innerHTML;
21934 //Roo.log(nodeName);
21936 switch(nodeName.toLowerCase()) {
21938 switch(className) {
21944 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21945 switch(this.viewMode){
21947 this.viewDate = this.moveMonth(this.viewDate, dir);
21951 this.viewDate = this.moveYear(this.viewDate, dir);
21957 var date = new Date();
21958 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21960 this.setValue(this.formatDate(this.date));
21967 if (className.indexOf('disabled') < 0) {
21968 if (!this.viewDate) {
21969 this.viewDate = new Date();
21971 this.viewDate.setUTCDate(1);
21972 if (className.indexOf('month') > -1) {
21973 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21975 var year = parseInt(html, 10) || 0;
21976 this.viewDate.setUTCFullYear(year);
21980 if(this.singleMode){
21981 this.setValue(this.formatDate(this.viewDate));
21992 //Roo.log(className);
21993 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21994 var day = parseInt(html, 10) || 1;
21995 var year = (this.viewDate || new Date()).getUTCFullYear(),
21996 month = (this.viewDate || new Date()).getUTCMonth();
21998 if (className.indexOf('old') > -1) {
22005 } else if (className.indexOf('new') > -1) {
22013 //Roo.log([year,month,day]);
22014 this.date = this.UTCDate(year, month, day,0,0,0,0);
22015 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22017 //Roo.log(this.formatDate(this.date));
22018 this.setValue(this.formatDate(this.date));
22025 setStartDate: function(startDate)
22027 this.startDate = startDate || -Infinity;
22028 if (this.startDate !== -Infinity) {
22029 this.startDate = this.parseDate(this.startDate);
22032 this.updateNavArrows();
22035 setEndDate: function(endDate)
22037 this.endDate = endDate || Infinity;
22038 if (this.endDate !== Infinity) {
22039 this.endDate = this.parseDate(this.endDate);
22042 this.updateNavArrows();
22045 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22047 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22048 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22049 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22051 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22052 return parseInt(d, 10);
22055 this.updateNavArrows();
22058 updateNavArrows: function()
22060 if(this.singleMode){
22064 var d = new Date(this.viewDate),
22065 year = d.getUTCFullYear(),
22066 month = d.getUTCMonth();
22068 Roo.each(this.picker().select('.prev', true).elements, function(v){
22070 switch (this.viewMode) {
22073 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22079 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22086 Roo.each(this.picker().select('.next', true).elements, function(v){
22088 switch (this.viewMode) {
22091 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22097 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22105 moveMonth: function(date, dir)
22110 var new_date = new Date(date.valueOf()),
22111 day = new_date.getUTCDate(),
22112 month = new_date.getUTCMonth(),
22113 mag = Math.abs(dir),
22115 dir = dir > 0 ? 1 : -1;
22118 // If going back one month, make sure month is not current month
22119 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22121 return new_date.getUTCMonth() == month;
22123 // If going forward one month, make sure month is as expected
22124 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22126 return new_date.getUTCMonth() != new_month;
22128 new_month = month + dir;
22129 new_date.setUTCMonth(new_month);
22130 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22131 if (new_month < 0 || new_month > 11) {
22132 new_month = (new_month + 12) % 12;
22135 // For magnitudes >1, move one month at a time...
22136 for (var i=0; i<mag; i++) {
22137 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22138 new_date = this.moveMonth(new_date, dir);
22140 // ...then reset the day, keeping it in the new month
22141 new_month = new_date.getUTCMonth();
22142 new_date.setUTCDate(day);
22144 return new_month != new_date.getUTCMonth();
22147 // Common date-resetting loop -- if date is beyond end of month, make it
22150 new_date.setUTCDate(--day);
22151 new_date.setUTCMonth(new_month);
22156 moveYear: function(date, dir)
22158 return this.moveMonth(date, dir*12);
22161 dateWithinRange: function(date)
22163 return date >= this.startDate && date <= this.endDate;
22169 this.picker().remove();
22172 validateValue : function(value)
22174 if(this.getVisibilityEl().hasClass('hidden')){
22178 if(value.length < 1) {
22179 if(this.allowBlank){
22185 if(value.length < this.minLength){
22188 if(value.length > this.maxLength){
22192 var vt = Roo.form.VTypes;
22193 if(!vt[this.vtype](value, this)){
22197 if(typeof this.validator == "function"){
22198 var msg = this.validator(value);
22204 if(this.regex && !this.regex.test(value)){
22208 if(typeof(this.parseDate(value)) == 'undefined'){
22212 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22216 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22226 this.date = this.viewDate = '';
22228 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22233 Roo.apply(Roo.bootstrap.DateField, {
22244 html: '<i class="fa fa-arrow-left"/>'
22254 html: '<i class="fa fa-arrow-right"/>'
22296 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22297 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22298 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22299 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22300 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22313 navFnc: 'FullYear',
22318 navFnc: 'FullYear',
22323 Roo.apply(Roo.bootstrap.DateField, {
22327 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22331 cls: 'datepicker-days',
22335 cls: 'table-condensed',
22337 Roo.bootstrap.DateField.head,
22341 Roo.bootstrap.DateField.footer
22348 cls: 'datepicker-months',
22352 cls: 'table-condensed',
22354 Roo.bootstrap.DateField.head,
22355 Roo.bootstrap.DateField.content,
22356 Roo.bootstrap.DateField.footer
22363 cls: 'datepicker-years',
22367 cls: 'table-condensed',
22369 Roo.bootstrap.DateField.head,
22370 Roo.bootstrap.DateField.content,
22371 Roo.bootstrap.DateField.footer
22390 * @class Roo.bootstrap.TimeField
22391 * @extends Roo.bootstrap.Input
22392 * Bootstrap DateField class
22396 * Create a new TimeField
22397 * @param {Object} config The config object
22400 Roo.bootstrap.TimeField = function(config){
22401 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22405 * Fires when this field show.
22406 * @param {Roo.bootstrap.DateField} thisthis
22407 * @param {Mixed} date The date value
22412 * Fires when this field hide.
22413 * @param {Roo.bootstrap.DateField} this
22414 * @param {Mixed} date The date value
22419 * Fires when select a date.
22420 * @param {Roo.bootstrap.DateField} this
22421 * @param {Mixed} date The date value
22427 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22430 * @cfg {String} format
22431 * The default time format string which can be overriden for localization support. The format must be
22432 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22436 getAutoCreate : function()
22438 this.after = '<i class="fa far fa-clock"></i>';
22439 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22443 onRender: function(ct, position)
22446 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22448 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22450 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22452 this.pop = this.picker().select('>.datepicker-time',true).first();
22453 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22455 this.picker().on('mousedown', this.onMousedown, this);
22456 this.picker().on('click', this.onClick, this);
22458 this.picker().addClass('datepicker-dropdown');
22463 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22464 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22465 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22466 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22467 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22468 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22472 fireKey: function(e){
22473 if (!this.picker().isVisible()){
22474 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22480 e.preventDefault();
22488 this.onTogglePeriod();
22491 this.onIncrementMinutes();
22494 this.onDecrementMinutes();
22503 onClick: function(e) {
22504 e.stopPropagation();
22505 e.preventDefault();
22508 picker : function()
22510 return this.pickerEl;
22513 fillTime: function()
22515 var time = this.pop.select('tbody', true).first();
22517 time.dom.innerHTML = '';
22532 cls: 'hours-up fa fas fa-chevron-up'
22552 cls: 'minutes-up fa fas fa-chevron-up'
22573 cls: 'timepicker-hour',
22588 cls: 'timepicker-minute',
22603 cls: 'btn btn-primary period',
22625 cls: 'hours-down fa fas fa-chevron-down'
22645 cls: 'minutes-down fa fas fa-chevron-down'
22663 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22670 var hours = this.time.getHours();
22671 var minutes = this.time.getMinutes();
22684 hours = hours - 12;
22688 hours = '0' + hours;
22692 minutes = '0' + minutes;
22695 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22696 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22697 this.pop.select('button', true).first().dom.innerHTML = period;
22703 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22705 var cls = ['bottom'];
22707 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22714 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22718 //this.picker().setXY(20000,20000);
22719 this.picker().addClass(cls.join('-'));
22723 Roo.each(cls, function(c){
22728 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22729 //_this.picker().setTop(_this.inputEl().getHeight());
22733 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22735 //_this.picker().setTop(0 - _this.picker().getHeight());
22740 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22744 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22752 onFocus : function()
22754 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22758 onBlur : function()
22760 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22766 this.picker().show();
22771 this.fireEvent('show', this, this.date);
22776 this.picker().hide();
22779 this.fireEvent('hide', this, this.date);
22782 setTime : function()
22785 this.setValue(this.time.format(this.format));
22787 this.fireEvent('select', this, this.date);
22792 onMousedown: function(e){
22793 e.stopPropagation();
22794 e.preventDefault();
22797 onIncrementHours: function()
22799 Roo.log('onIncrementHours');
22800 this.time = this.time.add(Date.HOUR, 1);
22805 onDecrementHours: function()
22807 Roo.log('onDecrementHours');
22808 this.time = this.time.add(Date.HOUR, -1);
22812 onIncrementMinutes: function()
22814 Roo.log('onIncrementMinutes');
22815 this.time = this.time.add(Date.MINUTE, 1);
22819 onDecrementMinutes: function()
22821 Roo.log('onDecrementMinutes');
22822 this.time = this.time.add(Date.MINUTE, -1);
22826 onTogglePeriod: function()
22828 Roo.log('onTogglePeriod');
22829 this.time = this.time.add(Date.HOUR, 12);
22837 Roo.apply(Roo.bootstrap.TimeField, {
22841 cls: 'datepicker dropdown-menu',
22845 cls: 'datepicker-time',
22849 cls: 'table-condensed',
22878 cls: 'btn btn-info ok',
22906 * @class Roo.bootstrap.MonthField
22907 * @extends Roo.bootstrap.Input
22908 * Bootstrap MonthField class
22910 * @cfg {String} language default en
22913 * Create a new MonthField
22914 * @param {Object} config The config object
22917 Roo.bootstrap.MonthField = function(config){
22918 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22923 * Fires when this field show.
22924 * @param {Roo.bootstrap.MonthField} this
22925 * @param {Mixed} date The date value
22930 * Fires when this field hide.
22931 * @param {Roo.bootstrap.MonthField} this
22932 * @param {Mixed} date The date value
22937 * Fires when select a date.
22938 * @param {Roo.bootstrap.MonthField} this
22939 * @param {String} oldvalue The old value
22940 * @param {String} newvalue The new value
22946 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22948 onRender: function(ct, position)
22951 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22953 this.language = this.language || 'en';
22954 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22955 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22957 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22958 this.isInline = false;
22959 this.isInput = true;
22960 this.component = this.el.select('.add-on', true).first() || false;
22961 this.component = (this.component && this.component.length === 0) ? false : this.component;
22962 this.hasInput = this.component && this.inputEL().length;
22964 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22966 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22968 this.picker().on('mousedown', this.onMousedown, this);
22969 this.picker().on('click', this.onClick, this);
22971 this.picker().addClass('datepicker-dropdown');
22973 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22974 v.setStyle('width', '189px');
22981 if(this.isInline) {
22987 setValue: function(v, suppressEvent)
22989 var o = this.getValue();
22991 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22995 if(suppressEvent !== true){
22996 this.fireEvent('select', this, o, v);
23001 getValue: function()
23006 onClick: function(e)
23008 e.stopPropagation();
23009 e.preventDefault();
23011 var target = e.getTarget();
23013 if(target.nodeName.toLowerCase() === 'i'){
23014 target = Roo.get(target).dom.parentNode;
23017 var nodeName = target.nodeName;
23018 var className = target.className;
23019 var html = target.innerHTML;
23021 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23025 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23027 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23033 picker : function()
23035 return this.pickerEl;
23038 fillMonths: function()
23041 var months = this.picker().select('>.datepicker-months td', true).first();
23043 months.dom.innerHTML = '';
23049 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23052 months.createChild(month);
23061 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23062 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23065 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23066 e.removeClass('active');
23068 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23069 e.addClass('active');
23076 if(this.isInline) {
23080 this.picker().removeClass(['bottom', 'top']);
23082 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23084 * place to the top of element!
23088 this.picker().addClass('top');
23089 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23094 this.picker().addClass('bottom');
23096 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23099 onFocus : function()
23101 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23105 onBlur : function()
23107 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23109 var d = this.inputEl().getValue();
23118 this.picker().show();
23119 this.picker().select('>.datepicker-months', true).first().show();
23123 this.fireEvent('show', this, this.date);
23128 if(this.isInline) {
23131 this.picker().hide();
23132 this.fireEvent('hide', this, this.date);
23136 onMousedown: function(e)
23138 e.stopPropagation();
23139 e.preventDefault();
23144 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23148 fireKey: function(e)
23150 if (!this.picker().isVisible()){
23151 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23162 e.preventDefault();
23166 dir = e.keyCode == 37 ? -1 : 1;
23168 this.vIndex = this.vIndex + dir;
23170 if(this.vIndex < 0){
23174 if(this.vIndex > 11){
23178 if(isNaN(this.vIndex)){
23182 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23188 dir = e.keyCode == 38 ? -1 : 1;
23190 this.vIndex = this.vIndex + dir * 4;
23192 if(this.vIndex < 0){
23196 if(this.vIndex > 11){
23200 if(isNaN(this.vIndex)){
23204 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23209 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23210 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23214 e.preventDefault();
23217 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23218 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23234 this.picker().remove();
23239 Roo.apply(Roo.bootstrap.MonthField, {
23258 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23259 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23264 Roo.apply(Roo.bootstrap.MonthField, {
23268 cls: 'datepicker dropdown-menu roo-dynamic',
23272 cls: 'datepicker-months',
23276 cls: 'table-condensed',
23278 Roo.bootstrap.DateField.content
23298 * @class Roo.bootstrap.CheckBox
23299 * @extends Roo.bootstrap.Input
23300 * Bootstrap CheckBox class
23302 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23303 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23304 * @cfg {String} boxLabel The text that appears beside the checkbox
23305 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23306 * @cfg {Boolean} checked initnal the element
23307 * @cfg {Boolean} inline inline the element (default false)
23308 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23309 * @cfg {String} tooltip label tooltip
23312 * Create a new CheckBox
23313 * @param {Object} config The config object
23316 Roo.bootstrap.CheckBox = function(config){
23317 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23322 * Fires when the element is checked or unchecked.
23323 * @param {Roo.bootstrap.CheckBox} this This input
23324 * @param {Boolean} checked The new checked value
23329 * Fires when the element is click.
23330 * @param {Roo.bootstrap.CheckBox} this This input
23337 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23339 inputType: 'checkbox',
23348 // checkbox success does not make any sense really..
23353 getAutoCreate : function()
23355 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23361 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23364 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23370 type : this.inputType,
23371 value : this.inputValue,
23372 cls : 'roo-' + this.inputType, //'form-box',
23373 placeholder : this.placeholder || ''
23377 if(this.inputType != 'radio'){
23381 cls : 'roo-hidden-value',
23382 value : this.checked ? this.inputValue : this.valueOff
23387 if (this.weight) { // Validity check?
23388 cfg.cls += " " + this.inputType + "-" + this.weight;
23391 if (this.disabled) {
23392 input.disabled=true;
23396 input.checked = this.checked;
23401 input.name = this.name;
23403 if(this.inputType != 'radio'){
23404 hidden.name = this.name;
23405 input.name = '_hidden_' + this.name;
23410 input.cls += ' input-' + this.size;
23415 ['xs','sm','md','lg'].map(function(size){
23416 if (settings[size]) {
23417 cfg.cls += ' col-' + size + '-' + settings[size];
23421 var inputblock = input;
23423 if (this.before || this.after) {
23426 cls : 'input-group',
23431 inputblock.cn.push({
23433 cls : 'input-group-addon',
23438 inputblock.cn.push(input);
23440 if(this.inputType != 'radio'){
23441 inputblock.cn.push(hidden);
23445 inputblock.cn.push({
23447 cls : 'input-group-addon',
23453 var boxLabelCfg = false;
23459 //'for': id, // box label is handled by onclick - so no for...
23461 html: this.boxLabel
23464 boxLabelCfg.tooltip = this.tooltip;
23470 if (align ==='left' && this.fieldLabel.length) {
23471 // Roo.log("left and has label");
23476 cls : 'control-label',
23477 html : this.fieldLabel
23488 cfg.cn[1].cn.push(boxLabelCfg);
23491 if(this.labelWidth > 12){
23492 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23495 if(this.labelWidth < 13 && this.labelmd == 0){
23496 this.labelmd = this.labelWidth;
23499 if(this.labellg > 0){
23500 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23501 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23504 if(this.labelmd > 0){
23505 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23506 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23509 if(this.labelsm > 0){
23510 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23511 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23514 if(this.labelxs > 0){
23515 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23516 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23519 } else if ( this.fieldLabel.length) {
23520 // Roo.log(" label");
23524 tag: this.boxLabel ? 'span' : 'label',
23526 cls: 'control-label box-input-label',
23527 //cls : 'input-group-addon',
23528 html : this.fieldLabel
23535 cfg.cn.push(boxLabelCfg);
23540 // Roo.log(" no label && no align");
23541 cfg.cn = [ inputblock ] ;
23543 cfg.cn.push(boxLabelCfg);
23551 if(this.inputType != 'radio'){
23552 cfg.cn.push(hidden);
23560 * return the real input element.
23562 inputEl: function ()
23564 return this.el.select('input.roo-' + this.inputType,true).first();
23566 hiddenEl: function ()
23568 return this.el.select('input.roo-hidden-value',true).first();
23571 labelEl: function()
23573 return this.el.select('label.control-label',true).first();
23575 /* depricated... */
23579 return this.labelEl();
23582 boxLabelEl: function()
23584 return this.el.select('label.box-label',true).first();
23587 initEvents : function()
23589 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23591 this.inputEl().on('click', this.onClick, this);
23593 if (this.boxLabel) {
23594 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23597 this.startValue = this.getValue();
23600 Roo.bootstrap.CheckBox.register(this);
23604 onClick : function(e)
23606 if(this.fireEvent('click', this, e) !== false){
23607 this.setChecked(!this.checked);
23612 setChecked : function(state,suppressEvent)
23614 this.startValue = this.getValue();
23616 if(this.inputType == 'radio'){
23618 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23619 e.dom.checked = false;
23622 this.inputEl().dom.checked = true;
23624 this.inputEl().dom.value = this.inputValue;
23626 if(suppressEvent !== true){
23627 this.fireEvent('check', this, true);
23635 this.checked = state;
23637 this.inputEl().dom.checked = state;
23640 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23642 if(suppressEvent !== true){
23643 this.fireEvent('check', this, state);
23649 getValue : function()
23651 if(this.inputType == 'radio'){
23652 return this.getGroupValue();
23655 return this.hiddenEl().dom.value;
23659 getGroupValue : function()
23661 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23665 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23668 setValue : function(v,suppressEvent)
23670 if(this.inputType == 'radio'){
23671 this.setGroupValue(v, suppressEvent);
23675 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23680 setGroupValue : function(v, suppressEvent)
23682 this.startValue = this.getValue();
23684 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23685 e.dom.checked = false;
23687 if(e.dom.value == v){
23688 e.dom.checked = true;
23692 if(suppressEvent !== true){
23693 this.fireEvent('check', this, true);
23701 validate : function()
23703 if(this.getVisibilityEl().hasClass('hidden')){
23709 (this.inputType == 'radio' && this.validateRadio()) ||
23710 (this.inputType == 'checkbox' && this.validateCheckbox())
23716 this.markInvalid();
23720 validateRadio : function()
23722 if(this.getVisibilityEl().hasClass('hidden')){
23726 if(this.allowBlank){
23732 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23733 if(!e.dom.checked){
23745 validateCheckbox : function()
23748 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23749 //return (this.getValue() == this.inputValue) ? true : false;
23752 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23760 for(var i in group){
23761 if(group[i].el.isVisible(true)){
23769 for(var i in group){
23774 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23781 * Mark this field as valid
23783 markValid : function()
23787 this.fireEvent('valid', this);
23789 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23792 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23799 if(this.inputType == 'radio'){
23800 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23801 var fg = e.findParent('.form-group', false, true);
23802 if (Roo.bootstrap.version == 3) {
23803 fg.removeClass([_this.invalidClass, _this.validClass]);
23804 fg.addClass(_this.validClass);
23806 fg.removeClass(['is-valid', 'is-invalid']);
23807 fg.addClass('is-valid');
23815 var fg = this.el.findParent('.form-group', false, true);
23816 if (Roo.bootstrap.version == 3) {
23817 fg.removeClass([this.invalidClass, this.validClass]);
23818 fg.addClass(this.validClass);
23820 fg.removeClass(['is-valid', 'is-invalid']);
23821 fg.addClass('is-valid');
23826 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23832 for(var i in group){
23833 var fg = group[i].el.findParent('.form-group', false, true);
23834 if (Roo.bootstrap.version == 3) {
23835 fg.removeClass([this.invalidClass, this.validClass]);
23836 fg.addClass(this.validClass);
23838 fg.removeClass(['is-valid', 'is-invalid']);
23839 fg.addClass('is-valid');
23845 * Mark this field as invalid
23846 * @param {String} msg The validation message
23848 markInvalid : function(msg)
23850 if(this.allowBlank){
23856 this.fireEvent('invalid', this, msg);
23858 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23861 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23865 label.markInvalid();
23868 if(this.inputType == 'radio'){
23870 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23871 var fg = e.findParent('.form-group', false, true);
23872 if (Roo.bootstrap.version == 3) {
23873 fg.removeClass([_this.invalidClass, _this.validClass]);
23874 fg.addClass(_this.invalidClass);
23876 fg.removeClass(['is-invalid', 'is-valid']);
23877 fg.addClass('is-invalid');
23885 var fg = this.el.findParent('.form-group', false, true);
23886 if (Roo.bootstrap.version == 3) {
23887 fg.removeClass([_this.invalidClass, _this.validClass]);
23888 fg.addClass(_this.invalidClass);
23890 fg.removeClass(['is-invalid', 'is-valid']);
23891 fg.addClass('is-invalid');
23896 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23902 for(var i in group){
23903 var fg = group[i].el.findParent('.form-group', false, true);
23904 if (Roo.bootstrap.version == 3) {
23905 fg.removeClass([_this.invalidClass, _this.validClass]);
23906 fg.addClass(_this.invalidClass);
23908 fg.removeClass(['is-invalid', 'is-valid']);
23909 fg.addClass('is-invalid');
23915 clearInvalid : function()
23917 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23919 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23923 if (label && label.iconEl) {
23924 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23925 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23929 disable : function()
23931 if(this.inputType != 'radio'){
23932 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23939 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23940 _this.getActionEl().addClass(this.disabledClass);
23941 e.dom.disabled = true;
23945 this.disabled = true;
23946 this.fireEvent("disable", this);
23950 enable : function()
23952 if(this.inputType != 'radio'){
23953 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23960 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23961 _this.getActionEl().removeClass(this.disabledClass);
23962 e.dom.disabled = false;
23966 this.disabled = false;
23967 this.fireEvent("enable", this);
23971 setBoxLabel : function(v)
23976 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23982 Roo.apply(Roo.bootstrap.CheckBox, {
23987 * register a CheckBox Group
23988 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23990 register : function(checkbox)
23992 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23993 this.groups[checkbox.groupId] = {};
23996 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24000 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24004 * fetch a CheckBox Group based on the group ID
24005 * @param {string} the group ID
24006 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24008 get: function(groupId) {
24009 if (typeof(this.groups[groupId]) == 'undefined') {
24013 return this.groups[groupId] ;
24026 * @class Roo.bootstrap.Radio
24027 * @extends Roo.bootstrap.Component
24028 * Bootstrap Radio class
24029 * @cfg {String} boxLabel - the label associated
24030 * @cfg {String} value - the value of radio
24033 * Create a new Radio
24034 * @param {Object} config The config object
24036 Roo.bootstrap.Radio = function(config){
24037 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24041 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24047 getAutoCreate : function()
24051 cls : 'form-group radio',
24056 html : this.boxLabel
24064 initEvents : function()
24066 this.parent().register(this);
24068 this.el.on('click', this.onClick, this);
24072 onClick : function(e)
24074 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24075 this.setChecked(true);
24079 setChecked : function(state, suppressEvent)
24081 this.parent().setValue(this.value, suppressEvent);
24085 setBoxLabel : function(v)
24090 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24105 * @class Roo.bootstrap.SecurePass
24106 * @extends Roo.bootstrap.Input
24107 * Bootstrap SecurePass class
24111 * Create a new SecurePass
24112 * @param {Object} config The config object
24115 Roo.bootstrap.SecurePass = function (config) {
24116 // these go here, so the translation tool can replace them..
24118 PwdEmpty: "Please type a password, and then retype it to confirm.",
24119 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24120 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24121 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24122 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24123 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24124 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24125 TooWeak: "Your password is Too Weak."
24127 this.meterLabel = "Password strength:";
24128 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24129 this.meterClass = [
24130 "roo-password-meter-tooweak",
24131 "roo-password-meter-weak",
24132 "roo-password-meter-medium",
24133 "roo-password-meter-strong",
24134 "roo-password-meter-grey"
24139 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24142 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24144 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24146 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24147 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24148 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24149 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24150 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24151 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24152 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24162 * @cfg {String/Object} Label for the strength meter (defaults to
24163 * 'Password strength:')
24168 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24169 * ['Weak', 'Medium', 'Strong'])
24172 pwdStrengths: false,
24185 initEvents: function ()
24187 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24189 if (this.el.is('input[type=password]') && Roo.isSafari) {
24190 this.el.on('keydown', this.SafariOnKeyDown, this);
24193 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24196 onRender: function (ct, position)
24198 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24199 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24200 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24202 this.trigger.createChild({
24207 cls: 'roo-password-meter-grey col-xs-12',
24210 //width: this.meterWidth + 'px'
24214 cls: 'roo-password-meter-text'
24220 if (this.hideTrigger) {
24221 this.trigger.setDisplayed(false);
24223 this.setSize(this.width || '', this.height || '');
24226 onDestroy: function ()
24228 if (this.trigger) {
24229 this.trigger.removeAllListeners();
24230 this.trigger.remove();
24233 this.wrap.remove();
24235 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24238 checkStrength: function ()
24240 var pwd = this.inputEl().getValue();
24241 if (pwd == this._lastPwd) {
24246 if (this.ClientSideStrongPassword(pwd)) {
24248 } else if (this.ClientSideMediumPassword(pwd)) {
24250 } else if (this.ClientSideWeakPassword(pwd)) {
24256 Roo.log('strength1: ' + strength);
24258 //var pm = this.trigger.child('div/div/div').dom;
24259 var pm = this.trigger.child('div/div');
24260 pm.removeClass(this.meterClass);
24261 pm.addClass(this.meterClass[strength]);
24264 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24266 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24268 this._lastPwd = pwd;
24272 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24274 this._lastPwd = '';
24276 var pm = this.trigger.child('div/div');
24277 pm.removeClass(this.meterClass);
24278 pm.addClass('roo-password-meter-grey');
24281 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24284 this.inputEl().dom.type='password';
24287 validateValue: function (value)
24289 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24292 if (value.length == 0) {
24293 if (this.allowBlank) {
24294 this.clearInvalid();
24298 this.markInvalid(this.errors.PwdEmpty);
24299 this.errorMsg = this.errors.PwdEmpty;
24307 if (!value.match(/[\x21-\x7e]+/)) {
24308 this.markInvalid(this.errors.PwdBadChar);
24309 this.errorMsg = this.errors.PwdBadChar;
24312 if (value.length < 6) {
24313 this.markInvalid(this.errors.PwdShort);
24314 this.errorMsg = this.errors.PwdShort;
24317 if (value.length > 16) {
24318 this.markInvalid(this.errors.PwdLong);
24319 this.errorMsg = this.errors.PwdLong;
24323 if (this.ClientSideStrongPassword(value)) {
24325 } else if (this.ClientSideMediumPassword(value)) {
24327 } else if (this.ClientSideWeakPassword(value)) {
24334 if (strength < 2) {
24335 //this.markInvalid(this.errors.TooWeak);
24336 this.errorMsg = this.errors.TooWeak;
24341 console.log('strength2: ' + strength);
24343 //var pm = this.trigger.child('div/div/div').dom;
24345 var pm = this.trigger.child('div/div');
24346 pm.removeClass(this.meterClass);
24347 pm.addClass(this.meterClass[strength]);
24349 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24351 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24353 this.errorMsg = '';
24357 CharacterSetChecks: function (type)
24360 this.fResult = false;
24363 isctype: function (character, type)
24366 case this.kCapitalLetter:
24367 if (character >= 'A' && character <= 'Z') {
24372 case this.kSmallLetter:
24373 if (character >= 'a' && character <= 'z') {
24379 if (character >= '0' && character <= '9') {
24384 case this.kPunctuation:
24385 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24396 IsLongEnough: function (pwd, size)
24398 return !(pwd == null || isNaN(size) || pwd.length < size);
24401 SpansEnoughCharacterSets: function (word, nb)
24403 if (!this.IsLongEnough(word, nb))
24408 var characterSetChecks = new Array(
24409 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24410 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24413 for (var index = 0; index < word.length; ++index) {
24414 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24415 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24416 characterSetChecks[nCharSet].fResult = true;
24423 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24424 if (characterSetChecks[nCharSet].fResult) {
24429 if (nCharSets < nb) {
24435 ClientSideStrongPassword: function (pwd)
24437 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24440 ClientSideMediumPassword: function (pwd)
24442 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24445 ClientSideWeakPassword: function (pwd)
24447 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24450 })//<script type="text/javascript">
24453 * Based Ext JS Library 1.1.1
24454 * Copyright(c) 2006-2007, Ext JS, LLC.
24460 * @class Roo.HtmlEditorCore
24461 * @extends Roo.Component
24462 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24464 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24467 Roo.HtmlEditorCore = function(config){
24470 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24475 * @event initialize
24476 * Fires when the editor is fully initialized (including the iframe)
24477 * @param {Roo.HtmlEditorCore} this
24482 * Fires when the editor is first receives the focus. Any insertion must wait
24483 * until after this event.
24484 * @param {Roo.HtmlEditorCore} this
24488 * @event beforesync
24489 * Fires before the textarea is updated with content from the editor iframe. Return false
24490 * to cancel the sync.
24491 * @param {Roo.HtmlEditorCore} this
24492 * @param {String} html
24496 * @event beforepush
24497 * Fires before the iframe editor is updated with content from the textarea. Return false
24498 * to cancel the push.
24499 * @param {Roo.HtmlEditorCore} this
24500 * @param {String} html
24505 * Fires when the textarea is updated with content from the editor iframe.
24506 * @param {Roo.HtmlEditorCore} this
24507 * @param {String} html
24512 * Fires when the iframe editor is updated with content from the textarea.
24513 * @param {Roo.HtmlEditorCore} this
24514 * @param {String} html
24519 * @event editorevent
24520 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24521 * @param {Roo.HtmlEditorCore} this
24527 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24529 // defaults : white / black...
24530 this.applyBlacklists();
24537 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24541 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24547 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24552 * @cfg {Number} height (in pixels)
24556 * @cfg {Number} width (in pixels)
24561 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24564 stylesheets: false,
24569 // private properties
24570 validationEvent : false,
24572 initialized : false,
24574 sourceEditMode : false,
24575 onFocus : Roo.emptyFn,
24577 hideMode:'offsets',
24581 // blacklist + whitelisted elements..
24588 * Protected method that will not generally be called directly. It
24589 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24590 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24592 getDocMarkup : function(){
24596 // inherit styels from page...??
24597 if (this.stylesheets === false) {
24599 Roo.get(document.head).select('style').each(function(node) {
24600 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24603 Roo.get(document.head).select('link').each(function(node) {
24604 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24607 } else if (!this.stylesheets.length) {
24609 st = '<style type="text/css">' +
24610 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24613 for (var i in this.stylesheets) {
24614 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24619 st += '<style type="text/css">' +
24620 'IMG { cursor: pointer } ' +
24623 var cls = 'roo-htmleditor-body';
24625 if(this.bodyCls.length){
24626 cls += ' ' + this.bodyCls;
24629 return '<html><head>' + st +
24630 //<style type="text/css">' +
24631 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24633 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24637 onRender : function(ct, position)
24640 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24641 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24644 this.el.dom.style.border = '0 none';
24645 this.el.dom.setAttribute('tabIndex', -1);
24646 this.el.addClass('x-hidden hide');
24650 if(Roo.isIE){ // fix IE 1px bogus margin
24651 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24655 this.frameId = Roo.id();
24659 var iframe = this.owner.wrap.createChild({
24661 cls: 'form-control', // bootstrap..
24663 name: this.frameId,
24664 frameBorder : 'no',
24665 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24670 this.iframe = iframe.dom;
24672 this.assignDocWin();
24674 this.doc.designMode = 'on';
24677 this.doc.write(this.getDocMarkup());
24681 var task = { // must defer to wait for browser to be ready
24683 //console.log("run task?" + this.doc.readyState);
24684 this.assignDocWin();
24685 if(this.doc.body || this.doc.readyState == 'complete'){
24687 this.doc.designMode="on";
24691 Roo.TaskMgr.stop(task);
24692 this.initEditor.defer(10, this);
24699 Roo.TaskMgr.start(task);
24704 onResize : function(w, h)
24706 Roo.log('resize: ' +w + ',' + h );
24707 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24711 if(typeof w == 'number'){
24713 this.iframe.style.width = w + 'px';
24715 if(typeof h == 'number'){
24717 this.iframe.style.height = h + 'px';
24719 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24726 * Toggles the editor between standard and source edit mode.
24727 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24729 toggleSourceEdit : function(sourceEditMode){
24731 this.sourceEditMode = sourceEditMode === true;
24733 if(this.sourceEditMode){
24735 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24738 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24739 //this.iframe.className = '';
24742 //this.setSize(this.owner.wrap.getSize());
24743 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24750 * Protected method that will not generally be called directly. If you need/want
24751 * custom HTML cleanup, this is the method you should override.
24752 * @param {String} html The HTML to be cleaned
24753 * return {String} The cleaned HTML
24755 cleanHtml : function(html){
24756 html = String(html);
24757 if(html.length > 5){
24758 if(Roo.isSafari){ // strip safari nonsense
24759 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24762 if(html == ' '){
24769 * HTML Editor -> Textarea
24770 * Protected method that will not generally be called directly. Syncs the contents
24771 * of the editor iframe with the textarea.
24773 syncValue : function(){
24774 if(this.initialized){
24775 var bd = (this.doc.body || this.doc.documentElement);
24776 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24777 var html = bd.innerHTML;
24779 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24780 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24782 html = '<div style="'+m[0]+'">' + html + '</div>';
24785 html = this.cleanHtml(html);
24786 // fix up the special chars.. normaly like back quotes in word...
24787 // however we do not want to do this with chinese..
24788 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24790 var cc = match.charCodeAt();
24792 // Get the character value, handling surrogate pairs
24793 if (match.length == 2) {
24794 // It's a surrogate pair, calculate the Unicode code point
24795 var high = match.charCodeAt(0) - 0xD800;
24796 var low = match.charCodeAt(1) - 0xDC00;
24797 cc = (high * 0x400) + low + 0x10000;
24799 (cc >= 0x4E00 && cc < 0xA000 ) ||
24800 (cc >= 0x3400 && cc < 0x4E00 ) ||
24801 (cc >= 0xf900 && cc < 0xfb00 )
24806 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24807 return "&#" + cc + ";";
24814 if(this.owner.fireEvent('beforesync', this, html) !== false){
24815 this.el.dom.value = html;
24816 this.owner.fireEvent('sync', this, html);
24822 * Protected method that will not generally be called directly. Pushes the value of the textarea
24823 * into the iframe editor.
24825 pushValue : function(){
24826 if(this.initialized){
24827 var v = this.el.dom.value.trim();
24829 // if(v.length < 1){
24833 if(this.owner.fireEvent('beforepush', this, v) !== false){
24834 var d = (this.doc.body || this.doc.documentElement);
24836 this.cleanUpPaste();
24837 this.el.dom.value = d.innerHTML;
24838 this.owner.fireEvent('push', this, v);
24844 deferFocus : function(){
24845 this.focus.defer(10, this);
24849 focus : function(){
24850 if(this.win && !this.sourceEditMode){
24857 assignDocWin: function()
24859 var iframe = this.iframe;
24862 this.doc = iframe.contentWindow.document;
24863 this.win = iframe.contentWindow;
24865 // if (!Roo.get(this.frameId)) {
24868 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24869 // this.win = Roo.get(this.frameId).dom.contentWindow;
24871 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24875 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24876 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24881 initEditor : function(){
24882 //console.log("INIT EDITOR");
24883 this.assignDocWin();
24887 this.doc.designMode="on";
24889 this.doc.write(this.getDocMarkup());
24892 var dbody = (this.doc.body || this.doc.documentElement);
24893 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24894 // this copies styles from the containing element into thsi one..
24895 // not sure why we need all of this..
24896 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24898 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24899 //ss['background-attachment'] = 'fixed'; // w3c
24900 dbody.bgProperties = 'fixed'; // ie
24901 //Roo.DomHelper.applyStyles(dbody, ss);
24902 Roo.EventManager.on(this.doc, {
24903 //'mousedown': this.onEditorEvent,
24904 'mouseup': this.onEditorEvent,
24905 'dblclick': this.onEditorEvent,
24906 'click': this.onEditorEvent,
24907 'keyup': this.onEditorEvent,
24912 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24914 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24915 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24917 this.initialized = true;
24919 this.owner.fireEvent('initialize', this);
24924 onDestroy : function(){
24930 //for (var i =0; i < this.toolbars.length;i++) {
24931 // // fixme - ask toolbars for heights?
24932 // this.toolbars[i].onDestroy();
24935 //this.wrap.dom.innerHTML = '';
24936 //this.wrap.remove();
24941 onFirstFocus : function(){
24943 this.assignDocWin();
24946 this.activated = true;
24949 if(Roo.isGecko){ // prevent silly gecko errors
24951 var s = this.win.getSelection();
24952 if(!s.focusNode || s.focusNode.nodeType != 3){
24953 var r = s.getRangeAt(0);
24954 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24959 this.execCmd('useCSS', true);
24960 this.execCmd('styleWithCSS', false);
24963 this.owner.fireEvent('activate', this);
24967 adjustFont: function(btn){
24968 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24969 //if(Roo.isSafari){ // safari
24972 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24973 if(Roo.isSafari){ // safari
24974 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24975 v = (v < 10) ? 10 : v;
24976 v = (v > 48) ? 48 : v;
24977 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24982 v = Math.max(1, v+adjust);
24984 this.execCmd('FontSize', v );
24987 onEditorEvent : function(e)
24989 this.owner.fireEvent('editorevent', this, e);
24990 // this.updateToolbar();
24991 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24994 insertTag : function(tg)
24996 // could be a bit smarter... -> wrap the current selected tRoo..
24997 if (tg.toLowerCase() == 'span' ||
24998 tg.toLowerCase() == 'code' ||
24999 tg.toLowerCase() == 'sup' ||
25000 tg.toLowerCase() == 'sub'
25003 range = this.createRange(this.getSelection());
25004 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25005 wrappingNode.appendChild(range.extractContents());
25006 range.insertNode(wrappingNode);
25013 this.execCmd("formatblock", tg);
25017 insertText : function(txt)
25021 var range = this.createRange();
25022 range.deleteContents();
25023 //alert(Sender.getAttribute('label'));
25025 range.insertNode(this.doc.createTextNode(txt));
25031 * Executes a Midas editor command on the editor document and performs necessary focus and
25032 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25033 * @param {String} cmd The Midas command
25034 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25036 relayCmd : function(cmd, value){
25038 this.execCmd(cmd, value);
25039 this.owner.fireEvent('editorevent', this);
25040 //this.updateToolbar();
25041 this.owner.deferFocus();
25045 * Executes a Midas editor command directly on the editor document.
25046 * For visual commands, you should use {@link #relayCmd} instead.
25047 * <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 execCmd : function(cmd, value){
25052 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25059 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25061 * @param {String} text | dom node..
25063 insertAtCursor : function(text)
25066 if(!this.activated){
25072 var r = this.doc.selection.createRange();
25083 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25087 // from jquery ui (MIT licenced)
25089 var win = this.win;
25091 if (win.getSelection && win.getSelection().getRangeAt) {
25092 range = win.getSelection().getRangeAt(0);
25093 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25094 range.insertNode(node);
25095 } else if (win.document.selection && win.document.selection.createRange) {
25096 // no firefox support
25097 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25098 win.document.selection.createRange().pasteHTML(txt);
25100 // no firefox support
25101 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25102 this.execCmd('InsertHTML', txt);
25111 mozKeyPress : function(e){
25113 var c = e.getCharCode(), cmd;
25116 c = String.fromCharCode(c).toLowerCase();
25130 this.cleanUpPaste.defer(100, this);
25138 e.preventDefault();
25146 fixKeys : function(){ // load time branching for fastest keydown performance
25148 return function(e){
25149 var k = e.getKey(), r;
25152 r = this.doc.selection.createRange();
25155 r.pasteHTML('    ');
25162 r = this.doc.selection.createRange();
25164 var target = r.parentElement();
25165 if(!target || target.tagName.toLowerCase() != 'li'){
25167 r.pasteHTML('<br />');
25173 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25174 this.cleanUpPaste.defer(100, this);
25180 }else if(Roo.isOpera){
25181 return function(e){
25182 var k = e.getKey();
25186 this.execCmd('InsertHTML','    ');
25189 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25190 this.cleanUpPaste.defer(100, this);
25195 }else if(Roo.isSafari){
25196 return function(e){
25197 var k = e.getKey();
25201 this.execCmd('InsertText','\t');
25205 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25206 this.cleanUpPaste.defer(100, this);
25214 getAllAncestors: function()
25216 var p = this.getSelectedNode();
25219 a.push(p); // push blank onto stack..
25220 p = this.getParentElement();
25224 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25228 a.push(this.doc.body);
25232 lastSelNode : false,
25235 getSelection : function()
25237 this.assignDocWin();
25238 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25241 getSelectedNode: function()
25243 // this may only work on Gecko!!!
25245 // should we cache this!!!!
25250 var range = this.createRange(this.getSelection()).cloneRange();
25253 var parent = range.parentElement();
25255 var testRange = range.duplicate();
25256 testRange.moveToElementText(parent);
25257 if (testRange.inRange(range)) {
25260 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25263 parent = parent.parentElement;
25268 // is ancestor a text element.
25269 var ac = range.commonAncestorContainer;
25270 if (ac.nodeType == 3) {
25271 ac = ac.parentNode;
25274 var ar = ac.childNodes;
25277 var other_nodes = [];
25278 var has_other_nodes = false;
25279 for (var i=0;i<ar.length;i++) {
25280 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25283 // fullly contained node.
25285 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25290 // probably selected..
25291 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25292 other_nodes.push(ar[i]);
25296 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25301 has_other_nodes = true;
25303 if (!nodes.length && other_nodes.length) {
25304 nodes= other_nodes;
25306 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25312 createRange: function(sel)
25314 // this has strange effects when using with
25315 // top toolbar - not sure if it's a great idea.
25316 //this.editor.contentWindow.focus();
25317 if (typeof sel != "undefined") {
25319 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25321 return this.doc.createRange();
25324 return this.doc.createRange();
25327 getParentElement: function()
25330 this.assignDocWin();
25331 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25333 var range = this.createRange(sel);
25336 var p = range.commonAncestorContainer;
25337 while (p.nodeType == 3) { // text node
25348 * Range intersection.. the hard stuff...
25352 * [ -- selected range --- ]
25356 * if end is before start or hits it. fail.
25357 * if start is after end or hits it fail.
25359 * if either hits (but other is outside. - then it's not
25365 // @see http://www.thismuchiknow.co.uk/?p=64.
25366 rangeIntersectsNode : function(range, node)
25368 var nodeRange = node.ownerDocument.createRange();
25370 nodeRange.selectNode(node);
25372 nodeRange.selectNodeContents(node);
25375 var rangeStartRange = range.cloneRange();
25376 rangeStartRange.collapse(true);
25378 var rangeEndRange = range.cloneRange();
25379 rangeEndRange.collapse(false);
25381 var nodeStartRange = nodeRange.cloneRange();
25382 nodeStartRange.collapse(true);
25384 var nodeEndRange = nodeRange.cloneRange();
25385 nodeEndRange.collapse(false);
25387 return rangeStartRange.compareBoundaryPoints(
25388 Range.START_TO_START, nodeEndRange) == -1 &&
25389 rangeEndRange.compareBoundaryPoints(
25390 Range.START_TO_START, nodeStartRange) == 1;
25394 rangeCompareNode : function(range, node)
25396 var nodeRange = node.ownerDocument.createRange();
25398 nodeRange.selectNode(node);
25400 nodeRange.selectNodeContents(node);
25404 range.collapse(true);
25406 nodeRange.collapse(true);
25408 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25409 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25411 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25413 var nodeIsBefore = ss == 1;
25414 var nodeIsAfter = ee == -1;
25416 if (nodeIsBefore && nodeIsAfter) {
25419 if (!nodeIsBefore && nodeIsAfter) {
25420 return 1; //right trailed.
25423 if (nodeIsBefore && !nodeIsAfter) {
25424 return 2; // left trailed.
25430 // private? - in a new class?
25431 cleanUpPaste : function()
25433 // cleans up the whole document..
25434 Roo.log('cleanuppaste');
25436 this.cleanUpChildren(this.doc.body);
25437 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25438 if (clean != this.doc.body.innerHTML) {
25439 this.doc.body.innerHTML = clean;
25444 cleanWordChars : function(input) {// change the chars to hex code
25445 var he = Roo.HtmlEditorCore;
25447 var output = input;
25448 Roo.each(he.swapCodes, function(sw) {
25449 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25451 output = output.replace(swapper, sw[1]);
25458 cleanUpChildren : function (n)
25460 if (!n.childNodes.length) {
25463 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25464 this.cleanUpChild(n.childNodes[i]);
25471 cleanUpChild : function (node)
25474 //console.log(node);
25475 if (node.nodeName == "#text") {
25476 // clean up silly Windows -- stuff?
25479 if (node.nodeName == "#comment") {
25480 node.parentNode.removeChild(node);
25481 // clean up silly Windows -- stuff?
25484 var lcname = node.tagName.toLowerCase();
25485 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25486 // whitelist of tags..
25488 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25490 node.parentNode.removeChild(node);
25495 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25497 // spans with no attributes - just remove them..
25498 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25499 remove_keep_children = true;
25502 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25503 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25505 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25506 // remove_keep_children = true;
25509 if (remove_keep_children) {
25510 this.cleanUpChildren(node);
25511 // inserts everything just before this node...
25512 while (node.childNodes.length) {
25513 var cn = node.childNodes[0];
25514 node.removeChild(cn);
25515 node.parentNode.insertBefore(cn, node);
25517 node.parentNode.removeChild(node);
25521 if (!node.attributes || !node.attributes.length) {
25526 this.cleanUpChildren(node);
25530 function cleanAttr(n,v)
25533 if (v.match(/^\./) || v.match(/^\//)) {
25536 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25539 if (v.match(/^#/)) {
25542 if (v.match(/^\{/)) { // allow template editing.
25545 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25546 node.removeAttribute(n);
25550 var cwhite = this.cwhite;
25551 var cblack = this.cblack;
25553 function cleanStyle(n,v)
25555 if (v.match(/expression/)) { //XSS?? should we even bother..
25556 node.removeAttribute(n);
25560 var parts = v.split(/;/);
25563 Roo.each(parts, function(p) {
25564 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25568 var l = p.split(':').shift().replace(/\s+/g,'');
25569 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25571 if ( cwhite.length && cblack.indexOf(l) > -1) {
25572 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25573 //node.removeAttribute(n);
25577 // only allow 'c whitelisted system attributes'
25578 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25579 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25580 //node.removeAttribute(n);
25590 if (clean.length) {
25591 node.setAttribute(n, clean.join(';'));
25593 node.removeAttribute(n);
25599 for (var i = node.attributes.length-1; i > -1 ; i--) {
25600 var a = node.attributes[i];
25603 if (a.name.toLowerCase().substr(0,2)=='on') {
25604 node.removeAttribute(a.name);
25607 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25608 node.removeAttribute(a.name);
25611 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25612 cleanAttr(a.name,a.value); // fixme..
25615 if (a.name == 'style') {
25616 cleanStyle(a.name,a.value);
25619 /// clean up MS crap..
25620 // tecnically this should be a list of valid class'es..
25623 if (a.name == 'class') {
25624 if (a.value.match(/^Mso/)) {
25625 node.removeAttribute('class');
25628 if (a.value.match(/^body$/)) {
25629 node.removeAttribute('class');
25640 this.cleanUpChildren(node);
25646 * Clean up MS wordisms...
25648 cleanWord : function(node)
25651 this.cleanWord(this.doc.body);
25656 node.nodeName == 'SPAN' &&
25657 !node.hasAttributes() &&
25658 node.childNodes.length == 1 &&
25659 node.firstChild.nodeName == "#text"
25661 var textNode = node.firstChild;
25662 node.removeChild(textNode);
25663 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25664 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25666 node.parentNode.insertBefore(textNode, node);
25667 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25668 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25670 node.parentNode.removeChild(node);
25673 if (node.nodeName == "#text") {
25674 // clean up silly Windows -- stuff?
25677 if (node.nodeName == "#comment") {
25678 node.parentNode.removeChild(node);
25679 // clean up silly Windows -- stuff?
25683 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25684 node.parentNode.removeChild(node);
25687 //Roo.log(node.tagName);
25688 // remove - but keep children..
25689 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25690 //Roo.log('-- removed');
25691 while (node.childNodes.length) {
25692 var cn = node.childNodes[0];
25693 node.removeChild(cn);
25694 node.parentNode.insertBefore(cn, node);
25695 // move node to parent - and clean it..
25696 this.cleanWord(cn);
25698 node.parentNode.removeChild(node);
25699 /// no need to iterate chidlren = it's got none..
25700 //this.iterateChildren(node, this.cleanWord);
25704 if (node.className.length) {
25706 var cn = node.className.split(/\W+/);
25708 Roo.each(cn, function(cls) {
25709 if (cls.match(/Mso[a-zA-Z]+/)) {
25714 node.className = cna.length ? cna.join(' ') : '';
25716 node.removeAttribute("class");
25720 if (node.hasAttribute("lang")) {
25721 node.removeAttribute("lang");
25724 if (node.hasAttribute("style")) {
25726 var styles = node.getAttribute("style").split(";");
25728 Roo.each(styles, function(s) {
25729 if (!s.match(/:/)) {
25732 var kv = s.split(":");
25733 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25736 // what ever is left... we allow.
25739 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25740 if (!nstyle.length) {
25741 node.removeAttribute('style');
25744 this.iterateChildren(node, this.cleanWord);
25750 * iterateChildren of a Node, calling fn each time, using this as the scole..
25751 * @param {DomNode} node node to iterate children of.
25752 * @param {Function} fn method of this class to call on each item.
25754 iterateChildren : function(node, fn)
25756 if (!node.childNodes.length) {
25759 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25760 fn.call(this, node.childNodes[i])
25766 * cleanTableWidths.
25768 * Quite often pasting from word etc.. results in tables with column and widths.
25769 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25772 cleanTableWidths : function(node)
25777 this.cleanTableWidths(this.doc.body);
25782 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25785 Roo.log(node.tagName);
25786 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25787 this.iterateChildren(node, this.cleanTableWidths);
25790 if (node.hasAttribute('width')) {
25791 node.removeAttribute('width');
25795 if (node.hasAttribute("style")) {
25798 var styles = node.getAttribute("style").split(";");
25800 Roo.each(styles, function(s) {
25801 if (!s.match(/:/)) {
25804 var kv = s.split(":");
25805 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25808 // what ever is left... we allow.
25811 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25812 if (!nstyle.length) {
25813 node.removeAttribute('style');
25817 this.iterateChildren(node, this.cleanTableWidths);
25825 domToHTML : function(currentElement, depth, nopadtext) {
25827 depth = depth || 0;
25828 nopadtext = nopadtext || false;
25830 if (!currentElement) {
25831 return this.domToHTML(this.doc.body);
25834 //Roo.log(currentElement);
25836 var allText = false;
25837 var nodeName = currentElement.nodeName;
25838 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25840 if (nodeName == '#text') {
25842 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25847 if (nodeName != 'BODY') {
25850 // Prints the node tagName, such as <A>, <IMG>, etc
25853 for(i = 0; i < currentElement.attributes.length;i++) {
25855 var aname = currentElement.attributes.item(i).name;
25856 if (!currentElement.attributes.item(i).value.length) {
25859 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25862 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25871 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25874 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25879 // Traverse the tree
25881 var currentElementChild = currentElement.childNodes.item(i);
25882 var allText = true;
25883 var innerHTML = '';
25885 while (currentElementChild) {
25886 // Formatting code (indent the tree so it looks nice on the screen)
25887 var nopad = nopadtext;
25888 if (lastnode == 'SPAN') {
25892 if (currentElementChild.nodeName == '#text') {
25893 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25894 toadd = nopadtext ? toadd : toadd.trim();
25895 if (!nopad && toadd.length > 80) {
25896 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25898 innerHTML += toadd;
25901 currentElementChild = currentElement.childNodes.item(i);
25907 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25909 // Recursively traverse the tree structure of the child node
25910 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25911 lastnode = currentElementChild.nodeName;
25913 currentElementChild=currentElement.childNodes.item(i);
25919 // The remaining code is mostly for formatting the tree
25920 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25925 ret+= "</"+tagName+">";
25931 applyBlacklists : function()
25933 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25934 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25938 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25939 if (b.indexOf(tag) > -1) {
25942 this.white.push(tag);
25946 Roo.each(w, function(tag) {
25947 if (b.indexOf(tag) > -1) {
25950 if (this.white.indexOf(tag) > -1) {
25953 this.white.push(tag);
25958 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25959 if (w.indexOf(tag) > -1) {
25962 this.black.push(tag);
25966 Roo.each(b, function(tag) {
25967 if (w.indexOf(tag) > -1) {
25970 if (this.black.indexOf(tag) > -1) {
25973 this.black.push(tag);
25978 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25979 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25983 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25984 if (b.indexOf(tag) > -1) {
25987 this.cwhite.push(tag);
25991 Roo.each(w, function(tag) {
25992 if (b.indexOf(tag) > -1) {
25995 if (this.cwhite.indexOf(tag) > -1) {
25998 this.cwhite.push(tag);
26003 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26004 if (w.indexOf(tag) > -1) {
26007 this.cblack.push(tag);
26011 Roo.each(b, function(tag) {
26012 if (w.indexOf(tag) > -1) {
26015 if (this.cblack.indexOf(tag) > -1) {
26018 this.cblack.push(tag);
26023 setStylesheets : function(stylesheets)
26025 if(typeof(stylesheets) == 'string'){
26026 Roo.get(this.iframe.contentDocument.head).createChild({
26028 rel : 'stylesheet',
26037 Roo.each(stylesheets, function(s) {
26042 Roo.get(_this.iframe.contentDocument.head).createChild({
26044 rel : 'stylesheet',
26053 removeStylesheets : function()
26057 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26062 setStyle : function(style)
26064 Roo.get(this.iframe.contentDocument.head).createChild({
26073 // hide stuff that is not compatible
26087 * @event specialkey
26091 * @cfg {String} fieldClass @hide
26094 * @cfg {String} focusClass @hide
26097 * @cfg {String} autoCreate @hide
26100 * @cfg {String} inputType @hide
26103 * @cfg {String} invalidClass @hide
26106 * @cfg {String} invalidText @hide
26109 * @cfg {String} msgFx @hide
26112 * @cfg {String} validateOnBlur @hide
26116 Roo.HtmlEditorCore.white = [
26117 'area', 'br', 'img', 'input', 'hr', 'wbr',
26119 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26120 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26121 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26122 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26123 'table', 'ul', 'xmp',
26125 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26128 'dir', 'menu', 'ol', 'ul', 'dl',
26134 Roo.HtmlEditorCore.black = [
26135 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26137 'base', 'basefont', 'bgsound', 'blink', 'body',
26138 'frame', 'frameset', 'head', 'html', 'ilayer',
26139 'iframe', 'layer', 'link', 'meta', 'object',
26140 'script', 'style' ,'title', 'xml' // clean later..
26142 Roo.HtmlEditorCore.clean = [
26143 'script', 'style', 'title', 'xml'
26145 Roo.HtmlEditorCore.remove = [
26150 Roo.HtmlEditorCore.ablack = [
26154 Roo.HtmlEditorCore.aclean = [
26155 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26159 Roo.HtmlEditorCore.pwhite= [
26160 'http', 'https', 'mailto'
26163 // white listed style attributes.
26164 Roo.HtmlEditorCore.cwhite= [
26165 // 'text-align', /// default is to allow most things..
26171 // black listed style attributes.
26172 Roo.HtmlEditorCore.cblack= [
26173 // 'font-size' -- this can be set by the project
26177 Roo.HtmlEditorCore.swapCodes =[
26178 [ 8211, "–" ],
26179 [ 8212, "—" ],
26196 * @class Roo.bootstrap.HtmlEditor
26197 * @extends Roo.bootstrap.TextArea
26198 * Bootstrap HtmlEditor class
26201 * Create a new HtmlEditor
26202 * @param {Object} config The config object
26205 Roo.bootstrap.HtmlEditor = function(config){
26206 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26207 if (!this.toolbars) {
26208 this.toolbars = [];
26211 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26214 * @event initialize
26215 * Fires when the editor is fully initialized (including the iframe)
26216 * @param {HtmlEditor} this
26221 * Fires when the editor is first receives the focus. Any insertion must wait
26222 * until after this event.
26223 * @param {HtmlEditor} this
26227 * @event beforesync
26228 * Fires before the textarea is updated with content from the editor iframe. Return false
26229 * to cancel the sync.
26230 * @param {HtmlEditor} this
26231 * @param {String} html
26235 * @event beforepush
26236 * Fires before the iframe editor is updated with content from the textarea. Return false
26237 * to cancel the push.
26238 * @param {HtmlEditor} this
26239 * @param {String} html
26244 * Fires when the textarea is updated with content from the editor iframe.
26245 * @param {HtmlEditor} this
26246 * @param {String} html
26251 * Fires when the iframe editor is updated with content from the textarea.
26252 * @param {HtmlEditor} this
26253 * @param {String} html
26257 * @event editmodechange
26258 * Fires when the editor switches edit modes
26259 * @param {HtmlEditor} this
26260 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26262 editmodechange: true,
26264 * @event editorevent
26265 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26266 * @param {HtmlEditor} this
26270 * @event firstfocus
26271 * Fires when on first focus - needed by toolbars..
26272 * @param {HtmlEditor} this
26277 * Auto save the htmlEditor value as a file into Events
26278 * @param {HtmlEditor} this
26282 * @event savedpreview
26283 * preview the saved version of htmlEditor
26284 * @param {HtmlEditor} this
26291 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26295 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26300 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26305 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26310 * @cfg {Number} height (in pixels)
26314 * @cfg {Number} width (in pixels)
26319 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26322 stylesheets: false,
26327 // private properties
26328 validationEvent : false,
26330 initialized : false,
26333 onFocus : Roo.emptyFn,
26335 hideMode:'offsets',
26337 tbContainer : false,
26341 toolbarContainer :function() {
26342 return this.wrap.select('.x-html-editor-tb',true).first();
26346 * Protected method that will not generally be called directly. It
26347 * is called when the editor creates its toolbar. Override this method if you need to
26348 * add custom toolbar buttons.
26349 * @param {HtmlEditor} editor
26351 createToolbar : function(){
26352 Roo.log('renewing');
26353 Roo.log("create toolbars");
26355 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26356 this.toolbars[0].render(this.toolbarContainer());
26360 // if (!editor.toolbars || !editor.toolbars.length) {
26361 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26364 // for (var i =0 ; i < editor.toolbars.length;i++) {
26365 // editor.toolbars[i] = Roo.factory(
26366 // typeof(editor.toolbars[i]) == 'string' ?
26367 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26368 // Roo.bootstrap.HtmlEditor);
26369 // editor.toolbars[i].init(editor);
26375 onRender : function(ct, position)
26377 // Roo.log("Call onRender: " + this.xtype);
26379 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26381 this.wrap = this.inputEl().wrap({
26382 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26385 this.editorcore.onRender(ct, position);
26387 if (this.resizable) {
26388 this.resizeEl = new Roo.Resizable(this.wrap, {
26392 minHeight : this.height,
26393 height: this.height,
26394 handles : this.resizable,
26397 resize : function(r, w, h) {
26398 _t.onResize(w,h); // -something
26404 this.createToolbar(this);
26407 if(!this.width && this.resizable){
26408 this.setSize(this.wrap.getSize());
26410 if (this.resizeEl) {
26411 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26412 // should trigger onReize..
26418 onResize : function(w, h)
26420 Roo.log('resize: ' +w + ',' + h );
26421 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26425 if(this.inputEl() ){
26426 if(typeof w == 'number'){
26427 var aw = w - this.wrap.getFrameWidth('lr');
26428 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26431 if(typeof h == 'number'){
26432 var tbh = -11; // fixme it needs to tool bar size!
26433 for (var i =0; i < this.toolbars.length;i++) {
26434 // fixme - ask toolbars for heights?
26435 tbh += this.toolbars[i].el.getHeight();
26436 //if (this.toolbars[i].footer) {
26437 // tbh += this.toolbars[i].footer.el.getHeight();
26445 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26446 ah -= 5; // knock a few pixes off for look..
26447 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26451 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26452 this.editorcore.onResize(ew,eh);
26457 * Toggles the editor between standard and source edit mode.
26458 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26460 toggleSourceEdit : function(sourceEditMode)
26462 this.editorcore.toggleSourceEdit(sourceEditMode);
26464 if(this.editorcore.sourceEditMode){
26465 Roo.log('editor - showing textarea');
26468 // Roo.log(this.syncValue());
26470 this.inputEl().removeClass(['hide', 'x-hidden']);
26471 this.inputEl().dom.removeAttribute('tabIndex');
26472 this.inputEl().focus();
26474 Roo.log('editor - hiding textarea');
26476 // Roo.log(this.pushValue());
26479 this.inputEl().addClass(['hide', 'x-hidden']);
26480 this.inputEl().dom.setAttribute('tabIndex', -1);
26481 //this.deferFocus();
26484 if(this.resizable){
26485 this.setSize(this.wrap.getSize());
26488 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26491 // private (for BoxComponent)
26492 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26494 // private (for BoxComponent)
26495 getResizeEl : function(){
26499 // private (for BoxComponent)
26500 getPositionEl : function(){
26505 initEvents : function(){
26506 this.originalValue = this.getValue();
26510 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26513 // markInvalid : Roo.emptyFn,
26515 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26518 // clearInvalid : Roo.emptyFn,
26520 setValue : function(v){
26521 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26522 this.editorcore.pushValue();
26527 deferFocus : function(){
26528 this.focus.defer(10, this);
26532 focus : function(){
26533 this.editorcore.focus();
26539 onDestroy : function(){
26545 for (var i =0; i < this.toolbars.length;i++) {
26546 // fixme - ask toolbars for heights?
26547 this.toolbars[i].onDestroy();
26550 this.wrap.dom.innerHTML = '';
26551 this.wrap.remove();
26556 onFirstFocus : function(){
26557 //Roo.log("onFirstFocus");
26558 this.editorcore.onFirstFocus();
26559 for (var i =0; i < this.toolbars.length;i++) {
26560 this.toolbars[i].onFirstFocus();
26566 syncValue : function()
26568 this.editorcore.syncValue();
26571 pushValue : function()
26573 this.editorcore.pushValue();
26577 // hide stuff that is not compatible
26591 * @event specialkey
26595 * @cfg {String} fieldClass @hide
26598 * @cfg {String} focusClass @hide
26601 * @cfg {String} autoCreate @hide
26604 * @cfg {String} inputType @hide
26608 * @cfg {String} invalidText @hide
26611 * @cfg {String} msgFx @hide
26614 * @cfg {String} validateOnBlur @hide
26623 Roo.namespace('Roo.bootstrap.htmleditor');
26625 * @class Roo.bootstrap.HtmlEditorToolbar1
26631 new Roo.bootstrap.HtmlEditor({
26634 new Roo.bootstrap.HtmlEditorToolbar1({
26635 disable : { fonts: 1 , format: 1, ..., ... , ...],
26641 * @cfg {Object} disable List of elements to disable..
26642 * @cfg {Array} btns List of additional buttons.
26646 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26649 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26652 Roo.apply(this, config);
26654 // default disabled, based on 'good practice'..
26655 this.disable = this.disable || {};
26656 Roo.applyIf(this.disable, {
26659 specialElements : true
26661 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26663 this.editor = config.editor;
26664 this.editorcore = config.editor.editorcore;
26666 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26668 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26669 // dont call parent... till later.
26671 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26676 editorcore : false,
26681 "h1","h2","h3","h4","h5","h6",
26683 "abbr", "acronym", "address", "cite", "samp", "var",
26687 onRender : function(ct, position)
26689 // Roo.log("Call onRender: " + this.xtype);
26691 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26693 this.el.dom.style.marginBottom = '0';
26695 var editorcore = this.editorcore;
26696 var editor= this.editor;
26699 var btn = function(id,cmd , toggle, handler, html){
26701 var event = toggle ? 'toggle' : 'click';
26706 xns: Roo.bootstrap,
26710 enableToggle:toggle !== false,
26712 pressed : toggle ? false : null,
26715 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26716 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26722 // var cb_box = function...
26727 xns: Roo.bootstrap,
26732 xns: Roo.bootstrap,
26736 Roo.each(this.formats, function(f) {
26737 style.menu.items.push({
26739 xns: Roo.bootstrap,
26740 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26745 editorcore.insertTag(this.tagname);
26752 children.push(style);
26754 btn('bold',false,true);
26755 btn('italic',false,true);
26756 btn('align-left', 'justifyleft',true);
26757 btn('align-center', 'justifycenter',true);
26758 btn('align-right' , 'justifyright',true);
26759 btn('link', false, false, function(btn) {
26760 //Roo.log("create link?");
26761 var url = prompt(this.createLinkText, this.defaultLinkValue);
26762 if(url && url != 'http:/'+'/'){
26763 this.editorcore.relayCmd('createlink', url);
26766 btn('list','insertunorderedlist',true);
26767 btn('pencil', false,true, function(btn){
26769 this.toggleSourceEdit(btn.pressed);
26772 if (this.editor.btns.length > 0) {
26773 for (var i = 0; i<this.editor.btns.length; i++) {
26774 children.push(this.editor.btns[i]);
26782 xns: Roo.bootstrap,
26787 xns: Roo.bootstrap,
26792 cog.menu.items.push({
26794 xns: Roo.bootstrap,
26795 html : Clean styles,
26800 editorcore.insertTag(this.tagname);
26809 this.xtype = 'NavSimplebar';
26811 for(var i=0;i< children.length;i++) {
26813 this.buttons.add(this.addxtypeChild(children[i]));
26817 editor.on('editorevent', this.updateToolbar, this);
26819 onBtnClick : function(id)
26821 this.editorcore.relayCmd(id);
26822 this.editorcore.focus();
26826 * Protected method that will not generally be called directly. It triggers
26827 * a toolbar update by reading the markup state of the current selection in the editor.
26829 updateToolbar: function(){
26831 if(!this.editorcore.activated){
26832 this.editor.onFirstFocus(); // is this neeed?
26836 var btns = this.buttons;
26837 var doc = this.editorcore.doc;
26838 btns.get('bold').setActive(doc.queryCommandState('bold'));
26839 btns.get('italic').setActive(doc.queryCommandState('italic'));
26840 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26842 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26843 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26844 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26846 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26847 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26850 var ans = this.editorcore.getAllAncestors();
26851 if (this.formatCombo) {
26854 var store = this.formatCombo.store;
26855 this.formatCombo.setValue("");
26856 for (var i =0; i < ans.length;i++) {
26857 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26859 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26867 // hides menus... - so this cant be on a menu...
26868 Roo.bootstrap.MenuMgr.hideAll();
26870 Roo.bootstrap.MenuMgr.hideAll();
26871 //this.editorsyncValue();
26873 onFirstFocus: function() {
26874 this.buttons.each(function(item){
26878 toggleSourceEdit : function(sourceEditMode){
26881 if(sourceEditMode){
26882 Roo.log("disabling buttons");
26883 this.buttons.each( function(item){
26884 if(item.cmd != 'pencil'){
26890 Roo.log("enabling buttons");
26891 if(this.editorcore.initialized){
26892 this.buttons.each( function(item){
26898 Roo.log("calling toggole on editor");
26899 // tell the editor that it's been pressed..
26900 this.editor.toggleSourceEdit(sourceEditMode);
26914 * @class Roo.bootstrap.Markdown
26915 * @extends Roo.bootstrap.TextArea
26916 * Bootstrap Showdown editable area
26917 * @cfg {string} content
26920 * Create a new Showdown
26923 Roo.bootstrap.Markdown = function(config){
26924 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26928 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26932 initEvents : function()
26935 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26936 this.markdownEl = this.el.createChild({
26937 cls : 'roo-markdown-area'
26939 this.inputEl().addClass('d-none');
26940 if (this.getValue() == '') {
26941 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26944 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26946 this.markdownEl.on('click', this.toggleTextEdit, this);
26947 this.on('blur', this.toggleTextEdit, this);
26948 this.on('specialkey', this.resizeTextArea, this);
26951 toggleTextEdit : function()
26953 var sh = this.markdownEl.getHeight();
26954 this.inputEl().addClass('d-none');
26955 this.markdownEl.addClass('d-none');
26956 if (!this.editing) {
26958 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26959 this.inputEl().removeClass('d-none');
26960 this.inputEl().focus();
26961 this.editing = true;
26964 // show showdown...
26965 this.updateMarkdown();
26966 this.markdownEl.removeClass('d-none');
26967 this.editing = false;
26970 updateMarkdown : function()
26972 if (this.getValue() == '') {
26973 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26977 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26980 resizeTextArea: function () {
26983 Roo.log([sh, this.getValue().split("\n").length * 30]);
26984 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26986 setValue : function(val)
26988 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26989 if (!this.editing) {
26990 this.updateMarkdown();
26996 if (!this.editing) {
26997 this.toggleTextEdit();
27005 * @class Roo.bootstrap.Table.AbstractSelectionModel
27006 * @extends Roo.util.Observable
27007 * Abstract base class for grid SelectionModels. It provides the interface that should be
27008 * implemented by descendant classes. This class should not be directly instantiated.
27011 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27012 this.locked = false;
27013 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27017 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27018 /** @ignore Called by the grid automatically. Do not call directly. */
27019 init : function(grid){
27025 * Locks the selections.
27028 this.locked = true;
27032 * Unlocks the selections.
27034 unlock : function(){
27035 this.locked = false;
27039 * Returns true if the selections are locked.
27040 * @return {Boolean}
27042 isLocked : function(){
27043 return this.locked;
27047 initEvents : function ()
27053 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27054 * @class Roo.bootstrap.Table.RowSelectionModel
27055 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27056 * It supports multiple selections and keyboard selection/navigation.
27058 * @param {Object} config
27061 Roo.bootstrap.Table.RowSelectionModel = function(config){
27062 Roo.apply(this, config);
27063 this.selections = new Roo.util.MixedCollection(false, function(o){
27068 this.lastActive = false;
27072 * @event selectionchange
27073 * Fires when the selection changes
27074 * @param {SelectionModel} this
27076 "selectionchange" : true,
27078 * @event afterselectionchange
27079 * Fires after the selection changes (eg. by key press or clicking)
27080 * @param {SelectionModel} this
27082 "afterselectionchange" : true,
27084 * @event beforerowselect
27085 * Fires when a row is selected being selected, return false to cancel.
27086 * @param {SelectionModel} this
27087 * @param {Number} rowIndex The selected index
27088 * @param {Boolean} keepExisting False if other selections will be cleared
27090 "beforerowselect" : true,
27093 * Fires when a row is selected.
27094 * @param {SelectionModel} this
27095 * @param {Number} rowIndex The selected index
27096 * @param {Roo.data.Record} r The record
27098 "rowselect" : true,
27100 * @event rowdeselect
27101 * Fires when a row is deselected.
27102 * @param {SelectionModel} this
27103 * @param {Number} rowIndex The selected index
27105 "rowdeselect" : true
27107 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27108 this.locked = false;
27111 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27113 * @cfg {Boolean} singleSelect
27114 * True to allow selection of only one row at a time (defaults to false)
27116 singleSelect : false,
27119 initEvents : function()
27122 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27123 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27124 //}else{ // allow click to work like normal
27125 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27127 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27128 this.grid.on("rowclick", this.handleMouseDown, this);
27130 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27131 "up" : function(e){
27133 this.selectPrevious(e.shiftKey);
27134 }else if(this.last !== false && this.lastActive !== false){
27135 var last = this.last;
27136 this.selectRange(this.last, this.lastActive-1);
27137 this.grid.getView().focusRow(this.lastActive);
27138 if(last !== false){
27142 this.selectFirstRow();
27144 this.fireEvent("afterselectionchange", this);
27146 "down" : function(e){
27148 this.selectNext(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);
27163 this.grid.store.on('load', function(){
27164 this.selections.clear();
27167 var view = this.grid.view;
27168 view.on("refresh", this.onRefresh, this);
27169 view.on("rowupdated", this.onRowUpdated, this);
27170 view.on("rowremoved", this.onRemove, this);
27175 onRefresh : function()
27177 var ds = this.grid.store, i, v = this.grid.view;
27178 var s = this.selections;
27179 s.each(function(r){
27180 if((i = ds.indexOfId(r.id)) != -1){
27189 onRemove : function(v, index, r){
27190 this.selections.remove(r);
27194 onRowUpdated : function(v, index, r){
27195 if(this.isSelected(r)){
27196 v.onRowSelect(index);
27202 * @param {Array} records The records to select
27203 * @param {Boolean} keepExisting (optional) True to keep existing selections
27205 selectRecords : function(records, keepExisting)
27208 this.clearSelections();
27210 var ds = this.grid.store;
27211 for(var i = 0, len = records.length; i < len; i++){
27212 this.selectRow(ds.indexOf(records[i]), true);
27217 * Gets the number of selected rows.
27220 getCount : function(){
27221 return this.selections.length;
27225 * Selects the first row in the grid.
27227 selectFirstRow : function(){
27232 * Select the last row.
27233 * @param {Boolean} keepExisting (optional) True to keep existing selections
27235 selectLastRow : function(keepExisting){
27236 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27237 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27241 * Selects the row immediately following the last selected row.
27242 * @param {Boolean} keepExisting (optional) True to keep existing selections
27244 selectNext : function(keepExisting)
27246 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27247 this.selectRow(this.last+1, keepExisting);
27248 this.grid.getView().focusRow(this.last);
27253 * Selects the row that precedes the last selected row.
27254 * @param {Boolean} keepExisting (optional) True to keep existing selections
27256 selectPrevious : function(keepExisting){
27258 this.selectRow(this.last-1, keepExisting);
27259 this.grid.getView().focusRow(this.last);
27264 * Returns the selected records
27265 * @return {Array} Array of selected records
27267 getSelections : function(){
27268 return [].concat(this.selections.items);
27272 * Returns the first selected record.
27275 getSelected : function(){
27276 return this.selections.itemAt(0);
27281 * Clears all selections.
27283 clearSelections : function(fast)
27289 var ds = this.grid.store;
27290 var s = this.selections;
27291 s.each(function(r){
27292 this.deselectRow(ds.indexOfId(r.id));
27296 this.selections.clear();
27303 * Selects all rows.
27305 selectAll : function(){
27309 this.selections.clear();
27310 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27311 this.selectRow(i, true);
27316 * Returns True if there is a selection.
27317 * @return {Boolean}
27319 hasSelection : function(){
27320 return this.selections.length > 0;
27324 * Returns True if the specified row is selected.
27325 * @param {Number/Record} record The record or index of the record to check
27326 * @return {Boolean}
27328 isSelected : function(index){
27329 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27330 return (r && this.selections.key(r.id) ? true : false);
27334 * Returns True if the specified record id is selected.
27335 * @param {String} id The id of record to check
27336 * @return {Boolean}
27338 isIdSelected : function(id){
27339 return (this.selections.key(id) ? true : false);
27344 handleMouseDBClick : function(e, t){
27348 handleMouseDown : function(e, t)
27350 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27351 if(this.isLocked() || rowIndex < 0 ){
27354 if(e.shiftKey && this.last !== false){
27355 var last = this.last;
27356 this.selectRange(last, rowIndex, e.ctrlKey);
27357 this.last = last; // reset the last
27361 var isSelected = this.isSelected(rowIndex);
27362 //Roo.log("select row:" + rowIndex);
27364 this.deselectRow(rowIndex);
27366 this.selectRow(rowIndex, true);
27370 if(e.button !== 0 && isSelected){
27371 alert('rowIndex 2: ' + rowIndex);
27372 view.focusRow(rowIndex);
27373 }else if(e.ctrlKey && isSelected){
27374 this.deselectRow(rowIndex);
27375 }else if(!isSelected){
27376 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27377 view.focusRow(rowIndex);
27381 this.fireEvent("afterselectionchange", this);
27384 handleDragableRowClick : function(grid, rowIndex, e)
27386 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27387 this.selectRow(rowIndex, false);
27388 grid.view.focusRow(rowIndex);
27389 this.fireEvent("afterselectionchange", this);
27394 * Selects multiple rows.
27395 * @param {Array} rows Array of the indexes of the row to select
27396 * @param {Boolean} keepExisting (optional) True to keep existing selections
27398 selectRows : function(rows, keepExisting){
27400 this.clearSelections();
27402 for(var i = 0, len = rows.length; i < len; i++){
27403 this.selectRow(rows[i], true);
27408 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27409 * @param {Number} startRow The index of the first row in the range
27410 * @param {Number} endRow The index of the last row in the range
27411 * @param {Boolean} keepExisting (optional) True to retain existing selections
27413 selectRange : function(startRow, endRow, keepExisting){
27418 this.clearSelections();
27420 if(startRow <= endRow){
27421 for(var i = startRow; i <= endRow; i++){
27422 this.selectRow(i, true);
27425 for(var i = startRow; i >= endRow; i--){
27426 this.selectRow(i, true);
27432 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27433 * @param {Number} startRow The index of the first row in the range
27434 * @param {Number} endRow The index of the last row in the range
27436 deselectRange : function(startRow, endRow, preventViewNotify){
27440 for(var i = startRow; i <= endRow; i++){
27441 this.deselectRow(i, preventViewNotify);
27447 * @param {Number} row The index of the row to select
27448 * @param {Boolean} keepExisting (optional) True to keep existing selections
27450 selectRow : function(index, keepExisting, preventViewNotify)
27452 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27455 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27456 if(!keepExisting || this.singleSelect){
27457 this.clearSelections();
27460 var r = this.grid.store.getAt(index);
27461 //console.log('selectRow - record id :' + r.id);
27463 this.selections.add(r);
27464 this.last = this.lastActive = index;
27465 if(!preventViewNotify){
27466 var proxy = new Roo.Element(
27467 this.grid.getRowDom(index)
27469 proxy.addClass('bg-info info');
27471 this.fireEvent("rowselect", this, index, r);
27472 this.fireEvent("selectionchange", this);
27478 * @param {Number} row The index of the row to deselect
27480 deselectRow : function(index, preventViewNotify)
27485 if(this.last == index){
27488 if(this.lastActive == index){
27489 this.lastActive = false;
27492 var r = this.grid.store.getAt(index);
27497 this.selections.remove(r);
27498 //.console.log('deselectRow - record id :' + r.id);
27499 if(!preventViewNotify){
27501 var proxy = new Roo.Element(
27502 this.grid.getRowDom(index)
27504 proxy.removeClass('bg-info info');
27506 this.fireEvent("rowdeselect", this, index);
27507 this.fireEvent("selectionchange", this);
27511 restoreLast : function(){
27513 this.last = this._last;
27518 acceptsNav : function(row, col, cm){
27519 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27523 onEditorKey : function(field, e){
27524 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27529 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27531 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27533 }else if(k == e.ENTER && !e.ctrlKey){
27537 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27539 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27541 }else if(k == e.ESC){
27545 g.startEditing(newCell[0], newCell[1]);
27551 * Ext JS Library 1.1.1
27552 * Copyright(c) 2006-2007, Ext JS, LLC.
27554 * Originally Released Under LGPL - original licence link has changed is not relivant.
27557 * <script type="text/javascript">
27561 * @class Roo.bootstrap.PagingToolbar
27562 * @extends Roo.bootstrap.NavSimplebar
27563 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27565 * Create a new PagingToolbar
27566 * @param {Object} config The config object
27567 * @param {Roo.data.Store} store
27569 Roo.bootstrap.PagingToolbar = function(config)
27571 // old args format still supported... - xtype is prefered..
27572 // created from xtype...
27574 this.ds = config.dataSource;
27576 if (config.store && !this.ds) {
27577 this.store= Roo.factory(config.store, Roo.data);
27578 this.ds = this.store;
27579 this.ds.xmodule = this.xmodule || false;
27582 this.toolbarItems = [];
27583 if (config.items) {
27584 this.toolbarItems = config.items;
27587 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27592 this.bind(this.ds);
27595 if (Roo.bootstrap.version == 4) {
27596 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27598 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27603 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27605 * @cfg {Roo.data.Store} dataSource
27606 * The underlying data store providing the paged data
27609 * @cfg {String/HTMLElement/Element} container
27610 * container The id or element that will contain the toolbar
27613 * @cfg {Boolean} displayInfo
27614 * True to display the displayMsg (defaults to false)
27617 * @cfg {Number} pageSize
27618 * The number of records to display per page (defaults to 20)
27622 * @cfg {String} displayMsg
27623 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27625 displayMsg : 'Displaying {0} - {1} of {2}',
27627 * @cfg {String} emptyMsg
27628 * The message to display when no records are found (defaults to "No data to display")
27630 emptyMsg : 'No data to display',
27632 * Customizable piece of the default paging text (defaults to "Page")
27635 beforePageText : "Page",
27637 * Customizable piece of the default paging text (defaults to "of %0")
27640 afterPageText : "of {0}",
27642 * Customizable piece of the default paging text (defaults to "First Page")
27645 firstText : "First Page",
27647 * Customizable piece of the default paging text (defaults to "Previous Page")
27650 prevText : "Previous Page",
27652 * Customizable piece of the default paging text (defaults to "Next Page")
27655 nextText : "Next Page",
27657 * Customizable piece of the default paging text (defaults to "Last Page")
27660 lastText : "Last Page",
27662 * Customizable piece of the default paging text (defaults to "Refresh")
27665 refreshText : "Refresh",
27669 onRender : function(ct, position)
27671 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27672 this.navgroup.parentId = this.id;
27673 this.navgroup.onRender(this.el, null);
27674 // add the buttons to the navgroup
27676 if(this.displayInfo){
27677 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27678 this.displayEl = this.el.select('.x-paging-info', true).first();
27679 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27680 // this.displayEl = navel.el.select('span',true).first();
27686 Roo.each(_this.buttons, function(e){ // this might need to use render????
27687 Roo.factory(e).render(_this.el);
27691 Roo.each(_this.toolbarItems, function(e) {
27692 _this.navgroup.addItem(e);
27696 this.first = this.navgroup.addItem({
27697 tooltip: this.firstText,
27698 cls: "prev btn-outline-secondary",
27699 html : ' <i class="fa fa-step-backward"></i>',
27701 preventDefault: true,
27702 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27705 this.prev = this.navgroup.addItem({
27706 tooltip: this.prevText,
27707 cls: "prev btn-outline-secondary",
27708 html : ' <i class="fa fa-backward"></i>',
27710 preventDefault: true,
27711 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27713 //this.addSeparator();
27716 var field = this.navgroup.addItem( {
27718 cls : 'x-paging-position btn-outline-secondary',
27720 html : this.beforePageText +
27721 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27722 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27725 this.field = field.el.select('input', true).first();
27726 this.field.on("keydown", this.onPagingKeydown, this);
27727 this.field.on("focus", function(){this.dom.select();});
27730 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27731 //this.field.setHeight(18);
27732 //this.addSeparator();
27733 this.next = this.navgroup.addItem({
27734 tooltip: this.nextText,
27735 cls: "next btn-outline-secondary",
27736 html : ' <i class="fa fa-forward"></i>',
27738 preventDefault: true,
27739 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27741 this.last = this.navgroup.addItem({
27742 tooltip: this.lastText,
27743 html : ' <i class="fa fa-step-forward"></i>',
27744 cls: "next btn-outline-secondary",
27746 preventDefault: true,
27747 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27749 //this.addSeparator();
27750 this.loading = this.navgroup.addItem({
27751 tooltip: this.refreshText,
27752 cls: "btn-outline-secondary",
27753 html : ' <i class="fa fa-refresh"></i>',
27754 preventDefault: true,
27755 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27761 updateInfo : function(){
27762 if(this.displayEl){
27763 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27764 var msg = count == 0 ?
27768 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27770 this.displayEl.update(msg);
27775 onLoad : function(ds, r, o)
27777 this.cursor = o.params && o.params.start ? o.params.start : 0;
27779 var d = this.getPageData(),
27784 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27785 this.field.dom.value = ap;
27786 this.first.setDisabled(ap == 1);
27787 this.prev.setDisabled(ap == 1);
27788 this.next.setDisabled(ap == ps);
27789 this.last.setDisabled(ap == ps);
27790 this.loading.enable();
27795 getPageData : function(){
27796 var total = this.ds.getTotalCount();
27799 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27800 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27805 onLoadError : function(){
27806 this.loading.enable();
27810 onPagingKeydown : function(e){
27811 var k = e.getKey();
27812 var d = this.getPageData();
27814 var v = this.field.dom.value, pageNum;
27815 if(!v || isNaN(pageNum = parseInt(v, 10))){
27816 this.field.dom.value = d.activePage;
27819 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27820 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27823 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))
27825 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27826 this.field.dom.value = pageNum;
27827 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27830 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27832 var v = this.field.dom.value, pageNum;
27833 var increment = (e.shiftKey) ? 10 : 1;
27834 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27837 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27838 this.field.dom.value = d.activePage;
27841 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27843 this.field.dom.value = parseInt(v, 10) + increment;
27844 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27845 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27852 beforeLoad : function(){
27854 this.loading.disable();
27859 onClick : function(which){
27868 ds.load({params:{start: 0, limit: this.pageSize}});
27871 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27874 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27877 var total = ds.getTotalCount();
27878 var extra = total % this.pageSize;
27879 var lastStart = extra ? (total - extra) : total-this.pageSize;
27880 ds.load({params:{start: lastStart, limit: this.pageSize}});
27883 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27889 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27890 * @param {Roo.data.Store} store The data store to unbind
27892 unbind : function(ds){
27893 ds.un("beforeload", this.beforeLoad, this);
27894 ds.un("load", this.onLoad, this);
27895 ds.un("loadexception", this.onLoadError, this);
27896 ds.un("remove", this.updateInfo, this);
27897 ds.un("add", this.updateInfo, this);
27898 this.ds = undefined;
27902 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27903 * @param {Roo.data.Store} store The data store to bind
27905 bind : function(ds){
27906 ds.on("beforeload", this.beforeLoad, this);
27907 ds.on("load", this.onLoad, this);
27908 ds.on("loadexception", this.onLoadError, this);
27909 ds.on("remove", this.updateInfo, this);
27910 ds.on("add", this.updateInfo, this);
27921 * @class Roo.bootstrap.MessageBar
27922 * @extends Roo.bootstrap.Component
27923 * Bootstrap MessageBar class
27924 * @cfg {String} html contents of the MessageBar
27925 * @cfg {String} weight (info | success | warning | danger) default info
27926 * @cfg {String} beforeClass insert the bar before the given class
27927 * @cfg {Boolean} closable (true | false) default false
27928 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27931 * Create a new Element
27932 * @param {Object} config The config object
27935 Roo.bootstrap.MessageBar = function(config){
27936 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27939 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27945 beforeClass: 'bootstrap-sticky-wrap',
27947 getAutoCreate : function(){
27951 cls: 'alert alert-dismissable alert-' + this.weight,
27956 html: this.html || ''
27962 cfg.cls += ' alert-messages-fixed';
27976 onRender : function(ct, position)
27978 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27981 var cfg = Roo.apply({}, this.getAutoCreate());
27985 cfg.cls += ' ' + this.cls;
27988 cfg.style = this.style;
27990 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27992 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27995 this.el.select('>button.close').on('click', this.hide, this);
28001 if (!this.rendered) {
28007 this.fireEvent('show', this);
28013 if (!this.rendered) {
28019 this.fireEvent('hide', this);
28022 update : function()
28024 // var e = this.el.dom.firstChild;
28026 // if(this.closable){
28027 // e = e.nextSibling;
28030 // e.data = this.html || '';
28032 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28048 * @class Roo.bootstrap.Graph
28049 * @extends Roo.bootstrap.Component
28050 * Bootstrap Graph class
28054 @cfg {String} graphtype bar | vbar | pie
28055 @cfg {number} g_x coodinator | centre x (pie)
28056 @cfg {number} g_y coodinator | centre y (pie)
28057 @cfg {number} g_r radius (pie)
28058 @cfg {number} g_height height of the chart (respected by all elements in the set)
28059 @cfg {number} g_width width of the chart (respected by all elements in the set)
28060 @cfg {Object} title The title of the chart
28063 -opts (object) options for the chart
28065 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28066 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28068 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.
28069 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28071 o stretch (boolean)
28073 -opts (object) options for the pie
28076 o startAngle (number)
28077 o endAngle (number)
28081 * Create a new Input
28082 * @param {Object} config The config object
28085 Roo.bootstrap.Graph = function(config){
28086 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28092 * The img click event for the img.
28093 * @param {Roo.EventObject} e
28099 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28110 //g_colors: this.colors,
28117 getAutoCreate : function(){
28128 onRender : function(ct,position){
28131 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28133 if (typeof(Raphael) == 'undefined') {
28134 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28138 this.raphael = Raphael(this.el.dom);
28140 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28141 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28142 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28143 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28145 r.text(160, 10, "Single Series Chart").attr(txtattr);
28146 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28147 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28148 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28150 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28151 r.barchart(330, 10, 300, 220, data1);
28152 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28153 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28156 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28157 // r.barchart(30, 30, 560, 250, xdata, {
28158 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28159 // axis : "0 0 1 1",
28160 // axisxlabels : xdata
28161 // //yvalues : cols,
28164 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28166 // this.load(null,xdata,{
28167 // axis : "0 0 1 1",
28168 // axisxlabels : xdata
28173 load : function(graphtype,xdata,opts)
28175 this.raphael.clear();
28177 graphtype = this.graphtype;
28182 var r = this.raphael,
28183 fin = function () {
28184 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28186 fout = function () {
28187 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28189 pfin = function() {
28190 this.sector.stop();
28191 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28194 this.label[0].stop();
28195 this.label[0].attr({ r: 7.5 });
28196 this.label[1].attr({ "font-weight": 800 });
28199 pfout = function() {
28200 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28203 this.label[0].animate({ r: 5 }, 500, "bounce");
28204 this.label[1].attr({ "font-weight": 400 });
28210 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28213 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28216 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28217 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28219 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28226 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28231 setTitle: function(o)
28236 initEvents: function() {
28239 this.el.on('click', this.onClick, this);
28243 onClick : function(e)
28245 Roo.log('img onclick');
28246 this.fireEvent('click', this, e);
28258 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28261 * @class Roo.bootstrap.dash.NumberBox
28262 * @extends Roo.bootstrap.Component
28263 * Bootstrap NumberBox class
28264 * @cfg {String} headline Box headline
28265 * @cfg {String} content Box content
28266 * @cfg {String} icon Box icon
28267 * @cfg {String} footer Footer text
28268 * @cfg {String} fhref Footer href
28271 * Create a new NumberBox
28272 * @param {Object} config The config object
28276 Roo.bootstrap.dash.NumberBox = function(config){
28277 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28281 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28290 getAutoCreate : function(){
28294 cls : 'small-box ',
28302 cls : 'roo-headline',
28303 html : this.headline
28307 cls : 'roo-content',
28308 html : this.content
28322 cls : 'ion ' + this.icon
28331 cls : 'small-box-footer',
28332 href : this.fhref || '#',
28336 cfg.cn.push(footer);
28343 onRender : function(ct,position){
28344 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28351 setHeadline: function (value)
28353 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28356 setFooter: function (value, href)
28358 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28361 this.el.select('a.small-box-footer',true).first().attr('href', href);
28366 setContent: function (value)
28368 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28371 initEvents: function()
28385 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28388 * @class Roo.bootstrap.dash.TabBox
28389 * @extends Roo.bootstrap.Component
28390 * Bootstrap TabBox class
28391 * @cfg {String} title Title of the TabBox
28392 * @cfg {String} icon Icon of the TabBox
28393 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28394 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28397 * Create a new TabBox
28398 * @param {Object} config The config object
28402 Roo.bootstrap.dash.TabBox = function(config){
28403 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28408 * When a pane is added
28409 * @param {Roo.bootstrap.dash.TabPane} pane
28413 * @event activatepane
28414 * When a pane is activated
28415 * @param {Roo.bootstrap.dash.TabPane} pane
28417 "activatepane" : true
28425 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28430 tabScrollable : false,
28432 getChildContainer : function()
28434 return this.el.select('.tab-content', true).first();
28437 getAutoCreate : function(){
28441 cls: 'pull-left header',
28449 cls: 'fa ' + this.icon
28455 cls: 'nav nav-tabs pull-right',
28461 if(this.tabScrollable){
28468 cls: 'nav nav-tabs pull-right',
28479 cls: 'nav-tabs-custom',
28484 cls: 'tab-content no-padding',
28492 initEvents : function()
28494 //Roo.log('add add pane handler');
28495 this.on('addpane', this.onAddPane, this);
28498 * Updates the box title
28499 * @param {String} html to set the title to.
28501 setTitle : function(value)
28503 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28505 onAddPane : function(pane)
28507 this.panes.push(pane);
28508 //Roo.log('addpane');
28510 // tabs are rendere left to right..
28511 if(!this.showtabs){
28515 var ctr = this.el.select('.nav-tabs', true).first();
28518 var existing = ctr.select('.nav-tab',true);
28519 var qty = existing.getCount();;
28522 var tab = ctr.createChild({
28524 cls : 'nav-tab' + (qty ? '' : ' active'),
28532 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28535 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28537 pane.el.addClass('active');
28542 onTabClick : function(ev,un,ob,pane)
28544 //Roo.log('tab - prev default');
28545 ev.preventDefault();
28548 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28549 pane.tab.addClass('active');
28550 //Roo.log(pane.title);
28551 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28552 // technically we should have a deactivate event.. but maybe add later.
28553 // and it should not de-activate the selected tab...
28554 this.fireEvent('activatepane', pane);
28555 pane.el.addClass('active');
28556 pane.fireEvent('activate');
28561 getActivePane : function()
28564 Roo.each(this.panes, function(p) {
28565 if(p.el.hasClass('active')){
28586 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28588 * @class Roo.bootstrap.TabPane
28589 * @extends Roo.bootstrap.Component
28590 * Bootstrap TabPane class
28591 * @cfg {Boolean} active (false | true) Default false
28592 * @cfg {String} title title of panel
28596 * Create a new TabPane
28597 * @param {Object} config The config object
28600 Roo.bootstrap.dash.TabPane = function(config){
28601 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28607 * When a pane is activated
28608 * @param {Roo.bootstrap.dash.TabPane} pane
28615 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28620 // the tabBox that this is attached to.
28623 getAutoCreate : function()
28631 cfg.cls += ' active';
28636 initEvents : function()
28638 //Roo.log('trigger add pane handler');
28639 this.parent().fireEvent('addpane', this)
28643 * Updates the tab title
28644 * @param {String} html to set the title to.
28646 setTitle: function(str)
28652 this.tab.select('a', true).first().dom.innerHTML = str;
28669 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28672 * @class Roo.bootstrap.menu.Menu
28673 * @extends Roo.bootstrap.Component
28674 * Bootstrap Menu class - container for Menu
28675 * @cfg {String} html Text of the menu
28676 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28677 * @cfg {String} icon Font awesome icon
28678 * @cfg {String} pos Menu align to (top | bottom) default bottom
28682 * Create a new Menu
28683 * @param {Object} config The config object
28687 Roo.bootstrap.menu.Menu = function(config){
28688 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28692 * @event beforeshow
28693 * Fires before this menu is displayed
28694 * @param {Roo.bootstrap.menu.Menu} this
28698 * @event beforehide
28699 * Fires before this menu is hidden
28700 * @param {Roo.bootstrap.menu.Menu} this
28705 * Fires after this menu is displayed
28706 * @param {Roo.bootstrap.menu.Menu} this
28711 * Fires after this menu is hidden
28712 * @param {Roo.bootstrap.menu.Menu} this
28717 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28718 * @param {Roo.bootstrap.menu.Menu} this
28719 * @param {Roo.EventObject} e
28726 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28730 weight : 'default',
28735 getChildContainer : function() {
28736 if(this.isSubMenu){
28740 return this.el.select('ul.dropdown-menu', true).first();
28743 getAutoCreate : function()
28748 cls : 'roo-menu-text',
28756 cls : 'fa ' + this.icon
28767 cls : 'dropdown-button btn btn-' + this.weight,
28772 cls : 'dropdown-toggle btn btn-' + this.weight,
28782 cls : 'dropdown-menu'
28788 if(this.pos == 'top'){
28789 cfg.cls += ' dropup';
28792 if(this.isSubMenu){
28795 cls : 'dropdown-menu'
28802 onRender : function(ct, position)
28804 this.isSubMenu = ct.hasClass('dropdown-submenu');
28806 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28809 initEvents : function()
28811 if(this.isSubMenu){
28815 this.hidden = true;
28817 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28818 this.triggerEl.on('click', this.onTriggerPress, this);
28820 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28821 this.buttonEl.on('click', this.onClick, this);
28827 if(this.isSubMenu){
28831 return this.el.select('ul.dropdown-menu', true).first();
28834 onClick : function(e)
28836 this.fireEvent("click", this, e);
28839 onTriggerPress : function(e)
28841 if (this.isVisible()) {
28848 isVisible : function(){
28849 return !this.hidden;
28854 this.fireEvent("beforeshow", this);
28856 this.hidden = false;
28857 this.el.addClass('open');
28859 Roo.get(document).on("mouseup", this.onMouseUp, this);
28861 this.fireEvent("show", this);
28868 this.fireEvent("beforehide", this);
28870 this.hidden = true;
28871 this.el.removeClass('open');
28873 Roo.get(document).un("mouseup", this.onMouseUp);
28875 this.fireEvent("hide", this);
28878 onMouseUp : function()
28892 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28895 * @class Roo.bootstrap.menu.Item
28896 * @extends Roo.bootstrap.Component
28897 * Bootstrap MenuItem class
28898 * @cfg {Boolean} submenu (true | false) default false
28899 * @cfg {String} html text of the item
28900 * @cfg {String} href the link
28901 * @cfg {Boolean} disable (true | false) default false
28902 * @cfg {Boolean} preventDefault (true | false) default true
28903 * @cfg {String} icon Font awesome icon
28904 * @cfg {String} pos Submenu align to (left | right) default right
28908 * Create a new Item
28909 * @param {Object} config The config object
28913 Roo.bootstrap.menu.Item = function(config){
28914 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28918 * Fires when the mouse is hovering over this menu
28919 * @param {Roo.bootstrap.menu.Item} this
28920 * @param {Roo.EventObject} e
28925 * Fires when the mouse exits this menu
28926 * @param {Roo.bootstrap.menu.Item} this
28927 * @param {Roo.EventObject} e
28933 * The raw click event for the entire grid.
28934 * @param {Roo.EventObject} e
28940 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28945 preventDefault: true,
28950 getAutoCreate : function()
28955 cls : 'roo-menu-item-text',
28963 cls : 'fa ' + this.icon
28972 href : this.href || '#',
28979 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28983 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28985 if(this.pos == 'left'){
28986 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28993 initEvents : function()
28995 this.el.on('mouseover', this.onMouseOver, this);
28996 this.el.on('mouseout', this.onMouseOut, this);
28998 this.el.select('a', true).first().on('click', this.onClick, this);
29002 onClick : function(e)
29004 if(this.preventDefault){
29005 e.preventDefault();
29008 this.fireEvent("click", this, e);
29011 onMouseOver : function(e)
29013 if(this.submenu && this.pos == 'left'){
29014 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29017 this.fireEvent("mouseover", this, e);
29020 onMouseOut : function(e)
29022 this.fireEvent("mouseout", this, e);
29034 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29037 * @class Roo.bootstrap.menu.Separator
29038 * @extends Roo.bootstrap.Component
29039 * Bootstrap Separator class
29042 * Create a new Separator
29043 * @param {Object} config The config object
29047 Roo.bootstrap.menu.Separator = function(config){
29048 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29051 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29053 getAutoCreate : function(){
29056 cls: 'dropdown-divider divider'
29074 * @class Roo.bootstrap.Tooltip
29075 * Bootstrap Tooltip class
29076 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29077 * to determine which dom element triggers the tooltip.
29079 * It needs to add support for additional attributes like tooltip-position
29082 * Create a new Toolti
29083 * @param {Object} config The config object
29086 Roo.bootstrap.Tooltip = function(config){
29087 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29089 this.alignment = Roo.bootstrap.Tooltip.alignment;
29091 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29092 this.alignment = config.alignment;
29097 Roo.apply(Roo.bootstrap.Tooltip, {
29099 * @function init initialize tooltip monitoring.
29103 currentTip : false,
29104 currentRegion : false,
29110 Roo.get(document).on('mouseover', this.enter ,this);
29111 Roo.get(document).on('mouseout', this.leave, this);
29114 this.currentTip = new Roo.bootstrap.Tooltip();
29117 enter : function(ev)
29119 var dom = ev.getTarget();
29121 //Roo.log(['enter',dom]);
29122 var el = Roo.fly(dom);
29123 if (this.currentEl) {
29125 //Roo.log(this.currentEl);
29126 //Roo.log(this.currentEl.contains(dom));
29127 if (this.currentEl == el) {
29130 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29136 if (this.currentTip.el) {
29137 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29141 if(!el || el.dom == document){
29147 if (!el.attr('tooltip')) {
29148 pel = el.findParent("[tooltip]");
29150 bindEl = Roo.get(pel);
29156 // you can not look for children, as if el is the body.. then everythign is the child..
29157 if (!pel && !el.attr('tooltip')) { //
29158 if (!el.select("[tooltip]").elements.length) {
29161 // is the mouse over this child...?
29162 bindEl = el.select("[tooltip]").first();
29163 var xy = ev.getXY();
29164 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29165 //Roo.log("not in region.");
29168 //Roo.log("child element over..");
29171 this.currentEl = el;
29172 this.currentTip.bind(bindEl);
29173 this.currentRegion = Roo.lib.Region.getRegion(dom);
29174 this.currentTip.enter();
29177 leave : function(ev)
29179 var dom = ev.getTarget();
29180 //Roo.log(['leave',dom]);
29181 if (!this.currentEl) {
29186 if (dom != this.currentEl.dom) {
29189 var xy = ev.getXY();
29190 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29193 // only activate leave if mouse cursor is outside... bounding box..
29198 if (this.currentTip) {
29199 this.currentTip.leave();
29201 //Roo.log('clear currentEl');
29202 this.currentEl = false;
29207 'left' : ['r-l', [-2,0], 'right'],
29208 'right' : ['l-r', [2,0], 'left'],
29209 'bottom' : ['t-b', [0,2], 'top'],
29210 'top' : [ 'b-t', [0,-2], 'bottom']
29216 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29221 delay : null, // can be { show : 300 , hide: 500}
29225 hoverState : null, //???
29227 placement : 'bottom',
29231 getAutoCreate : function(){
29238 cls : 'tooltip-arrow arrow'
29241 cls : 'tooltip-inner'
29248 bind : function(el)
29253 initEvents : function()
29255 this.arrowEl = this.el.select('.arrow', true).first();
29256 this.innerEl = this.el.select('.tooltip-inner', true).first();
29259 enter : function () {
29261 if (this.timeout != null) {
29262 clearTimeout(this.timeout);
29265 this.hoverState = 'in';
29266 //Roo.log("enter - show");
29267 if (!this.delay || !this.delay.show) {
29272 this.timeout = setTimeout(function () {
29273 if (_t.hoverState == 'in') {
29276 }, this.delay.show);
29280 clearTimeout(this.timeout);
29282 this.hoverState = 'out';
29283 if (!this.delay || !this.delay.hide) {
29289 this.timeout = setTimeout(function () {
29290 //Roo.log("leave - timeout");
29292 if (_t.hoverState == 'out') {
29294 Roo.bootstrap.Tooltip.currentEl = false;
29299 show : function (msg)
29302 this.render(document.body);
29305 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29307 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29309 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29311 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29312 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29314 var placement = typeof this.placement == 'function' ?
29315 this.placement.call(this, this.el, on_el) :
29318 var autoToken = /\s?auto?\s?/i;
29319 var autoPlace = autoToken.test(placement);
29321 placement = placement.replace(autoToken, '') || 'top';
29325 //this.el.setXY([0,0]);
29327 //this.el.dom.style.display='block';
29329 //this.el.appendTo(on_el);
29331 var p = this.getPosition();
29332 var box = this.el.getBox();
29338 var align = this.alignment[placement];
29340 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29342 if(placement == 'top' || placement == 'bottom'){
29344 placement = 'right';
29347 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29348 placement = 'left';
29351 var scroll = Roo.select('body', true).first().getScroll();
29353 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29357 align = this.alignment[placement];
29359 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29363 var elems = document.getElementsByTagName('div');
29364 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29365 for (var i = 0; i < elems.length; i++) {
29366 var zindex = Number.parseInt(
29367 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29370 if (zindex > highest) {
29377 this.el.dom.style.zIndex = highest;
29379 this.el.alignTo(this.bindEl, align[0],align[1]);
29380 //var arrow = this.el.select('.arrow',true).first();
29381 //arrow.set(align[2],
29383 this.el.addClass(placement);
29384 this.el.addClass("bs-tooltip-"+ placement);
29386 this.el.addClass('in fade show');
29388 this.hoverState = null;
29390 if (this.el.hasClass('fade')) {
29405 //this.el.setXY([0,0]);
29406 this.el.removeClass(['show', 'in']);
29422 * @class Roo.bootstrap.LocationPicker
29423 * @extends Roo.bootstrap.Component
29424 * Bootstrap LocationPicker class
29425 * @cfg {Number} latitude Position when init default 0
29426 * @cfg {Number} longitude Position when init default 0
29427 * @cfg {Number} zoom default 15
29428 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29429 * @cfg {Boolean} mapTypeControl default false
29430 * @cfg {Boolean} disableDoubleClickZoom default false
29431 * @cfg {Boolean} scrollwheel default true
29432 * @cfg {Boolean} streetViewControl default false
29433 * @cfg {Number} radius default 0
29434 * @cfg {String} locationName
29435 * @cfg {Boolean} draggable default true
29436 * @cfg {Boolean} enableAutocomplete default false
29437 * @cfg {Boolean} enableReverseGeocode default true
29438 * @cfg {String} markerTitle
29441 * Create a new LocationPicker
29442 * @param {Object} config The config object
29446 Roo.bootstrap.LocationPicker = function(config){
29448 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29453 * Fires when the picker initialized.
29454 * @param {Roo.bootstrap.LocationPicker} this
29455 * @param {Google Location} location
29459 * @event positionchanged
29460 * Fires when the picker position changed.
29461 * @param {Roo.bootstrap.LocationPicker} this
29462 * @param {Google Location} location
29464 positionchanged : true,
29467 * Fires when the map resize.
29468 * @param {Roo.bootstrap.LocationPicker} this
29473 * Fires when the map show.
29474 * @param {Roo.bootstrap.LocationPicker} this
29479 * Fires when the map hide.
29480 * @param {Roo.bootstrap.LocationPicker} this
29485 * Fires when click the map.
29486 * @param {Roo.bootstrap.LocationPicker} this
29487 * @param {Map event} e
29491 * @event mapRightClick
29492 * Fires when right click the map.
29493 * @param {Roo.bootstrap.LocationPicker} this
29494 * @param {Map event} e
29496 mapRightClick : true,
29498 * @event markerClick
29499 * Fires when click the marker.
29500 * @param {Roo.bootstrap.LocationPicker} this
29501 * @param {Map event} e
29503 markerClick : true,
29505 * @event markerRightClick
29506 * Fires when right click the marker.
29507 * @param {Roo.bootstrap.LocationPicker} this
29508 * @param {Map event} e
29510 markerRightClick : true,
29512 * @event OverlayViewDraw
29513 * Fires when OverlayView Draw
29514 * @param {Roo.bootstrap.LocationPicker} this
29516 OverlayViewDraw : true,
29518 * @event OverlayViewOnAdd
29519 * Fires when OverlayView Draw
29520 * @param {Roo.bootstrap.LocationPicker} this
29522 OverlayViewOnAdd : true,
29524 * @event OverlayViewOnRemove
29525 * Fires when OverlayView Draw
29526 * @param {Roo.bootstrap.LocationPicker} this
29528 OverlayViewOnRemove : true,
29530 * @event OverlayViewShow
29531 * Fires when OverlayView Draw
29532 * @param {Roo.bootstrap.LocationPicker} this
29533 * @param {Pixel} cpx
29535 OverlayViewShow : true,
29537 * @event OverlayViewHide
29538 * Fires when OverlayView Draw
29539 * @param {Roo.bootstrap.LocationPicker} this
29541 OverlayViewHide : true,
29543 * @event loadexception
29544 * Fires when load google lib failed.
29545 * @param {Roo.bootstrap.LocationPicker} this
29547 loadexception : true
29552 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29554 gMapContext: false,
29560 mapTypeControl: false,
29561 disableDoubleClickZoom: false,
29563 streetViewControl: false,
29567 enableAutocomplete: false,
29568 enableReverseGeocode: true,
29571 getAutoCreate: function()
29576 cls: 'roo-location-picker'
29582 initEvents: function(ct, position)
29584 if(!this.el.getWidth() || this.isApplied()){
29588 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29593 initial: function()
29595 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29596 this.fireEvent('loadexception', this);
29600 if(!this.mapTypeId){
29601 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29604 this.gMapContext = this.GMapContext();
29606 this.initOverlayView();
29608 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29612 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29613 _this.setPosition(_this.gMapContext.marker.position);
29616 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29617 _this.fireEvent('mapClick', this, event);
29621 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29622 _this.fireEvent('mapRightClick', this, event);
29626 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29627 _this.fireEvent('markerClick', this, event);
29631 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29632 _this.fireEvent('markerRightClick', this, event);
29636 this.setPosition(this.gMapContext.location);
29638 this.fireEvent('initial', this, this.gMapContext.location);
29641 initOverlayView: function()
29645 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29649 _this.fireEvent('OverlayViewDraw', _this);
29654 _this.fireEvent('OverlayViewOnAdd', _this);
29657 onRemove: function()
29659 _this.fireEvent('OverlayViewOnRemove', _this);
29662 show: function(cpx)
29664 _this.fireEvent('OverlayViewShow', _this, cpx);
29669 _this.fireEvent('OverlayViewHide', _this);
29675 fromLatLngToContainerPixel: function(event)
29677 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29680 isApplied: function()
29682 return this.getGmapContext() == false ? false : true;
29685 getGmapContext: function()
29687 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29690 GMapContext: function()
29692 var position = new google.maps.LatLng(this.latitude, this.longitude);
29694 var _map = new google.maps.Map(this.el.dom, {
29697 mapTypeId: this.mapTypeId,
29698 mapTypeControl: this.mapTypeControl,
29699 disableDoubleClickZoom: this.disableDoubleClickZoom,
29700 scrollwheel: this.scrollwheel,
29701 streetViewControl: this.streetViewControl,
29702 locationName: this.locationName,
29703 draggable: this.draggable,
29704 enableAutocomplete: this.enableAutocomplete,
29705 enableReverseGeocode: this.enableReverseGeocode
29708 var _marker = new google.maps.Marker({
29709 position: position,
29711 title: this.markerTitle,
29712 draggable: this.draggable
29719 location: position,
29720 radius: this.radius,
29721 locationName: this.locationName,
29722 addressComponents: {
29723 formatted_address: null,
29724 addressLine1: null,
29725 addressLine2: null,
29727 streetNumber: null,
29731 stateOrProvince: null
29734 domContainer: this.el.dom,
29735 geodecoder: new google.maps.Geocoder()
29739 drawCircle: function(center, radius, options)
29741 if (this.gMapContext.circle != null) {
29742 this.gMapContext.circle.setMap(null);
29746 options = Roo.apply({}, options, {
29747 strokeColor: "#0000FF",
29748 strokeOpacity: .35,
29750 fillColor: "#0000FF",
29754 options.map = this.gMapContext.map;
29755 options.radius = radius;
29756 options.center = center;
29757 this.gMapContext.circle = new google.maps.Circle(options);
29758 return this.gMapContext.circle;
29764 setPosition: function(location)
29766 this.gMapContext.location = location;
29767 this.gMapContext.marker.setPosition(location);
29768 this.gMapContext.map.panTo(location);
29769 this.drawCircle(location, this.gMapContext.radius, {});
29773 if (this.gMapContext.settings.enableReverseGeocode) {
29774 this.gMapContext.geodecoder.geocode({
29775 latLng: this.gMapContext.location
29776 }, function(results, status) {
29778 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29779 _this.gMapContext.locationName = results[0].formatted_address;
29780 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29782 _this.fireEvent('positionchanged', this, location);
29789 this.fireEvent('positionchanged', this, location);
29794 google.maps.event.trigger(this.gMapContext.map, "resize");
29796 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29798 this.fireEvent('resize', this);
29801 setPositionByLatLng: function(latitude, longitude)
29803 this.setPosition(new google.maps.LatLng(latitude, longitude));
29806 getCurrentPosition: function()
29809 latitude: this.gMapContext.location.lat(),
29810 longitude: this.gMapContext.location.lng()
29814 getAddressName: function()
29816 return this.gMapContext.locationName;
29819 getAddressComponents: function()
29821 return this.gMapContext.addressComponents;
29824 address_component_from_google_geocode: function(address_components)
29828 for (var i = 0; i < address_components.length; i++) {
29829 var component = address_components[i];
29830 if (component.types.indexOf("postal_code") >= 0) {
29831 result.postalCode = component.short_name;
29832 } else if (component.types.indexOf("street_number") >= 0) {
29833 result.streetNumber = component.short_name;
29834 } else if (component.types.indexOf("route") >= 0) {
29835 result.streetName = component.short_name;
29836 } else if (component.types.indexOf("neighborhood") >= 0) {
29837 result.city = component.short_name;
29838 } else if (component.types.indexOf("locality") >= 0) {
29839 result.city = component.short_name;
29840 } else if (component.types.indexOf("sublocality") >= 0) {
29841 result.district = component.short_name;
29842 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29843 result.stateOrProvince = component.short_name;
29844 } else if (component.types.indexOf("country") >= 0) {
29845 result.country = component.short_name;
29849 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29850 result.addressLine2 = "";
29854 setZoomLevel: function(zoom)
29856 this.gMapContext.map.setZoom(zoom);
29869 this.fireEvent('show', this);
29880 this.fireEvent('hide', this);
29885 Roo.apply(Roo.bootstrap.LocationPicker, {
29887 OverlayView : function(map, options)
29889 options = options || {};
29896 * @class Roo.bootstrap.Alert
29897 * @extends Roo.bootstrap.Component
29898 * Bootstrap Alert class - shows an alert area box
29900 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29901 Enter a valid email address
29904 * @cfg {String} title The title of alert
29905 * @cfg {String} html The content of alert
29906 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29907 * @cfg {String} fa font-awesomeicon
29908 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29909 * @cfg {Boolean} close true to show a x closer
29913 * Create a new alert
29914 * @param {Object} config The config object
29918 Roo.bootstrap.Alert = function(config){
29919 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29923 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29929 faicon: false, // BC
29933 getAutoCreate : function()
29945 style : this.close ? '' : 'display:none'
29949 cls : 'roo-alert-icon'
29954 cls : 'roo-alert-title',
29959 cls : 'roo-alert-text',
29966 cfg.cn[0].cls += ' fa ' + this.faicon;
29969 cfg.cn[0].cls += ' fa ' + this.fa;
29973 cfg.cls += ' alert-' + this.weight;
29979 initEvents: function()
29981 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29982 this.titleEl = this.el.select('.roo-alert-title',true).first();
29983 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29984 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29985 if (this.seconds > 0) {
29986 this.hide.defer(this.seconds, this);
29990 * Set the Title Message HTML
29991 * @param {String} html
29993 setTitle : function(str)
29995 this.titleEl.dom.innerHTML = str;
29999 * Set the Body Message HTML
30000 * @param {String} html
30002 setHtml : function(str)
30004 this.htmlEl.dom.innerHTML = str;
30007 * Set the Weight of the alert
30008 * @param {String} (success|info|warning|danger) weight
30011 setWeight : function(weight)
30014 this.el.removeClass('alert-' + this.weight);
30017 this.weight = weight;
30019 this.el.addClass('alert-' + this.weight);
30022 * Set the Icon of the alert
30023 * @param {String} see fontawsome names (name without the 'fa-' bit)
30025 setIcon : function(icon)
30028 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30031 this.faicon = icon;
30033 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30058 * @class Roo.bootstrap.UploadCropbox
30059 * @extends Roo.bootstrap.Component
30060 * Bootstrap UploadCropbox class
30061 * @cfg {String} emptyText show when image has been loaded
30062 * @cfg {String} rotateNotify show when image too small to rotate
30063 * @cfg {Number} errorTimeout default 3000
30064 * @cfg {Number} minWidth default 300
30065 * @cfg {Number} minHeight default 300
30066 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30067 * @cfg {Boolean} isDocument (true|false) default false
30068 * @cfg {String} url action url
30069 * @cfg {String} paramName default 'imageUpload'
30070 * @cfg {String} method default POST
30071 * @cfg {Boolean} loadMask (true|false) default true
30072 * @cfg {Boolean} loadingText default 'Loading...'
30075 * Create a new UploadCropbox
30076 * @param {Object} config The config object
30079 Roo.bootstrap.UploadCropbox = function(config){
30080 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30084 * @event beforeselectfile
30085 * Fire before select file
30086 * @param {Roo.bootstrap.UploadCropbox} this
30088 "beforeselectfile" : true,
30091 * Fire after initEvent
30092 * @param {Roo.bootstrap.UploadCropbox} this
30097 * Fire after initEvent
30098 * @param {Roo.bootstrap.UploadCropbox} this
30099 * @param {String} data
30104 * Fire when preparing the file data
30105 * @param {Roo.bootstrap.UploadCropbox} this
30106 * @param {Object} file
30111 * Fire when get exception
30112 * @param {Roo.bootstrap.UploadCropbox} this
30113 * @param {XMLHttpRequest} xhr
30115 "exception" : true,
30117 * @event beforeloadcanvas
30118 * Fire before load the canvas
30119 * @param {Roo.bootstrap.UploadCropbox} this
30120 * @param {String} src
30122 "beforeloadcanvas" : true,
30125 * Fire when trash image
30126 * @param {Roo.bootstrap.UploadCropbox} this
30131 * Fire when download the image
30132 * @param {Roo.bootstrap.UploadCropbox} this
30136 * @event footerbuttonclick
30137 * Fire when footerbuttonclick
30138 * @param {Roo.bootstrap.UploadCropbox} this
30139 * @param {String} type
30141 "footerbuttonclick" : true,
30145 * @param {Roo.bootstrap.UploadCropbox} this
30150 * Fire when rotate the image
30151 * @param {Roo.bootstrap.UploadCropbox} this
30152 * @param {String} pos
30157 * Fire when inspect the file
30158 * @param {Roo.bootstrap.UploadCropbox} this
30159 * @param {Object} file
30164 * Fire when xhr upload the file
30165 * @param {Roo.bootstrap.UploadCropbox} this
30166 * @param {Object} data
30171 * Fire when arrange the file data
30172 * @param {Roo.bootstrap.UploadCropbox} this
30173 * @param {Object} formData
30178 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30181 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30183 emptyText : 'Click to upload image',
30184 rotateNotify : 'Image is too small to rotate',
30185 errorTimeout : 3000,
30199 cropType : 'image/jpeg',
30201 canvasLoaded : false,
30202 isDocument : false,
30204 paramName : 'imageUpload',
30206 loadingText : 'Loading...',
30209 getAutoCreate : function()
30213 cls : 'roo-upload-cropbox',
30217 cls : 'roo-upload-cropbox-selector',
30222 cls : 'roo-upload-cropbox-body',
30223 style : 'cursor:pointer',
30227 cls : 'roo-upload-cropbox-preview'
30231 cls : 'roo-upload-cropbox-thumb'
30235 cls : 'roo-upload-cropbox-empty-notify',
30236 html : this.emptyText
30240 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30241 html : this.rotateNotify
30247 cls : 'roo-upload-cropbox-footer',
30250 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30260 onRender : function(ct, position)
30262 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30264 if (this.buttons.length) {
30266 Roo.each(this.buttons, function(bb) {
30268 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30270 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30276 this.maskEl = this.el;
30280 initEvents : function()
30282 this.urlAPI = (window.createObjectURL && window) ||
30283 (window.URL && URL.revokeObjectURL && URL) ||
30284 (window.webkitURL && webkitURL);
30286 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30287 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30289 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30290 this.selectorEl.hide();
30292 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30293 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30295 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30296 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30297 this.thumbEl.hide();
30299 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30300 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30302 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30303 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30304 this.errorEl.hide();
30306 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30307 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30308 this.footerEl.hide();
30310 this.setThumbBoxSize();
30316 this.fireEvent('initial', this);
30323 window.addEventListener("resize", function() { _this.resize(); } );
30325 this.bodyEl.on('click', this.beforeSelectFile, this);
30328 this.bodyEl.on('touchstart', this.onTouchStart, this);
30329 this.bodyEl.on('touchmove', this.onTouchMove, this);
30330 this.bodyEl.on('touchend', this.onTouchEnd, this);
30334 this.bodyEl.on('mousedown', this.onMouseDown, this);
30335 this.bodyEl.on('mousemove', this.onMouseMove, this);
30336 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30337 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30338 Roo.get(document).on('mouseup', this.onMouseUp, this);
30341 this.selectorEl.on('change', this.onFileSelected, this);
30347 this.baseScale = 1;
30349 this.baseRotate = 1;
30350 this.dragable = false;
30351 this.pinching = false;
30354 this.cropData = false;
30355 this.notifyEl.dom.innerHTML = this.emptyText;
30357 this.selectorEl.dom.value = '';
30361 resize : function()
30363 if(this.fireEvent('resize', this) != false){
30364 this.setThumbBoxPosition();
30365 this.setCanvasPosition();
30369 onFooterButtonClick : function(e, el, o, type)
30372 case 'rotate-left' :
30373 this.onRotateLeft(e);
30375 case 'rotate-right' :
30376 this.onRotateRight(e);
30379 this.beforeSelectFile(e);
30394 this.fireEvent('footerbuttonclick', this, type);
30397 beforeSelectFile : function(e)
30399 e.preventDefault();
30401 if(this.fireEvent('beforeselectfile', this) != false){
30402 this.selectorEl.dom.click();
30406 onFileSelected : function(e)
30408 e.preventDefault();
30410 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30414 var file = this.selectorEl.dom.files[0];
30416 if(this.fireEvent('inspect', this, file) != false){
30417 this.prepare(file);
30422 trash : function(e)
30424 this.fireEvent('trash', this);
30427 download : function(e)
30429 this.fireEvent('download', this);
30432 loadCanvas : function(src)
30434 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30438 this.imageEl = document.createElement('img');
30442 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30444 this.imageEl.src = src;
30448 onLoadCanvas : function()
30450 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30451 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30453 this.bodyEl.un('click', this.beforeSelectFile, this);
30455 this.notifyEl.hide();
30456 this.thumbEl.show();
30457 this.footerEl.show();
30459 this.baseRotateLevel();
30461 if(this.isDocument){
30462 this.setThumbBoxSize();
30465 this.setThumbBoxPosition();
30467 this.baseScaleLevel();
30473 this.canvasLoaded = true;
30476 this.maskEl.unmask();
30481 setCanvasPosition : function()
30483 if(!this.canvasEl){
30487 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30488 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30490 this.previewEl.setLeft(pw);
30491 this.previewEl.setTop(ph);
30495 onMouseDown : function(e)
30499 this.dragable = true;
30500 this.pinching = false;
30502 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30503 this.dragable = false;
30507 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30508 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30512 onMouseMove : function(e)
30516 if(!this.canvasLoaded){
30520 if (!this.dragable){
30524 var minX = Math.ceil(this.thumbEl.getLeft(true));
30525 var minY = Math.ceil(this.thumbEl.getTop(true));
30527 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30528 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30530 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30531 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30533 x = x - this.mouseX;
30534 y = y - this.mouseY;
30536 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30537 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30539 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30540 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30542 this.previewEl.setLeft(bgX);
30543 this.previewEl.setTop(bgY);
30545 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30546 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30549 onMouseUp : function(e)
30553 this.dragable = false;
30556 onMouseWheel : function(e)
30560 this.startScale = this.scale;
30562 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30564 if(!this.zoomable()){
30565 this.scale = this.startScale;
30574 zoomable : function()
30576 var minScale = this.thumbEl.getWidth() / this.minWidth;
30578 if(this.minWidth < this.minHeight){
30579 minScale = this.thumbEl.getHeight() / this.minHeight;
30582 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30583 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30587 (this.rotate == 0 || this.rotate == 180) &&
30589 width > this.imageEl.OriginWidth ||
30590 height > this.imageEl.OriginHeight ||
30591 (width < this.minWidth && height < this.minHeight)
30599 (this.rotate == 90 || this.rotate == 270) &&
30601 width > this.imageEl.OriginWidth ||
30602 height > this.imageEl.OriginHeight ||
30603 (width < this.minHeight && height < this.minWidth)
30610 !this.isDocument &&
30611 (this.rotate == 0 || this.rotate == 180) &&
30613 width < this.minWidth ||
30614 width > this.imageEl.OriginWidth ||
30615 height < this.minHeight ||
30616 height > this.imageEl.OriginHeight
30623 !this.isDocument &&
30624 (this.rotate == 90 || this.rotate == 270) &&
30626 width < this.minHeight ||
30627 width > this.imageEl.OriginWidth ||
30628 height < this.minWidth ||
30629 height > this.imageEl.OriginHeight
30639 onRotateLeft : function(e)
30641 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30643 var minScale = this.thumbEl.getWidth() / this.minWidth;
30645 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30646 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30648 this.startScale = this.scale;
30650 while (this.getScaleLevel() < minScale){
30652 this.scale = this.scale + 1;
30654 if(!this.zoomable()){
30659 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30660 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30665 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30672 this.scale = this.startScale;
30674 this.onRotateFail();
30679 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30681 if(this.isDocument){
30682 this.setThumbBoxSize();
30683 this.setThumbBoxPosition();
30684 this.setCanvasPosition();
30689 this.fireEvent('rotate', this, 'left');
30693 onRotateRight : function(e)
30695 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30697 var minScale = this.thumbEl.getWidth() / this.minWidth;
30699 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30700 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30702 this.startScale = this.scale;
30704 while (this.getScaleLevel() < minScale){
30706 this.scale = this.scale + 1;
30708 if(!this.zoomable()){
30713 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30714 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30719 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30726 this.scale = this.startScale;
30728 this.onRotateFail();
30733 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30735 if(this.isDocument){
30736 this.setThumbBoxSize();
30737 this.setThumbBoxPosition();
30738 this.setCanvasPosition();
30743 this.fireEvent('rotate', this, 'right');
30746 onRotateFail : function()
30748 this.errorEl.show(true);
30752 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30757 this.previewEl.dom.innerHTML = '';
30759 var canvasEl = document.createElement("canvas");
30761 var contextEl = canvasEl.getContext("2d");
30763 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30764 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30765 var center = this.imageEl.OriginWidth / 2;
30767 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30768 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30769 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30770 center = this.imageEl.OriginHeight / 2;
30773 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30775 contextEl.translate(center, center);
30776 contextEl.rotate(this.rotate * Math.PI / 180);
30778 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30780 this.canvasEl = document.createElement("canvas");
30782 this.contextEl = this.canvasEl.getContext("2d");
30784 switch (this.rotate) {
30787 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30788 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30790 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30795 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30796 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30798 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30799 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);
30803 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30808 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30809 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30811 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30812 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);
30816 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);
30821 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30822 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30824 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30825 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30829 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30836 this.previewEl.appendChild(this.canvasEl);
30838 this.setCanvasPosition();
30843 if(!this.canvasLoaded){
30847 var imageCanvas = document.createElement("canvas");
30849 var imageContext = imageCanvas.getContext("2d");
30851 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30852 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30854 var center = imageCanvas.width / 2;
30856 imageContext.translate(center, center);
30858 imageContext.rotate(this.rotate * Math.PI / 180);
30860 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30862 var canvas = document.createElement("canvas");
30864 var context = canvas.getContext("2d");
30866 canvas.width = this.minWidth;
30867 canvas.height = this.minHeight;
30869 switch (this.rotate) {
30872 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30873 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30875 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30876 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30878 var targetWidth = this.minWidth - 2 * x;
30879 var targetHeight = this.minHeight - 2 * y;
30883 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30884 scale = targetWidth / width;
30887 if(x > 0 && y == 0){
30888 scale = targetHeight / height;
30891 if(x > 0 && y > 0){
30892 scale = targetWidth / width;
30894 if(width < height){
30895 scale = targetHeight / height;
30899 context.scale(scale, scale);
30901 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30902 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30904 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30905 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30907 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30912 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30913 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30915 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30916 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30918 var targetWidth = this.minWidth - 2 * x;
30919 var targetHeight = this.minHeight - 2 * y;
30923 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30924 scale = targetWidth / width;
30927 if(x > 0 && y == 0){
30928 scale = targetHeight / height;
30931 if(x > 0 && y > 0){
30932 scale = targetWidth / width;
30934 if(width < height){
30935 scale = targetHeight / height;
30939 context.scale(scale, scale);
30941 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30942 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30944 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30945 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30947 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30949 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30954 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30955 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30957 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30958 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30960 var targetWidth = this.minWidth - 2 * x;
30961 var targetHeight = this.minHeight - 2 * y;
30965 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30966 scale = targetWidth / width;
30969 if(x > 0 && y == 0){
30970 scale = targetHeight / height;
30973 if(x > 0 && y > 0){
30974 scale = targetWidth / width;
30976 if(width < height){
30977 scale = targetHeight / height;
30981 context.scale(scale, scale);
30983 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30984 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30986 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30987 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30989 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30990 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30992 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30997 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30998 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31000 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31001 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31003 var targetWidth = this.minWidth - 2 * x;
31004 var targetHeight = this.minHeight - 2 * y;
31008 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31009 scale = targetWidth / width;
31012 if(x > 0 && y == 0){
31013 scale = targetHeight / height;
31016 if(x > 0 && y > 0){
31017 scale = targetWidth / width;
31019 if(width < height){
31020 scale = targetHeight / height;
31024 context.scale(scale, scale);
31026 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31027 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31029 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31030 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31032 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31034 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31041 this.cropData = canvas.toDataURL(this.cropType);
31043 if(this.fireEvent('crop', this, this.cropData) !== false){
31044 this.process(this.file, this.cropData);
31051 setThumbBoxSize : function()
31055 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31056 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31057 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31059 this.minWidth = width;
31060 this.minHeight = height;
31062 if(this.rotate == 90 || this.rotate == 270){
31063 this.minWidth = height;
31064 this.minHeight = width;
31069 width = Math.ceil(this.minWidth * height / this.minHeight);
31071 if(this.minWidth > this.minHeight){
31073 height = Math.ceil(this.minHeight * width / this.minWidth);
31076 this.thumbEl.setStyle({
31077 width : width + 'px',
31078 height : height + 'px'
31085 setThumbBoxPosition : function()
31087 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31088 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31090 this.thumbEl.setLeft(x);
31091 this.thumbEl.setTop(y);
31095 baseRotateLevel : function()
31097 this.baseRotate = 1;
31100 typeof(this.exif) != 'undefined' &&
31101 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31102 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31104 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31107 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31111 baseScaleLevel : function()
31115 if(this.isDocument){
31117 if(this.baseRotate == 6 || this.baseRotate == 8){
31119 height = this.thumbEl.getHeight();
31120 this.baseScale = height / this.imageEl.OriginWidth;
31122 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31123 width = this.thumbEl.getWidth();
31124 this.baseScale = width / this.imageEl.OriginHeight;
31130 height = this.thumbEl.getHeight();
31131 this.baseScale = height / this.imageEl.OriginHeight;
31133 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31134 width = this.thumbEl.getWidth();
31135 this.baseScale = width / this.imageEl.OriginWidth;
31141 if(this.baseRotate == 6 || this.baseRotate == 8){
31143 width = this.thumbEl.getHeight();
31144 this.baseScale = width / this.imageEl.OriginHeight;
31146 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31147 height = this.thumbEl.getWidth();
31148 this.baseScale = height / this.imageEl.OriginHeight;
31151 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31152 height = this.thumbEl.getWidth();
31153 this.baseScale = height / this.imageEl.OriginHeight;
31155 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31156 width = this.thumbEl.getHeight();
31157 this.baseScale = width / this.imageEl.OriginWidth;
31164 width = this.thumbEl.getWidth();
31165 this.baseScale = width / this.imageEl.OriginWidth;
31167 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31168 height = this.thumbEl.getHeight();
31169 this.baseScale = height / this.imageEl.OriginHeight;
31172 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31174 height = this.thumbEl.getHeight();
31175 this.baseScale = height / this.imageEl.OriginHeight;
31177 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31178 width = this.thumbEl.getWidth();
31179 this.baseScale = width / this.imageEl.OriginWidth;
31187 getScaleLevel : function()
31189 return this.baseScale * Math.pow(1.1, this.scale);
31192 onTouchStart : function(e)
31194 if(!this.canvasLoaded){
31195 this.beforeSelectFile(e);
31199 var touches = e.browserEvent.touches;
31205 if(touches.length == 1){
31206 this.onMouseDown(e);
31210 if(touches.length != 2){
31216 for(var i = 0, finger; finger = touches[i]; i++){
31217 coords.push(finger.pageX, finger.pageY);
31220 var x = Math.pow(coords[0] - coords[2], 2);
31221 var y = Math.pow(coords[1] - coords[3], 2);
31223 this.startDistance = Math.sqrt(x + y);
31225 this.startScale = this.scale;
31227 this.pinching = true;
31228 this.dragable = false;
31232 onTouchMove : function(e)
31234 if(!this.pinching && !this.dragable){
31238 var touches = e.browserEvent.touches;
31245 this.onMouseMove(e);
31251 for(var i = 0, finger; finger = touches[i]; i++){
31252 coords.push(finger.pageX, finger.pageY);
31255 var x = Math.pow(coords[0] - coords[2], 2);
31256 var y = Math.pow(coords[1] - coords[3], 2);
31258 this.endDistance = Math.sqrt(x + y);
31260 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31262 if(!this.zoomable()){
31263 this.scale = this.startScale;
31271 onTouchEnd : function(e)
31273 this.pinching = false;
31274 this.dragable = false;
31278 process : function(file, crop)
31281 this.maskEl.mask(this.loadingText);
31284 this.xhr = new XMLHttpRequest();
31286 file.xhr = this.xhr;
31288 this.xhr.open(this.method, this.url, true);
31291 "Accept": "application/json",
31292 "Cache-Control": "no-cache",
31293 "X-Requested-With": "XMLHttpRequest"
31296 for (var headerName in headers) {
31297 var headerValue = headers[headerName];
31299 this.xhr.setRequestHeader(headerName, headerValue);
31305 this.xhr.onload = function()
31307 _this.xhrOnLoad(_this.xhr);
31310 this.xhr.onerror = function()
31312 _this.xhrOnError(_this.xhr);
31315 var formData = new FormData();
31317 formData.append('returnHTML', 'NO');
31320 formData.append('crop', crop);
31323 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31324 formData.append(this.paramName, file, file.name);
31327 if(typeof(file.filename) != 'undefined'){
31328 formData.append('filename', file.filename);
31331 if(typeof(file.mimetype) != 'undefined'){
31332 formData.append('mimetype', file.mimetype);
31335 if(this.fireEvent('arrange', this, formData) != false){
31336 this.xhr.send(formData);
31340 xhrOnLoad : function(xhr)
31343 this.maskEl.unmask();
31346 if (xhr.readyState !== 4) {
31347 this.fireEvent('exception', this, xhr);
31351 var response = Roo.decode(xhr.responseText);
31353 if(!response.success){
31354 this.fireEvent('exception', this, xhr);
31358 var response = Roo.decode(xhr.responseText);
31360 this.fireEvent('upload', this, response);
31364 xhrOnError : function()
31367 this.maskEl.unmask();
31370 Roo.log('xhr on error');
31372 var response = Roo.decode(xhr.responseText);
31378 prepare : function(file)
31381 this.maskEl.mask(this.loadingText);
31387 if(typeof(file) === 'string'){
31388 this.loadCanvas(file);
31392 if(!file || !this.urlAPI){
31397 this.cropType = file.type;
31401 if(this.fireEvent('prepare', this, this.file) != false){
31403 var reader = new FileReader();
31405 reader.onload = function (e) {
31406 if (e.target.error) {
31407 Roo.log(e.target.error);
31411 var buffer = e.target.result,
31412 dataView = new DataView(buffer),
31414 maxOffset = dataView.byteLength - 4,
31418 if (dataView.getUint16(0) === 0xffd8) {
31419 while (offset < maxOffset) {
31420 markerBytes = dataView.getUint16(offset);
31422 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31423 markerLength = dataView.getUint16(offset + 2) + 2;
31424 if (offset + markerLength > dataView.byteLength) {
31425 Roo.log('Invalid meta data: Invalid segment size.');
31429 if(markerBytes == 0xffe1){
31430 _this.parseExifData(
31437 offset += markerLength;
31447 var url = _this.urlAPI.createObjectURL(_this.file);
31449 _this.loadCanvas(url);
31454 reader.readAsArrayBuffer(this.file);
31460 parseExifData : function(dataView, offset, length)
31462 var tiffOffset = offset + 10,
31466 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31467 // No Exif data, might be XMP data instead
31471 // Check for the ASCII code for "Exif" (0x45786966):
31472 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31473 // No Exif data, might be XMP data instead
31476 if (tiffOffset + 8 > dataView.byteLength) {
31477 Roo.log('Invalid Exif data: Invalid segment size.');
31480 // Check for the two null bytes:
31481 if (dataView.getUint16(offset + 8) !== 0x0000) {
31482 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31485 // Check the byte alignment:
31486 switch (dataView.getUint16(tiffOffset)) {
31488 littleEndian = true;
31491 littleEndian = false;
31494 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31497 // Check for the TIFF tag marker (0x002A):
31498 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31499 Roo.log('Invalid Exif data: Missing TIFF marker.');
31502 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31503 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31505 this.parseExifTags(
31508 tiffOffset + dirOffset,
31513 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31518 if (dirOffset + 6 > dataView.byteLength) {
31519 Roo.log('Invalid Exif data: Invalid directory offset.');
31522 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31523 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31524 if (dirEndOffset + 4 > dataView.byteLength) {
31525 Roo.log('Invalid Exif data: Invalid directory size.');
31528 for (i = 0; i < tagsNumber; i += 1) {
31532 dirOffset + 2 + 12 * i, // tag offset
31536 // Return the offset to the next directory:
31537 return dataView.getUint32(dirEndOffset, littleEndian);
31540 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31542 var tag = dataView.getUint16(offset, littleEndian);
31544 this.exif[tag] = this.getExifValue(
31548 dataView.getUint16(offset + 2, littleEndian), // tag type
31549 dataView.getUint32(offset + 4, littleEndian), // tag length
31554 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31556 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31565 Roo.log('Invalid Exif data: Invalid tag type.');
31569 tagSize = tagType.size * length;
31570 // Determine if the value is contained in the dataOffset bytes,
31571 // or if the value at the dataOffset is a pointer to the actual data:
31572 dataOffset = tagSize > 4 ?
31573 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31574 if (dataOffset + tagSize > dataView.byteLength) {
31575 Roo.log('Invalid Exif data: Invalid data offset.');
31578 if (length === 1) {
31579 return tagType.getValue(dataView, dataOffset, littleEndian);
31582 for (i = 0; i < length; i += 1) {
31583 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31586 if (tagType.ascii) {
31588 // Concatenate the chars:
31589 for (i = 0; i < values.length; i += 1) {
31591 // Ignore the terminating NULL byte(s):
31592 if (c === '\u0000') {
31604 Roo.apply(Roo.bootstrap.UploadCropbox, {
31606 'Orientation': 0x0112
31610 1: 0, //'top-left',
31612 3: 180, //'bottom-right',
31613 // 4: 'bottom-left',
31615 6: 90, //'right-top',
31616 // 7: 'right-bottom',
31617 8: 270 //'left-bottom'
31621 // byte, 8-bit unsigned int:
31623 getValue: function (dataView, dataOffset) {
31624 return dataView.getUint8(dataOffset);
31628 // ascii, 8-bit byte:
31630 getValue: function (dataView, dataOffset) {
31631 return String.fromCharCode(dataView.getUint8(dataOffset));
31636 // short, 16 bit int:
31638 getValue: function (dataView, dataOffset, littleEndian) {
31639 return dataView.getUint16(dataOffset, littleEndian);
31643 // long, 32 bit int:
31645 getValue: function (dataView, dataOffset, littleEndian) {
31646 return dataView.getUint32(dataOffset, littleEndian);
31650 // rational = two long values, first is numerator, second is denominator:
31652 getValue: function (dataView, dataOffset, littleEndian) {
31653 return dataView.getUint32(dataOffset, littleEndian) /
31654 dataView.getUint32(dataOffset + 4, littleEndian);
31658 // slong, 32 bit signed int:
31660 getValue: function (dataView, dataOffset, littleEndian) {
31661 return dataView.getInt32(dataOffset, littleEndian);
31665 // srational, two slongs, first is numerator, second is denominator:
31667 getValue: function (dataView, dataOffset, littleEndian) {
31668 return dataView.getInt32(dataOffset, littleEndian) /
31669 dataView.getInt32(dataOffset + 4, littleEndian);
31679 cls : 'btn-group roo-upload-cropbox-rotate-left',
31680 action : 'rotate-left',
31684 cls : 'btn btn-default',
31685 html : '<i class="fa fa-undo"></i>'
31691 cls : 'btn-group roo-upload-cropbox-picture',
31692 action : 'picture',
31696 cls : 'btn btn-default',
31697 html : '<i class="fa fa-picture-o"></i>'
31703 cls : 'btn-group roo-upload-cropbox-rotate-right',
31704 action : 'rotate-right',
31708 cls : 'btn btn-default',
31709 html : '<i class="fa fa-repeat"></i>'
31717 cls : 'btn-group roo-upload-cropbox-rotate-left',
31718 action : 'rotate-left',
31722 cls : 'btn btn-default',
31723 html : '<i class="fa fa-undo"></i>'
31729 cls : 'btn-group roo-upload-cropbox-download',
31730 action : 'download',
31734 cls : 'btn btn-default',
31735 html : '<i class="fa fa-download"></i>'
31741 cls : 'btn-group roo-upload-cropbox-crop',
31746 cls : 'btn btn-default',
31747 html : '<i class="fa fa-crop"></i>'
31753 cls : 'btn-group roo-upload-cropbox-trash',
31758 cls : 'btn btn-default',
31759 html : '<i class="fa fa-trash"></i>'
31765 cls : 'btn-group roo-upload-cropbox-rotate-right',
31766 action : 'rotate-right',
31770 cls : 'btn btn-default',
31771 html : '<i class="fa fa-repeat"></i>'
31779 cls : 'btn-group roo-upload-cropbox-rotate-left',
31780 action : 'rotate-left',
31784 cls : 'btn btn-default',
31785 html : '<i class="fa fa-undo"></i>'
31791 cls : 'btn-group roo-upload-cropbox-rotate-right',
31792 action : 'rotate-right',
31796 cls : 'btn btn-default',
31797 html : '<i class="fa fa-repeat"></i>'
31810 * @class Roo.bootstrap.DocumentManager
31811 * @extends Roo.bootstrap.Component
31812 * Bootstrap DocumentManager class
31813 * @cfg {String} paramName default 'imageUpload'
31814 * @cfg {String} toolTipName default 'filename'
31815 * @cfg {String} method default POST
31816 * @cfg {String} url action url
31817 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31818 * @cfg {Boolean} multiple multiple upload default true
31819 * @cfg {Number} thumbSize default 300
31820 * @cfg {String} fieldLabel
31821 * @cfg {Number} labelWidth default 4
31822 * @cfg {String} labelAlign (left|top) default left
31823 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31824 * @cfg {Number} labellg set the width of label (1-12)
31825 * @cfg {Number} labelmd set the width of label (1-12)
31826 * @cfg {Number} labelsm set the width of label (1-12)
31827 * @cfg {Number} labelxs set the width of label (1-12)
31830 * Create a new DocumentManager
31831 * @param {Object} config The config object
31834 Roo.bootstrap.DocumentManager = function(config){
31835 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31838 this.delegates = [];
31843 * Fire when initial the DocumentManager
31844 * @param {Roo.bootstrap.DocumentManager} this
31849 * inspect selected file
31850 * @param {Roo.bootstrap.DocumentManager} this
31851 * @param {File} file
31856 * Fire when xhr load exception
31857 * @param {Roo.bootstrap.DocumentManager} this
31858 * @param {XMLHttpRequest} xhr
31860 "exception" : true,
31862 * @event afterupload
31863 * Fire when xhr load exception
31864 * @param {Roo.bootstrap.DocumentManager} this
31865 * @param {XMLHttpRequest} xhr
31867 "afterupload" : true,
31870 * prepare the form data
31871 * @param {Roo.bootstrap.DocumentManager} this
31872 * @param {Object} formData
31877 * Fire when remove the file
31878 * @param {Roo.bootstrap.DocumentManager} this
31879 * @param {Object} file
31884 * Fire after refresh the file
31885 * @param {Roo.bootstrap.DocumentManager} this
31890 * Fire after click the image
31891 * @param {Roo.bootstrap.DocumentManager} this
31892 * @param {Object} file
31897 * Fire when upload a image and editable set to true
31898 * @param {Roo.bootstrap.DocumentManager} this
31899 * @param {Object} file
31903 * @event beforeselectfile
31904 * Fire before select file
31905 * @param {Roo.bootstrap.DocumentManager} this
31907 "beforeselectfile" : true,
31910 * Fire before process file
31911 * @param {Roo.bootstrap.DocumentManager} this
31912 * @param {Object} file
31916 * @event previewrendered
31917 * Fire when preview rendered
31918 * @param {Roo.bootstrap.DocumentManager} this
31919 * @param {Object} file
31921 "previewrendered" : true,
31924 "previewResize" : true
31929 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31938 paramName : 'imageUpload',
31939 toolTipName : 'filename',
31942 labelAlign : 'left',
31952 getAutoCreate : function()
31954 var managerWidget = {
31956 cls : 'roo-document-manager',
31960 cls : 'roo-document-manager-selector',
31965 cls : 'roo-document-manager-uploader',
31969 cls : 'roo-document-manager-upload-btn',
31970 html : '<i class="fa fa-plus"></i>'
31981 cls : 'column col-md-12',
31986 if(this.fieldLabel.length){
31991 cls : 'column col-md-12',
31992 html : this.fieldLabel
31996 cls : 'column col-md-12',
32001 if(this.labelAlign == 'left'){
32006 html : this.fieldLabel
32015 if(this.labelWidth > 12){
32016 content[0].style = "width: " + this.labelWidth + 'px';
32019 if(this.labelWidth < 13 && this.labelmd == 0){
32020 this.labelmd = this.labelWidth;
32023 if(this.labellg > 0){
32024 content[0].cls += ' col-lg-' + this.labellg;
32025 content[1].cls += ' col-lg-' + (12 - this.labellg);
32028 if(this.labelmd > 0){
32029 content[0].cls += ' col-md-' + this.labelmd;
32030 content[1].cls += ' col-md-' + (12 - this.labelmd);
32033 if(this.labelsm > 0){
32034 content[0].cls += ' col-sm-' + this.labelsm;
32035 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32038 if(this.labelxs > 0){
32039 content[0].cls += ' col-xs-' + this.labelxs;
32040 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32048 cls : 'row clearfix',
32056 initEvents : function()
32058 this.managerEl = this.el.select('.roo-document-manager', true).first();
32059 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32061 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32062 this.selectorEl.hide();
32065 this.selectorEl.attr('multiple', 'multiple');
32068 this.selectorEl.on('change', this.onFileSelected, this);
32070 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32071 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32073 this.uploader.on('click', this.onUploaderClick, this);
32075 this.renderProgressDialog();
32079 window.addEventListener("resize", function() { _this.refresh(); } );
32081 this.fireEvent('initial', this);
32084 renderProgressDialog : function()
32088 this.progressDialog = new Roo.bootstrap.Modal({
32089 cls : 'roo-document-manager-progress-dialog',
32090 allow_close : false,
32101 btnclick : function() {
32102 _this.uploadCancel();
32108 this.progressDialog.render(Roo.get(document.body));
32110 this.progress = new Roo.bootstrap.Progress({
32111 cls : 'roo-document-manager-progress',
32116 this.progress.render(this.progressDialog.getChildContainer());
32118 this.progressBar = new Roo.bootstrap.ProgressBar({
32119 cls : 'roo-document-manager-progress-bar',
32122 aria_valuemax : 12,
32126 this.progressBar.render(this.progress.getChildContainer());
32129 onUploaderClick : function(e)
32131 e.preventDefault();
32133 if(this.fireEvent('beforeselectfile', this) != false){
32134 this.selectorEl.dom.click();
32139 onFileSelected : function(e)
32141 e.preventDefault();
32143 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32147 Roo.each(this.selectorEl.dom.files, function(file){
32148 if(this.fireEvent('inspect', this, file) != false){
32149 this.files.push(file);
32159 this.selectorEl.dom.value = '';
32161 if(!this.files || !this.files.length){
32165 if(this.boxes > 0 && this.files.length > this.boxes){
32166 this.files = this.files.slice(0, this.boxes);
32169 this.uploader.show();
32171 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32172 this.uploader.hide();
32181 Roo.each(this.files, function(file){
32183 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32184 var f = this.renderPreview(file);
32189 if(file.type.indexOf('image') != -1){
32190 this.delegates.push(
32192 _this.process(file);
32193 }).createDelegate(this)
32201 _this.process(file);
32202 }).createDelegate(this)
32207 this.files = files;
32209 this.delegates = this.delegates.concat(docs);
32211 if(!this.delegates.length){
32216 this.progressBar.aria_valuemax = this.delegates.length;
32223 arrange : function()
32225 if(!this.delegates.length){
32226 this.progressDialog.hide();
32231 var delegate = this.delegates.shift();
32233 this.progressDialog.show();
32235 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32237 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32242 refresh : function()
32244 this.uploader.show();
32246 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32247 this.uploader.hide();
32250 Roo.isTouch ? this.closable(false) : this.closable(true);
32252 this.fireEvent('refresh', this);
32255 onRemove : function(e, el, o)
32257 e.preventDefault();
32259 this.fireEvent('remove', this, o);
32263 remove : function(o)
32267 Roo.each(this.files, function(file){
32268 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32277 this.files = files;
32284 Roo.each(this.files, function(file){
32289 file.target.remove();
32298 onClick : function(e, el, o)
32300 e.preventDefault();
32302 this.fireEvent('click', this, o);
32306 closable : function(closable)
32308 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32310 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32322 xhrOnLoad : function(xhr)
32324 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32328 if (xhr.readyState !== 4) {
32330 this.fireEvent('exception', this, xhr);
32334 var response = Roo.decode(xhr.responseText);
32336 if(!response.success){
32338 this.fireEvent('exception', this, xhr);
32342 var file = this.renderPreview(response.data);
32344 this.files.push(file);
32348 this.fireEvent('afterupload', this, xhr);
32352 xhrOnError : function(xhr)
32354 Roo.log('xhr on error');
32356 var response = Roo.decode(xhr.responseText);
32363 process : function(file)
32365 if(this.fireEvent('process', this, file) !== false){
32366 if(this.editable && file.type.indexOf('image') != -1){
32367 this.fireEvent('edit', this, file);
32371 this.uploadStart(file, false);
32378 uploadStart : function(file, crop)
32380 this.xhr = new XMLHttpRequest();
32382 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32387 file.xhr = this.xhr;
32389 this.managerEl.createChild({
32391 cls : 'roo-document-manager-loading',
32395 tooltip : file.name,
32396 cls : 'roo-document-manager-thumb',
32397 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32403 this.xhr.open(this.method, this.url, true);
32406 "Accept": "application/json",
32407 "Cache-Control": "no-cache",
32408 "X-Requested-With": "XMLHttpRequest"
32411 for (var headerName in headers) {
32412 var headerValue = headers[headerName];
32414 this.xhr.setRequestHeader(headerName, headerValue);
32420 this.xhr.onload = function()
32422 _this.xhrOnLoad(_this.xhr);
32425 this.xhr.onerror = function()
32427 _this.xhrOnError(_this.xhr);
32430 var formData = new FormData();
32432 formData.append('returnHTML', 'NO');
32435 formData.append('crop', crop);
32438 formData.append(this.paramName, file, file.name);
32445 if(this.fireEvent('prepare', this, formData, options) != false){
32447 if(options.manually){
32451 this.xhr.send(formData);
32455 this.uploadCancel();
32458 uploadCancel : function()
32464 this.delegates = [];
32466 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32473 renderPreview : function(file)
32475 if(typeof(file.target) != 'undefined' && file.target){
32479 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32481 var previewEl = this.managerEl.createChild({
32483 cls : 'roo-document-manager-preview',
32487 tooltip : file[this.toolTipName],
32488 cls : 'roo-document-manager-thumb',
32489 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32494 html : '<i class="fa fa-times-circle"></i>'
32499 var close = previewEl.select('button.close', true).first();
32501 close.on('click', this.onRemove, this, file);
32503 file.target = previewEl;
32505 var image = previewEl.select('img', true).first();
32509 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32511 image.on('click', this.onClick, this, file);
32513 this.fireEvent('previewrendered', this, file);
32519 onPreviewLoad : function(file, image)
32521 if(typeof(file.target) == 'undefined' || !file.target){
32525 var width = image.dom.naturalWidth || image.dom.width;
32526 var height = image.dom.naturalHeight || image.dom.height;
32528 if(!this.previewResize) {
32532 if(width > height){
32533 file.target.addClass('wide');
32537 file.target.addClass('tall');
32542 uploadFromSource : function(file, crop)
32544 this.xhr = new XMLHttpRequest();
32546 this.managerEl.createChild({
32548 cls : 'roo-document-manager-loading',
32552 tooltip : file.name,
32553 cls : 'roo-document-manager-thumb',
32554 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32560 this.xhr.open(this.method, this.url, true);
32563 "Accept": "application/json",
32564 "Cache-Control": "no-cache",
32565 "X-Requested-With": "XMLHttpRequest"
32568 for (var headerName in headers) {
32569 var headerValue = headers[headerName];
32571 this.xhr.setRequestHeader(headerName, headerValue);
32577 this.xhr.onload = function()
32579 _this.xhrOnLoad(_this.xhr);
32582 this.xhr.onerror = function()
32584 _this.xhrOnError(_this.xhr);
32587 var formData = new FormData();
32589 formData.append('returnHTML', 'NO');
32591 formData.append('crop', crop);
32593 if(typeof(file.filename) != 'undefined'){
32594 formData.append('filename', file.filename);
32597 if(typeof(file.mimetype) != 'undefined'){
32598 formData.append('mimetype', file.mimetype);
32603 if(this.fireEvent('prepare', this, formData) != false){
32604 this.xhr.send(formData);
32614 * @class Roo.bootstrap.DocumentViewer
32615 * @extends Roo.bootstrap.Component
32616 * Bootstrap DocumentViewer class
32617 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32618 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32621 * Create a new DocumentViewer
32622 * @param {Object} config The config object
32625 Roo.bootstrap.DocumentViewer = function(config){
32626 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32631 * Fire after initEvent
32632 * @param {Roo.bootstrap.DocumentViewer} this
32638 * @param {Roo.bootstrap.DocumentViewer} this
32643 * Fire after download button
32644 * @param {Roo.bootstrap.DocumentViewer} this
32649 * Fire after trash button
32650 * @param {Roo.bootstrap.DocumentViewer} this
32657 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32659 showDownload : true,
32663 getAutoCreate : function()
32667 cls : 'roo-document-viewer',
32671 cls : 'roo-document-viewer-body',
32675 cls : 'roo-document-viewer-thumb',
32679 cls : 'roo-document-viewer-image'
32687 cls : 'roo-document-viewer-footer',
32690 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32694 cls : 'btn-group roo-document-viewer-download',
32698 cls : 'btn btn-default',
32699 html : '<i class="fa fa-download"></i>'
32705 cls : 'btn-group roo-document-viewer-trash',
32709 cls : 'btn btn-default',
32710 html : '<i class="fa fa-trash"></i>'
32723 initEvents : function()
32725 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32726 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32728 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32729 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32731 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32732 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32734 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32735 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32737 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32738 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32740 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32741 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32743 this.bodyEl.on('click', this.onClick, this);
32744 this.downloadBtn.on('click', this.onDownload, this);
32745 this.trashBtn.on('click', this.onTrash, this);
32747 this.downloadBtn.hide();
32748 this.trashBtn.hide();
32750 if(this.showDownload){
32751 this.downloadBtn.show();
32754 if(this.showTrash){
32755 this.trashBtn.show();
32758 if(!this.showDownload && !this.showTrash) {
32759 this.footerEl.hide();
32764 initial : function()
32766 this.fireEvent('initial', this);
32770 onClick : function(e)
32772 e.preventDefault();
32774 this.fireEvent('click', this);
32777 onDownload : function(e)
32779 e.preventDefault();
32781 this.fireEvent('download', this);
32784 onTrash : function(e)
32786 e.preventDefault();
32788 this.fireEvent('trash', this);
32800 * @class Roo.bootstrap.NavProgressBar
32801 * @extends Roo.bootstrap.Component
32802 * Bootstrap NavProgressBar class
32805 * Create a new nav progress bar
32806 * @param {Object} config The config object
32809 Roo.bootstrap.NavProgressBar = function(config){
32810 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32812 this.bullets = this.bullets || [];
32814 // Roo.bootstrap.NavProgressBar.register(this);
32818 * Fires when the active item changes
32819 * @param {Roo.bootstrap.NavProgressBar} this
32820 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32821 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32828 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32833 getAutoCreate : function()
32835 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32839 cls : 'roo-navigation-bar-group',
32843 cls : 'roo-navigation-top-bar'
32847 cls : 'roo-navigation-bullets-bar',
32851 cls : 'roo-navigation-bar'
32858 cls : 'roo-navigation-bottom-bar'
32868 initEvents: function()
32873 onRender : function(ct, position)
32875 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32877 if(this.bullets.length){
32878 Roo.each(this.bullets, function(b){
32887 addItem : function(cfg)
32889 var item = new Roo.bootstrap.NavProgressItem(cfg);
32891 item.parentId = this.id;
32892 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32895 var top = new Roo.bootstrap.Element({
32897 cls : 'roo-navigation-bar-text'
32900 var bottom = new Roo.bootstrap.Element({
32902 cls : 'roo-navigation-bar-text'
32905 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32906 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32908 var topText = new Roo.bootstrap.Element({
32910 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32913 var bottomText = new Roo.bootstrap.Element({
32915 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32918 topText.onRender(top.el, null);
32919 bottomText.onRender(bottom.el, null);
32922 item.bottomEl = bottom;
32925 this.barItems.push(item);
32930 getActive : function()
32932 var active = false;
32934 Roo.each(this.barItems, function(v){
32936 if (!v.isActive()) {
32948 setActiveItem : function(item)
32952 Roo.each(this.barItems, function(v){
32953 if (v.rid == item.rid) {
32957 if (v.isActive()) {
32958 v.setActive(false);
32963 item.setActive(true);
32965 this.fireEvent('changed', this, item, prev);
32968 getBarItem: function(rid)
32972 Roo.each(this.barItems, function(e) {
32973 if (e.rid != rid) {
32984 indexOfItem : function(item)
32988 Roo.each(this.barItems, function(v, i){
32990 if (v.rid != item.rid) {
33001 setActiveNext : function()
33003 var i = this.indexOfItem(this.getActive());
33005 if (i > this.barItems.length) {
33009 this.setActiveItem(this.barItems[i+1]);
33012 setActivePrev : function()
33014 var i = this.indexOfItem(this.getActive());
33020 this.setActiveItem(this.barItems[i-1]);
33023 format : function()
33025 if(!this.barItems.length){
33029 var width = 100 / this.barItems.length;
33031 Roo.each(this.barItems, function(i){
33032 i.el.setStyle('width', width + '%');
33033 i.topEl.el.setStyle('width', width + '%');
33034 i.bottomEl.el.setStyle('width', width + '%');
33043 * Nav Progress Item
33048 * @class Roo.bootstrap.NavProgressItem
33049 * @extends Roo.bootstrap.Component
33050 * Bootstrap NavProgressItem class
33051 * @cfg {String} rid the reference id
33052 * @cfg {Boolean} active (true|false) Is item active default false
33053 * @cfg {Boolean} disabled (true|false) Is item active default false
33054 * @cfg {String} html
33055 * @cfg {String} position (top|bottom) text position default bottom
33056 * @cfg {String} icon show icon instead of number
33059 * Create a new NavProgressItem
33060 * @param {Object} config The config object
33062 Roo.bootstrap.NavProgressItem = function(config){
33063 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33068 * The raw click event for the entire grid.
33069 * @param {Roo.bootstrap.NavProgressItem} this
33070 * @param {Roo.EventObject} e
33077 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33083 position : 'bottom',
33086 getAutoCreate : function()
33088 var iconCls = 'roo-navigation-bar-item-icon';
33090 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33094 cls: 'roo-navigation-bar-item',
33104 cfg.cls += ' active';
33107 cfg.cls += ' disabled';
33113 disable : function()
33115 this.setDisabled(true);
33118 enable : function()
33120 this.setDisabled(false);
33123 initEvents: function()
33125 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33127 this.iconEl.on('click', this.onClick, this);
33130 onClick : function(e)
33132 e.preventDefault();
33138 if(this.fireEvent('click', this, e) === false){
33142 this.parent().setActiveItem(this);
33145 isActive: function ()
33147 return this.active;
33150 setActive : function(state)
33152 if(this.active == state){
33156 this.active = state;
33159 this.el.addClass('active');
33163 this.el.removeClass('active');
33168 setDisabled : function(state)
33170 if(this.disabled == state){
33174 this.disabled = state;
33177 this.el.addClass('disabled');
33181 this.el.removeClass('disabled');
33184 tooltipEl : function()
33186 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33199 * @class Roo.bootstrap.FieldLabel
33200 * @extends Roo.bootstrap.Component
33201 * Bootstrap FieldLabel class
33202 * @cfg {String} html contents of the element
33203 * @cfg {String} tag tag of the element default label
33204 * @cfg {String} cls class of the element
33205 * @cfg {String} target label target
33206 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33207 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33208 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33209 * @cfg {String} iconTooltip default "This field is required"
33210 * @cfg {String} indicatorpos (left|right) default left
33213 * Create a new FieldLabel
33214 * @param {Object} config The config object
33217 Roo.bootstrap.FieldLabel = function(config){
33218 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33223 * Fires after the field has been marked as invalid.
33224 * @param {Roo.form.FieldLabel} this
33225 * @param {String} msg The validation message
33230 * Fires after the field has been validated with no errors.
33231 * @param {Roo.form.FieldLabel} this
33237 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33244 invalidClass : 'has-warning',
33245 validClass : 'has-success',
33246 iconTooltip : 'This field is required',
33247 indicatorpos : 'left',
33249 getAutoCreate : function(){
33252 if (!this.allowBlank) {
33258 cls : 'roo-bootstrap-field-label ' + this.cls,
33263 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33264 tooltip : this.iconTooltip
33273 if(this.indicatorpos == 'right'){
33276 cls : 'roo-bootstrap-field-label ' + this.cls,
33285 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33286 tooltip : this.iconTooltip
33295 initEvents: function()
33297 Roo.bootstrap.Element.superclass.initEvents.call(this);
33299 this.indicator = this.indicatorEl();
33301 if(this.indicator){
33302 this.indicator.removeClass('visible');
33303 this.indicator.addClass('invisible');
33306 Roo.bootstrap.FieldLabel.register(this);
33309 indicatorEl : function()
33311 var indicator = this.el.select('i.roo-required-indicator',true).first();
33322 * Mark this field as valid
33324 markValid : function()
33326 if(this.indicator){
33327 this.indicator.removeClass('visible');
33328 this.indicator.addClass('invisible');
33330 if (Roo.bootstrap.version == 3) {
33331 this.el.removeClass(this.invalidClass);
33332 this.el.addClass(this.validClass);
33334 this.el.removeClass('is-invalid');
33335 this.el.addClass('is-valid');
33339 this.fireEvent('valid', this);
33343 * Mark this field as invalid
33344 * @param {String} msg The validation message
33346 markInvalid : function(msg)
33348 if(this.indicator){
33349 this.indicator.removeClass('invisible');
33350 this.indicator.addClass('visible');
33352 if (Roo.bootstrap.version == 3) {
33353 this.el.removeClass(this.validClass);
33354 this.el.addClass(this.invalidClass);
33356 this.el.removeClass('is-valid');
33357 this.el.addClass('is-invalid');
33361 this.fireEvent('invalid', this, msg);
33367 Roo.apply(Roo.bootstrap.FieldLabel, {
33372 * register a FieldLabel Group
33373 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33375 register : function(label)
33377 if(this.groups.hasOwnProperty(label.target)){
33381 this.groups[label.target] = label;
33385 * fetch a FieldLabel Group based on the target
33386 * @param {string} target
33387 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33389 get: function(target) {
33390 if (typeof(this.groups[target]) == 'undefined') {
33394 return this.groups[target] ;
33403 * page DateSplitField.
33409 * @class Roo.bootstrap.DateSplitField
33410 * @extends Roo.bootstrap.Component
33411 * Bootstrap DateSplitField class
33412 * @cfg {string} fieldLabel - the label associated
33413 * @cfg {Number} labelWidth set the width of label (0-12)
33414 * @cfg {String} labelAlign (top|left)
33415 * @cfg {Boolean} dayAllowBlank (true|false) default false
33416 * @cfg {Boolean} monthAllowBlank (true|false) default false
33417 * @cfg {Boolean} yearAllowBlank (true|false) default false
33418 * @cfg {string} dayPlaceholder
33419 * @cfg {string} monthPlaceholder
33420 * @cfg {string} yearPlaceholder
33421 * @cfg {string} dayFormat default 'd'
33422 * @cfg {string} monthFormat default 'm'
33423 * @cfg {string} yearFormat default 'Y'
33424 * @cfg {Number} labellg set the width of label (1-12)
33425 * @cfg {Number} labelmd set the width of label (1-12)
33426 * @cfg {Number} labelsm set the width of label (1-12)
33427 * @cfg {Number} labelxs set the width of label (1-12)
33431 * Create a new DateSplitField
33432 * @param {Object} config The config object
33435 Roo.bootstrap.DateSplitField = function(config){
33436 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33442 * getting the data of years
33443 * @param {Roo.bootstrap.DateSplitField} this
33444 * @param {Object} years
33449 * getting the data of days
33450 * @param {Roo.bootstrap.DateSplitField} this
33451 * @param {Object} days
33456 * Fires after the field has been marked as invalid.
33457 * @param {Roo.form.Field} this
33458 * @param {String} msg The validation message
33463 * Fires after the field has been validated with no errors.
33464 * @param {Roo.form.Field} this
33470 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33473 labelAlign : 'top',
33475 dayAllowBlank : false,
33476 monthAllowBlank : false,
33477 yearAllowBlank : false,
33478 dayPlaceholder : '',
33479 monthPlaceholder : '',
33480 yearPlaceholder : '',
33484 isFormField : true,
33490 getAutoCreate : function()
33494 cls : 'row roo-date-split-field-group',
33499 cls : 'form-hidden-field roo-date-split-field-group-value',
33505 var labelCls = 'col-md-12';
33506 var contentCls = 'col-md-4';
33508 if(this.fieldLabel){
33512 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33516 html : this.fieldLabel
33521 if(this.labelAlign == 'left'){
33523 if(this.labelWidth > 12){
33524 label.style = "width: " + this.labelWidth + 'px';
33527 if(this.labelWidth < 13 && this.labelmd == 0){
33528 this.labelmd = this.labelWidth;
33531 if(this.labellg > 0){
33532 labelCls = ' col-lg-' + this.labellg;
33533 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33536 if(this.labelmd > 0){
33537 labelCls = ' col-md-' + this.labelmd;
33538 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33541 if(this.labelsm > 0){
33542 labelCls = ' col-sm-' + this.labelsm;
33543 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33546 if(this.labelxs > 0){
33547 labelCls = ' col-xs-' + this.labelxs;
33548 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33552 label.cls += ' ' + labelCls;
33554 cfg.cn.push(label);
33557 Roo.each(['day', 'month', 'year'], function(t){
33560 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33567 inputEl: function ()
33569 return this.el.select('.roo-date-split-field-group-value', true).first();
33572 onRender : function(ct, position)
33576 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33578 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33580 this.dayField = new Roo.bootstrap.ComboBox({
33581 allowBlank : this.dayAllowBlank,
33582 alwaysQuery : true,
33583 displayField : 'value',
33586 forceSelection : true,
33588 placeholder : this.dayPlaceholder,
33589 selectOnFocus : true,
33590 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33591 triggerAction : 'all',
33593 valueField : 'value',
33594 store : new Roo.data.SimpleStore({
33595 data : (function() {
33597 _this.fireEvent('days', _this, days);
33600 fields : [ 'value' ]
33603 select : function (_self, record, index)
33605 _this.setValue(_this.getValue());
33610 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33612 this.monthField = new Roo.bootstrap.MonthField({
33613 after : '<i class=\"fa fa-calendar\"></i>',
33614 allowBlank : this.monthAllowBlank,
33615 placeholder : this.monthPlaceholder,
33618 render : function (_self)
33620 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33621 e.preventDefault();
33625 select : function (_self, oldvalue, newvalue)
33627 _this.setValue(_this.getValue());
33632 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33634 this.yearField = new Roo.bootstrap.ComboBox({
33635 allowBlank : this.yearAllowBlank,
33636 alwaysQuery : true,
33637 displayField : 'value',
33640 forceSelection : true,
33642 placeholder : this.yearPlaceholder,
33643 selectOnFocus : true,
33644 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33645 triggerAction : 'all',
33647 valueField : 'value',
33648 store : new Roo.data.SimpleStore({
33649 data : (function() {
33651 _this.fireEvent('years', _this, years);
33654 fields : [ 'value' ]
33657 select : function (_self, record, index)
33659 _this.setValue(_this.getValue());
33664 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33667 setValue : function(v, format)
33669 this.inputEl.dom.value = v;
33671 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33673 var d = Date.parseDate(v, f);
33680 this.setDay(d.format(this.dayFormat));
33681 this.setMonth(d.format(this.monthFormat));
33682 this.setYear(d.format(this.yearFormat));
33689 setDay : function(v)
33691 this.dayField.setValue(v);
33692 this.inputEl.dom.value = this.getValue();
33697 setMonth : function(v)
33699 this.monthField.setValue(v, true);
33700 this.inputEl.dom.value = this.getValue();
33705 setYear : function(v)
33707 this.yearField.setValue(v);
33708 this.inputEl.dom.value = this.getValue();
33713 getDay : function()
33715 return this.dayField.getValue();
33718 getMonth : function()
33720 return this.monthField.getValue();
33723 getYear : function()
33725 return this.yearField.getValue();
33728 getValue : function()
33730 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33732 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33742 this.inputEl.dom.value = '';
33747 validate : function()
33749 var d = this.dayField.validate();
33750 var m = this.monthField.validate();
33751 var y = this.yearField.validate();
33756 (!this.dayAllowBlank && !d) ||
33757 (!this.monthAllowBlank && !m) ||
33758 (!this.yearAllowBlank && !y)
33763 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33772 this.markInvalid();
33777 markValid : function()
33780 var label = this.el.select('label', true).first();
33781 var icon = this.el.select('i.fa-star', true).first();
33787 this.fireEvent('valid', this);
33791 * Mark this field as invalid
33792 * @param {String} msg The validation message
33794 markInvalid : function(msg)
33797 var label = this.el.select('label', true).first();
33798 var icon = this.el.select('i.fa-star', true).first();
33800 if(label && !icon){
33801 this.el.select('.roo-date-split-field-label', true).createChild({
33803 cls : 'text-danger fa fa-lg fa-star',
33804 tooltip : 'This field is required',
33805 style : 'margin-right:5px;'
33809 this.fireEvent('invalid', this, msg);
33812 clearInvalid : function()
33814 var label = this.el.select('label', true).first();
33815 var icon = this.el.select('i.fa-star', true).first();
33821 this.fireEvent('valid', this);
33824 getName: function()
33834 * http://masonry.desandro.com
33836 * The idea is to render all the bricks based on vertical width...
33838 * The original code extends 'outlayer' - we might need to use that....
33844 * @class Roo.bootstrap.LayoutMasonry
33845 * @extends Roo.bootstrap.Component
33846 * Bootstrap Layout Masonry class
33849 * Create a new Element
33850 * @param {Object} config The config object
33853 Roo.bootstrap.LayoutMasonry = function(config){
33855 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33859 Roo.bootstrap.LayoutMasonry.register(this);
33865 * Fire after layout the items
33866 * @param {Roo.bootstrap.LayoutMasonry} this
33867 * @param {Roo.EventObject} e
33874 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33877 * @cfg {Boolean} isLayoutInstant = no animation?
33879 isLayoutInstant : false, // needed?
33882 * @cfg {Number} boxWidth width of the columns
33887 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33892 * @cfg {Number} padWidth padding below box..
33897 * @cfg {Number} gutter gutter width..
33902 * @cfg {Number} maxCols maximum number of columns
33908 * @cfg {Boolean} isAutoInitial defalut true
33910 isAutoInitial : true,
33915 * @cfg {Boolean} isHorizontal defalut false
33917 isHorizontal : false,
33919 currentSize : null,
33925 bricks: null, //CompositeElement
33929 _isLayoutInited : false,
33931 // isAlternative : false, // only use for vertical layout...
33934 * @cfg {Number} alternativePadWidth padding below box..
33936 alternativePadWidth : 50,
33938 selectedBrick : [],
33940 getAutoCreate : function(){
33942 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33946 cls: 'blog-masonary-wrapper ' + this.cls,
33948 cls : 'mas-boxes masonary'
33955 getChildContainer: function( )
33957 if (this.boxesEl) {
33958 return this.boxesEl;
33961 this.boxesEl = this.el.select('.mas-boxes').first();
33963 return this.boxesEl;
33967 initEvents : function()
33971 if(this.isAutoInitial){
33972 Roo.log('hook children rendered');
33973 this.on('childrenrendered', function() {
33974 Roo.log('children rendered');
33980 initial : function()
33982 this.selectedBrick = [];
33984 this.currentSize = this.el.getBox(true);
33986 Roo.EventManager.onWindowResize(this.resize, this);
33988 if(!this.isAutoInitial){
33996 //this.layout.defer(500,this);
34000 resize : function()
34002 var cs = this.el.getBox(true);
34005 this.currentSize.width == cs.width &&
34006 this.currentSize.x == cs.x &&
34007 this.currentSize.height == cs.height &&
34008 this.currentSize.y == cs.y
34010 Roo.log("no change in with or X or Y");
34014 this.currentSize = cs;
34020 layout : function()
34022 this._resetLayout();
34024 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34026 this.layoutItems( isInstant );
34028 this._isLayoutInited = true;
34030 this.fireEvent('layout', this);
34034 _resetLayout : function()
34036 if(this.isHorizontal){
34037 this.horizontalMeasureColumns();
34041 this.verticalMeasureColumns();
34045 verticalMeasureColumns : function()
34047 this.getContainerWidth();
34049 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34050 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34054 var boxWidth = this.boxWidth + this.padWidth;
34056 if(this.containerWidth < this.boxWidth){
34057 boxWidth = this.containerWidth
34060 var containerWidth = this.containerWidth;
34062 var cols = Math.floor(containerWidth / boxWidth);
34064 this.cols = Math.max( cols, 1 );
34066 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34068 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34070 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34072 this.colWidth = boxWidth + avail - this.padWidth;
34074 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34075 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34078 horizontalMeasureColumns : function()
34080 this.getContainerWidth();
34082 var boxWidth = this.boxWidth;
34084 if(this.containerWidth < boxWidth){
34085 boxWidth = this.containerWidth;
34088 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34090 this.el.setHeight(boxWidth);
34094 getContainerWidth : function()
34096 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34099 layoutItems : function( isInstant )
34101 Roo.log(this.bricks);
34103 var items = Roo.apply([], this.bricks);
34105 if(this.isHorizontal){
34106 this._horizontalLayoutItems( items , isInstant );
34110 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34111 // this._verticalAlternativeLayoutItems( items , isInstant );
34115 this._verticalLayoutItems( items , isInstant );
34119 _verticalLayoutItems : function ( items , isInstant)
34121 if ( !items || !items.length ) {
34126 ['xs', 'xs', 'xs', 'tall'],
34127 ['xs', 'xs', 'tall'],
34128 ['xs', 'xs', 'sm'],
34129 ['xs', 'xs', 'xs'],
34135 ['sm', 'xs', 'xs'],
34139 ['tall', 'xs', 'xs', 'xs'],
34140 ['tall', 'xs', 'xs'],
34152 Roo.each(items, function(item, k){
34154 switch (item.size) {
34155 // these layouts take up a full box,
34166 boxes.push([item]);
34189 var filterPattern = function(box, length)
34197 var pattern = box.slice(0, length);
34201 Roo.each(pattern, function(i){
34202 format.push(i.size);
34205 Roo.each(standard, function(s){
34207 if(String(s) != String(format)){
34216 if(!match && length == 1){
34221 filterPattern(box, length - 1);
34225 queue.push(pattern);
34227 box = box.slice(length, box.length);
34229 filterPattern(box, 4);
34235 Roo.each(boxes, function(box, k){
34241 if(box.length == 1){
34246 filterPattern(box, 4);
34250 this._processVerticalLayoutQueue( queue, isInstant );
34254 // _verticalAlternativeLayoutItems : function( items , isInstant )
34256 // if ( !items || !items.length ) {
34260 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34264 _horizontalLayoutItems : function ( items , isInstant)
34266 if ( !items || !items.length || items.length < 3) {
34272 var eItems = items.slice(0, 3);
34274 items = items.slice(3, items.length);
34277 ['xs', 'xs', 'xs', 'wide'],
34278 ['xs', 'xs', 'wide'],
34279 ['xs', 'xs', 'sm'],
34280 ['xs', 'xs', 'xs'],
34286 ['sm', 'xs', 'xs'],
34290 ['wide', 'xs', 'xs', 'xs'],
34291 ['wide', 'xs', 'xs'],
34304 Roo.each(items, function(item, k){
34306 switch (item.size) {
34317 boxes.push([item]);
34341 var filterPattern = function(box, length)
34349 var pattern = box.slice(0, length);
34353 Roo.each(pattern, function(i){
34354 format.push(i.size);
34357 Roo.each(standard, function(s){
34359 if(String(s) != String(format)){
34368 if(!match && length == 1){
34373 filterPattern(box, length - 1);
34377 queue.push(pattern);
34379 box = box.slice(length, box.length);
34381 filterPattern(box, 4);
34387 Roo.each(boxes, function(box, k){
34393 if(box.length == 1){
34398 filterPattern(box, 4);
34405 var pos = this.el.getBox(true);
34409 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34411 var hit_end = false;
34413 Roo.each(queue, function(box){
34417 Roo.each(box, function(b){
34419 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34429 Roo.each(box, function(b){
34431 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34434 mx = Math.max(mx, b.x);
34438 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34442 Roo.each(box, function(b){
34444 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34458 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34461 /** Sets position of item in DOM
34462 * @param {Element} item
34463 * @param {Number} x - horizontal position
34464 * @param {Number} y - vertical position
34465 * @param {Boolean} isInstant - disables transitions
34467 _processVerticalLayoutQueue : function( queue, isInstant )
34469 var pos = this.el.getBox(true);
34474 for (var i = 0; i < this.cols; i++){
34478 Roo.each(queue, function(box, k){
34480 var col = k % this.cols;
34482 Roo.each(box, function(b,kk){
34484 b.el.position('absolute');
34486 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34487 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34489 if(b.size == 'md-left' || b.size == 'md-right'){
34490 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34491 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34494 b.el.setWidth(width);
34495 b.el.setHeight(height);
34497 b.el.select('iframe',true).setSize(width,height);
34501 for (var i = 0; i < this.cols; i++){
34503 if(maxY[i] < maxY[col]){
34508 col = Math.min(col, i);
34512 x = pos.x + col * (this.colWidth + this.padWidth);
34516 var positions = [];
34518 switch (box.length){
34520 positions = this.getVerticalOneBoxColPositions(x, y, box);
34523 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34526 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34529 positions = this.getVerticalFourBoxColPositions(x, y, box);
34535 Roo.each(box, function(b,kk){
34537 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34539 var sz = b.el.getSize();
34541 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34549 for (var i = 0; i < this.cols; i++){
34550 mY = Math.max(mY, maxY[i]);
34553 this.el.setHeight(mY - pos.y);
34557 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34559 // var pos = this.el.getBox(true);
34562 // var maxX = pos.right;
34564 // var maxHeight = 0;
34566 // Roo.each(items, function(item, k){
34570 // item.el.position('absolute');
34572 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34574 // item.el.setWidth(width);
34576 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34578 // item.el.setHeight(height);
34581 // item.el.setXY([x, y], isInstant ? false : true);
34583 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34586 // y = y + height + this.alternativePadWidth;
34588 // maxHeight = maxHeight + height + this.alternativePadWidth;
34592 // this.el.setHeight(maxHeight);
34596 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34598 var pos = this.el.getBox(true);
34603 var maxX = pos.right;
34605 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34607 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34609 Roo.each(queue, function(box, k){
34611 Roo.each(box, function(b, kk){
34613 b.el.position('absolute');
34615 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34616 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34618 if(b.size == 'md-left' || b.size == 'md-right'){
34619 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34620 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34623 b.el.setWidth(width);
34624 b.el.setHeight(height);
34632 var positions = [];
34634 switch (box.length){
34636 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34639 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34642 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34645 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34651 Roo.each(box, function(b,kk){
34653 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34655 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34663 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34665 Roo.each(eItems, function(b,k){
34667 b.size = (k == 0) ? 'sm' : 'xs';
34668 b.x = (k == 0) ? 2 : 1;
34669 b.y = (k == 0) ? 2 : 1;
34671 b.el.position('absolute');
34673 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34675 b.el.setWidth(width);
34677 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34679 b.el.setHeight(height);
34683 var positions = [];
34686 x : maxX - this.unitWidth * 2 - this.gutter,
34691 x : maxX - this.unitWidth,
34692 y : minY + (this.unitWidth + this.gutter) * 2
34696 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34700 Roo.each(eItems, function(b,k){
34702 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34708 getVerticalOneBoxColPositions : function(x, y, box)
34712 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34714 if(box[0].size == 'md-left'){
34718 if(box[0].size == 'md-right'){
34723 x : x + (this.unitWidth + this.gutter) * rand,
34730 getVerticalTwoBoxColPositions : function(x, y, box)
34734 if(box[0].size == 'xs'){
34738 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34742 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34756 x : x + (this.unitWidth + this.gutter) * 2,
34757 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34764 getVerticalThreeBoxColPositions : function(x, y, box)
34768 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34776 x : x + (this.unitWidth + this.gutter) * 1,
34781 x : x + (this.unitWidth + this.gutter) * 2,
34789 if(box[0].size == 'xs' && box[1].size == 'xs'){
34798 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34802 x : x + (this.unitWidth + this.gutter) * 1,
34816 x : x + (this.unitWidth + this.gutter) * 2,
34821 x : x + (this.unitWidth + this.gutter) * 2,
34822 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34829 getVerticalFourBoxColPositions : function(x, y, box)
34833 if(box[0].size == 'xs'){
34842 y : y + (this.unitHeight + this.gutter) * 1
34847 y : y + (this.unitHeight + this.gutter) * 2
34851 x : x + (this.unitWidth + this.gutter) * 1,
34865 x : x + (this.unitWidth + this.gutter) * 2,
34870 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34871 y : y + (this.unitHeight + this.gutter) * 1
34875 x : x + (this.unitWidth + this.gutter) * 2,
34876 y : y + (this.unitWidth + this.gutter) * 2
34883 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34887 if(box[0].size == 'md-left'){
34889 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34896 if(box[0].size == 'md-right'){
34898 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34899 y : minY + (this.unitWidth + this.gutter) * 1
34905 var rand = Math.floor(Math.random() * (4 - box[0].y));
34908 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34909 y : minY + (this.unitWidth + this.gutter) * rand
34916 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34920 if(box[0].size == 'xs'){
34923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34928 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34929 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34942 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34943 y : minY + (this.unitWidth + this.gutter) * 2
34950 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34954 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963 y : minY + (this.unitWidth + this.gutter) * 1
34967 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34968 y : minY + (this.unitWidth + this.gutter) * 2
34975 if(box[0].size == 'xs' && box[1].size == 'xs'){
34978 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34988 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34989 y : minY + (this.unitWidth + this.gutter) * 1
34997 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35002 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35003 y : minY + (this.unitWidth + this.gutter) * 2
35007 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35008 y : minY + (this.unitWidth + this.gutter) * 2
35015 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35019 if(box[0].size == 'xs'){
35022 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35027 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35032 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),
35037 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35038 y : minY + (this.unitWidth + this.gutter) * 1
35046 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35051 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35052 y : minY + (this.unitWidth + this.gutter) * 2
35056 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35057 y : minY + (this.unitWidth + this.gutter) * 2
35061 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),
35062 y : minY + (this.unitWidth + this.gutter) * 2
35070 * remove a Masonry Brick
35071 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35073 removeBrick : function(brick_id)
35079 for (var i = 0; i<this.bricks.length; i++) {
35080 if (this.bricks[i].id == brick_id) {
35081 this.bricks.splice(i,1);
35082 this.el.dom.removeChild(Roo.get(brick_id).dom);
35089 * adds a Masonry Brick
35090 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35092 addBrick : function(cfg)
35094 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35095 //this.register(cn);
35096 cn.parentId = this.id;
35097 cn.render(this.el);
35102 * register a Masonry Brick
35103 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35106 register : function(brick)
35108 this.bricks.push(brick);
35109 brick.masonryId = this.id;
35113 * clear all the Masonry Brick
35115 clearAll : function()
35118 //this.getChildContainer().dom.innerHTML = "";
35119 this.el.dom.innerHTML = '';
35122 getSelected : function()
35124 if (!this.selectedBrick) {
35128 return this.selectedBrick;
35132 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35136 * register a Masonry Layout
35137 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35140 register : function(layout)
35142 this.groups[layout.id] = layout;
35145 * fetch a Masonry Layout based on the masonry layout ID
35146 * @param {string} the masonry layout to add
35147 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35150 get: function(layout_id) {
35151 if (typeof(this.groups[layout_id]) == 'undefined') {
35154 return this.groups[layout_id] ;
35166 * http://masonry.desandro.com
35168 * The idea is to render all the bricks based on vertical width...
35170 * The original code extends 'outlayer' - we might need to use that....
35176 * @class Roo.bootstrap.LayoutMasonryAuto
35177 * @extends Roo.bootstrap.Component
35178 * Bootstrap Layout Masonry class
35181 * Create a new Element
35182 * @param {Object} config The config object
35185 Roo.bootstrap.LayoutMasonryAuto = function(config){
35186 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35189 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35192 * @cfg {Boolean} isFitWidth - resize the width..
35194 isFitWidth : false, // options..
35196 * @cfg {Boolean} isOriginLeft = left align?
35198 isOriginLeft : true,
35200 * @cfg {Boolean} isOriginTop = top align?
35202 isOriginTop : false,
35204 * @cfg {Boolean} isLayoutInstant = no animation?
35206 isLayoutInstant : false, // needed?
35208 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35210 isResizingContainer : true,
35212 * @cfg {Number} columnWidth width of the columns
35218 * @cfg {Number} maxCols maximum number of columns
35223 * @cfg {Number} padHeight padding below box..
35229 * @cfg {Boolean} isAutoInitial defalut true
35232 isAutoInitial : true,
35238 initialColumnWidth : 0,
35239 currentSize : null,
35241 colYs : null, // array.
35248 bricks: null, //CompositeElement
35249 cols : 0, // array?
35250 // element : null, // wrapped now this.el
35251 _isLayoutInited : null,
35254 getAutoCreate : function(){
35258 cls: 'blog-masonary-wrapper ' + this.cls,
35260 cls : 'mas-boxes masonary'
35267 getChildContainer: function( )
35269 if (this.boxesEl) {
35270 return this.boxesEl;
35273 this.boxesEl = this.el.select('.mas-boxes').first();
35275 return this.boxesEl;
35279 initEvents : function()
35283 if(this.isAutoInitial){
35284 Roo.log('hook children rendered');
35285 this.on('childrenrendered', function() {
35286 Roo.log('children rendered');
35293 initial : function()
35295 this.reloadItems();
35297 this.currentSize = this.el.getBox(true);
35299 /// was window resize... - let's see if this works..
35300 Roo.EventManager.onWindowResize(this.resize, this);
35302 if(!this.isAutoInitial){
35307 this.layout.defer(500,this);
35310 reloadItems: function()
35312 this.bricks = this.el.select('.masonry-brick', true);
35314 this.bricks.each(function(b) {
35315 //Roo.log(b.getSize());
35316 if (!b.attr('originalwidth')) {
35317 b.attr('originalwidth', b.getSize().width);
35322 Roo.log(this.bricks.elements.length);
35325 resize : function()
35328 var cs = this.el.getBox(true);
35330 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35331 Roo.log("no change in with or X");
35334 this.currentSize = cs;
35338 layout : function()
35341 this._resetLayout();
35342 //this._manageStamps();
35344 // don't animate first layout
35345 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35346 this.layoutItems( isInstant );
35348 // flag for initalized
35349 this._isLayoutInited = true;
35352 layoutItems : function( isInstant )
35354 //var items = this._getItemsForLayout( this.items );
35355 // original code supports filtering layout items.. we just ignore it..
35357 this._layoutItems( this.bricks , isInstant );
35359 this._postLayout();
35361 _layoutItems : function ( items , isInstant)
35363 //this.fireEvent( 'layout', this, items );
35366 if ( !items || !items.elements.length ) {
35367 // no items, emit event with empty array
35372 items.each(function(item) {
35373 Roo.log("layout item");
35375 // get x/y object from method
35376 var position = this._getItemLayoutPosition( item );
35378 position.item = item;
35379 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35380 queue.push( position );
35383 this._processLayoutQueue( queue );
35385 /** Sets position of item in DOM
35386 * @param {Element} item
35387 * @param {Number} x - horizontal position
35388 * @param {Number} y - vertical position
35389 * @param {Boolean} isInstant - disables transitions
35391 _processLayoutQueue : function( queue )
35393 for ( var i=0, len = queue.length; i < len; i++ ) {
35394 var obj = queue[i];
35395 obj.item.position('absolute');
35396 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35402 * Any logic you want to do after each layout,
35403 * i.e. size the container
35405 _postLayout : function()
35407 this.resizeContainer();
35410 resizeContainer : function()
35412 if ( !this.isResizingContainer ) {
35415 var size = this._getContainerSize();
35417 this.el.setSize(size.width,size.height);
35418 this.boxesEl.setSize(size.width,size.height);
35424 _resetLayout : function()
35426 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35427 this.colWidth = this.el.getWidth();
35428 //this.gutter = this.el.getWidth();
35430 this.measureColumns();
35436 this.colYs.push( 0 );
35442 measureColumns : function()
35444 this.getContainerWidth();
35445 // if columnWidth is 0, default to outerWidth of first item
35446 if ( !this.columnWidth ) {
35447 var firstItem = this.bricks.first();
35448 Roo.log(firstItem);
35449 this.columnWidth = this.containerWidth;
35450 if (firstItem && firstItem.attr('originalwidth') ) {
35451 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35453 // columnWidth fall back to item of first element
35454 Roo.log("set column width?");
35455 this.initialColumnWidth = this.columnWidth ;
35457 // if first elem has no width, default to size of container
35462 if (this.initialColumnWidth) {
35463 this.columnWidth = this.initialColumnWidth;
35468 // column width is fixed at the top - however if container width get's smaller we should
35471 // this bit calcs how man columns..
35473 var columnWidth = this.columnWidth += this.gutter;
35475 // calculate columns
35476 var containerWidth = this.containerWidth + this.gutter;
35478 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35479 // fix rounding errors, typically with gutters
35480 var excess = columnWidth - containerWidth % columnWidth;
35483 // if overshoot is less than a pixel, round up, otherwise floor it
35484 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35485 cols = Math[ mathMethod ]( cols );
35486 this.cols = Math.max( cols, 1 );
35487 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35489 // padding positioning..
35490 var totalColWidth = this.cols * this.columnWidth;
35491 var padavail = this.containerWidth - totalColWidth;
35492 // so for 2 columns - we need 3 'pads'
35494 var padNeeded = (1+this.cols) * this.padWidth;
35496 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35498 this.columnWidth += padExtra
35499 //this.padWidth = Math.floor(padavail / ( this.cols));
35501 // adjust colum width so that padding is fixed??
35503 // we have 3 columns ... total = width * 3
35504 // we have X left over... that should be used by
35506 //if (this.expandC) {
35514 getContainerWidth : function()
35516 /* // container is parent if fit width
35517 var container = this.isFitWidth ? this.element.parentNode : this.element;
35518 // check that this.size and size are there
35519 // IE8 triggers resize on body size change, so they might not be
35521 var size = getSize( container ); //FIXME
35522 this.containerWidth = size && size.innerWidth; //FIXME
35525 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35529 _getItemLayoutPosition : function( item ) // what is item?
35531 // we resize the item to our columnWidth..
35533 item.setWidth(this.columnWidth);
35534 item.autoBoxAdjust = false;
35536 var sz = item.getSize();
35538 // how many columns does this brick span
35539 var remainder = this.containerWidth % this.columnWidth;
35541 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35542 // round if off by 1 pixel, otherwise use ceil
35543 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35544 colSpan = Math.min( colSpan, this.cols );
35546 // normally this should be '1' as we dont' currently allow multi width columns..
35548 var colGroup = this._getColGroup( colSpan );
35549 // get the minimum Y value from the columns
35550 var minimumY = Math.min.apply( Math, colGroup );
35551 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35553 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35555 // position the brick
35557 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35558 y: this.currentSize.y + minimumY + this.padHeight
35562 // apply setHeight to necessary columns
35563 var setHeight = minimumY + sz.height + this.padHeight;
35564 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35566 var setSpan = this.cols + 1 - colGroup.length;
35567 for ( var i = 0; i < setSpan; i++ ) {
35568 this.colYs[ shortColIndex + i ] = setHeight ;
35575 * @param {Number} colSpan - number of columns the element spans
35576 * @returns {Array} colGroup
35578 _getColGroup : function( colSpan )
35580 if ( colSpan < 2 ) {
35581 // if brick spans only one column, use all the column Ys
35586 // how many different places could this brick fit horizontally
35587 var groupCount = this.cols + 1 - colSpan;
35588 // for each group potential horizontal position
35589 for ( var i = 0; i < groupCount; i++ ) {
35590 // make an array of colY values for that one group
35591 var groupColYs = this.colYs.slice( i, i + colSpan );
35592 // and get the max value of the array
35593 colGroup[i] = Math.max.apply( Math, groupColYs );
35598 _manageStamp : function( stamp )
35600 var stampSize = stamp.getSize();
35601 var offset = stamp.getBox();
35602 // get the columns that this stamp affects
35603 var firstX = this.isOriginLeft ? offset.x : offset.right;
35604 var lastX = firstX + stampSize.width;
35605 var firstCol = Math.floor( firstX / this.columnWidth );
35606 firstCol = Math.max( 0, firstCol );
35608 var lastCol = Math.floor( lastX / this.columnWidth );
35609 // lastCol should not go over if multiple of columnWidth #425
35610 lastCol -= lastX % this.columnWidth ? 0 : 1;
35611 lastCol = Math.min( this.cols - 1, lastCol );
35613 // set colYs to bottom of the stamp
35614 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35617 for ( var i = firstCol; i <= lastCol; i++ ) {
35618 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35623 _getContainerSize : function()
35625 this.maxY = Math.max.apply( Math, this.colYs );
35630 if ( this.isFitWidth ) {
35631 size.width = this._getContainerFitWidth();
35637 _getContainerFitWidth : function()
35639 var unusedCols = 0;
35640 // count unused columns
35643 if ( this.colYs[i] !== 0 ) {
35648 // fit container to columns that have been used
35649 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35652 needsResizeLayout : function()
35654 var previousWidth = this.containerWidth;
35655 this.getContainerWidth();
35656 return previousWidth !== this.containerWidth;
35671 * @class Roo.bootstrap.MasonryBrick
35672 * @extends Roo.bootstrap.Component
35673 * Bootstrap MasonryBrick class
35676 * Create a new MasonryBrick
35677 * @param {Object} config The config object
35680 Roo.bootstrap.MasonryBrick = function(config){
35682 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35684 Roo.bootstrap.MasonryBrick.register(this);
35690 * When a MasonryBrick is clcik
35691 * @param {Roo.bootstrap.MasonryBrick} this
35692 * @param {Roo.EventObject} e
35698 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35701 * @cfg {String} title
35705 * @cfg {String} html
35709 * @cfg {String} bgimage
35713 * @cfg {String} videourl
35717 * @cfg {String} cls
35721 * @cfg {String} href
35725 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35730 * @cfg {String} placetitle (center|bottom)
35735 * @cfg {Boolean} isFitContainer defalut true
35737 isFitContainer : true,
35740 * @cfg {Boolean} preventDefault defalut false
35742 preventDefault : false,
35745 * @cfg {Boolean} inverse defalut false
35747 maskInverse : false,
35749 getAutoCreate : function()
35751 if(!this.isFitContainer){
35752 return this.getSplitAutoCreate();
35755 var cls = 'masonry-brick masonry-brick-full';
35757 if(this.href.length){
35758 cls += ' masonry-brick-link';
35761 if(this.bgimage.length){
35762 cls += ' masonry-brick-image';
35765 if(this.maskInverse){
35766 cls += ' mask-inverse';
35769 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35770 cls += ' enable-mask';
35774 cls += ' masonry-' + this.size + '-brick';
35777 if(this.placetitle.length){
35779 switch (this.placetitle) {
35781 cls += ' masonry-center-title';
35784 cls += ' masonry-bottom-title';
35791 if(!this.html.length && !this.bgimage.length){
35792 cls += ' masonry-center-title';
35795 if(!this.html.length && this.bgimage.length){
35796 cls += ' masonry-bottom-title';
35801 cls += ' ' + this.cls;
35805 tag: (this.href.length) ? 'a' : 'div',
35810 cls: 'masonry-brick-mask'
35814 cls: 'masonry-brick-paragraph',
35820 if(this.href.length){
35821 cfg.href = this.href;
35824 var cn = cfg.cn[1].cn;
35826 if(this.title.length){
35829 cls: 'masonry-brick-title',
35834 if(this.html.length){
35837 cls: 'masonry-brick-text',
35842 if (!this.title.length && !this.html.length) {
35843 cfg.cn[1].cls += ' hide';
35846 if(this.bgimage.length){
35849 cls: 'masonry-brick-image-view',
35854 if(this.videourl.length){
35855 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35856 // youtube support only?
35859 cls: 'masonry-brick-image-view',
35862 allowfullscreen : true
35870 getSplitAutoCreate : function()
35872 var cls = 'masonry-brick masonry-brick-split';
35874 if(this.href.length){
35875 cls += ' masonry-brick-link';
35878 if(this.bgimage.length){
35879 cls += ' masonry-brick-image';
35883 cls += ' masonry-' + this.size + '-brick';
35886 switch (this.placetitle) {
35888 cls += ' masonry-center-title';
35891 cls += ' masonry-bottom-title';
35894 if(!this.bgimage.length){
35895 cls += ' masonry-center-title';
35898 if(this.bgimage.length){
35899 cls += ' masonry-bottom-title';
35905 cls += ' ' + this.cls;
35909 tag: (this.href.length) ? 'a' : 'div',
35914 cls: 'masonry-brick-split-head',
35918 cls: 'masonry-brick-paragraph',
35925 cls: 'masonry-brick-split-body',
35931 if(this.href.length){
35932 cfg.href = this.href;
35935 if(this.title.length){
35936 cfg.cn[0].cn[0].cn.push({
35938 cls: 'masonry-brick-title',
35943 if(this.html.length){
35944 cfg.cn[1].cn.push({
35946 cls: 'masonry-brick-text',
35951 if(this.bgimage.length){
35952 cfg.cn[0].cn.push({
35954 cls: 'masonry-brick-image-view',
35959 if(this.videourl.length){
35960 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35961 // youtube support only?
35962 cfg.cn[0].cn.cn.push({
35964 cls: 'masonry-brick-image-view',
35967 allowfullscreen : true
35974 initEvents: function()
35976 switch (this.size) {
36009 this.el.on('touchstart', this.onTouchStart, this);
36010 this.el.on('touchmove', this.onTouchMove, this);
36011 this.el.on('touchend', this.onTouchEnd, this);
36012 this.el.on('contextmenu', this.onContextMenu, this);
36014 this.el.on('mouseenter' ,this.enter, this);
36015 this.el.on('mouseleave', this.leave, this);
36016 this.el.on('click', this.onClick, this);
36019 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36020 this.parent().bricks.push(this);
36025 onClick: function(e, el)
36027 var time = this.endTimer - this.startTimer;
36028 // Roo.log(e.preventDefault());
36031 e.preventDefault();
36036 if(!this.preventDefault){
36040 e.preventDefault();
36042 if (this.activeClass != '') {
36043 this.selectBrick();
36046 this.fireEvent('click', this, e);
36049 enter: function(e, el)
36051 e.preventDefault();
36053 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36057 if(this.bgimage.length && this.html.length){
36058 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36062 leave: function(e, el)
36064 e.preventDefault();
36066 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36070 if(this.bgimage.length && this.html.length){
36071 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36075 onTouchStart: function(e, el)
36077 // e.preventDefault();
36079 this.touchmoved = false;
36081 if(!this.isFitContainer){
36085 if(!this.bgimage.length || !this.html.length){
36089 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36091 this.timer = new Date().getTime();
36095 onTouchMove: function(e, el)
36097 this.touchmoved = true;
36100 onContextMenu : function(e,el)
36102 e.preventDefault();
36103 e.stopPropagation();
36107 onTouchEnd: function(e, el)
36109 // e.preventDefault();
36111 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36118 if(!this.bgimage.length || !this.html.length){
36120 if(this.href.length){
36121 window.location.href = this.href;
36127 if(!this.isFitContainer){
36131 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36133 window.location.href = this.href;
36136 //selection on single brick only
36137 selectBrick : function() {
36139 if (!this.parentId) {
36143 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36144 var index = m.selectedBrick.indexOf(this.id);
36147 m.selectedBrick.splice(index,1);
36148 this.el.removeClass(this.activeClass);
36152 for(var i = 0; i < m.selectedBrick.length; i++) {
36153 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36154 b.el.removeClass(b.activeClass);
36157 m.selectedBrick = [];
36159 m.selectedBrick.push(this.id);
36160 this.el.addClass(this.activeClass);
36164 isSelected : function(){
36165 return this.el.hasClass(this.activeClass);
36170 Roo.apply(Roo.bootstrap.MasonryBrick, {
36173 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36175 * register a Masonry Brick
36176 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36179 register : function(brick)
36181 //this.groups[brick.id] = brick;
36182 this.groups.add(brick.id, brick);
36185 * fetch a masonry brick based on the masonry brick ID
36186 * @param {string} the masonry brick to add
36187 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36190 get: function(brick_id)
36192 // if (typeof(this.groups[brick_id]) == 'undefined') {
36195 // return this.groups[brick_id] ;
36197 if(this.groups.key(brick_id)) {
36198 return this.groups.key(brick_id);
36216 * @class Roo.bootstrap.Brick
36217 * @extends Roo.bootstrap.Component
36218 * Bootstrap Brick class
36221 * Create a new Brick
36222 * @param {Object} config The config object
36225 Roo.bootstrap.Brick = function(config){
36226 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36232 * When a Brick is click
36233 * @param {Roo.bootstrap.Brick} this
36234 * @param {Roo.EventObject} e
36240 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36243 * @cfg {String} title
36247 * @cfg {String} html
36251 * @cfg {String} bgimage
36255 * @cfg {String} cls
36259 * @cfg {String} href
36263 * @cfg {String} video
36267 * @cfg {Boolean} square
36271 getAutoCreate : function()
36273 var cls = 'roo-brick';
36275 if(this.href.length){
36276 cls += ' roo-brick-link';
36279 if(this.bgimage.length){
36280 cls += ' roo-brick-image';
36283 if(!this.html.length && !this.bgimage.length){
36284 cls += ' roo-brick-center-title';
36287 if(!this.html.length && this.bgimage.length){
36288 cls += ' roo-brick-bottom-title';
36292 cls += ' ' + this.cls;
36296 tag: (this.href.length) ? 'a' : 'div',
36301 cls: 'roo-brick-paragraph',
36307 if(this.href.length){
36308 cfg.href = this.href;
36311 var cn = cfg.cn[0].cn;
36313 if(this.title.length){
36316 cls: 'roo-brick-title',
36321 if(this.html.length){
36324 cls: 'roo-brick-text',
36331 if(this.bgimage.length){
36334 cls: 'roo-brick-image-view',
36342 initEvents: function()
36344 if(this.title.length || this.html.length){
36345 this.el.on('mouseenter' ,this.enter, this);
36346 this.el.on('mouseleave', this.leave, this);
36349 Roo.EventManager.onWindowResize(this.resize, this);
36351 if(this.bgimage.length){
36352 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36353 this.imageEl.on('load', this.onImageLoad, this);
36360 onImageLoad : function()
36365 resize : function()
36367 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36369 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36371 if(this.bgimage.length){
36372 var image = this.el.select('.roo-brick-image-view', true).first();
36374 image.setWidth(paragraph.getWidth());
36377 image.setHeight(paragraph.getWidth());
36380 this.el.setHeight(image.getHeight());
36381 paragraph.setHeight(image.getHeight());
36387 enter: function(e, el)
36389 e.preventDefault();
36391 if(this.bgimage.length){
36392 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36393 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36397 leave: function(e, el)
36399 e.preventDefault();
36401 if(this.bgimage.length){
36402 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36403 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36418 * @class Roo.bootstrap.NumberField
36419 * @extends Roo.bootstrap.Input
36420 * Bootstrap NumberField class
36426 * Create a new NumberField
36427 * @param {Object} config The config object
36430 Roo.bootstrap.NumberField = function(config){
36431 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36434 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36437 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36439 allowDecimals : true,
36441 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36443 decimalSeparator : ".",
36445 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36447 decimalPrecision : 2,
36449 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36451 allowNegative : true,
36454 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36458 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36460 minValue : Number.NEGATIVE_INFINITY,
36462 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36464 maxValue : Number.MAX_VALUE,
36466 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36468 minText : "The minimum value for this field is {0}",
36470 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36472 maxText : "The maximum value for this field is {0}",
36474 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36475 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36477 nanText : "{0} is not a valid number",
36479 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36481 thousandsDelimiter : false,
36483 * @cfg {String} valueAlign alignment of value
36485 valueAlign : "left",
36487 getAutoCreate : function()
36489 var hiddenInput = {
36493 cls: 'hidden-number-input'
36497 hiddenInput.name = this.name;
36502 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36504 this.name = hiddenInput.name;
36506 if(cfg.cn.length > 0) {
36507 cfg.cn.push(hiddenInput);
36514 initEvents : function()
36516 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36518 var allowed = "0123456789";
36520 if(this.allowDecimals){
36521 allowed += this.decimalSeparator;
36524 if(this.allowNegative){
36528 if(this.thousandsDelimiter) {
36532 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36534 var keyPress = function(e){
36536 var k = e.getKey();
36538 var c = e.getCharCode();
36541 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36542 allowed.indexOf(String.fromCharCode(c)) === -1
36548 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36552 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36557 this.el.on("keypress", keyPress, this);
36560 validateValue : function(value)
36563 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36567 var num = this.parseValue(value);
36570 this.markInvalid(String.format(this.nanText, value));
36574 if(num < this.minValue){
36575 this.markInvalid(String.format(this.minText, this.minValue));
36579 if(num > this.maxValue){
36580 this.markInvalid(String.format(this.maxText, this.maxValue));
36587 getValue : function()
36589 var v = this.hiddenEl().getValue();
36591 return this.fixPrecision(this.parseValue(v));
36594 parseValue : function(value)
36596 if(this.thousandsDelimiter) {
36598 r = new RegExp(",", "g");
36599 value = value.replace(r, "");
36602 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36603 return isNaN(value) ? '' : value;
36606 fixPrecision : function(value)
36608 if(this.thousandsDelimiter) {
36610 r = new RegExp(",", "g");
36611 value = value.replace(r, "");
36614 var nan = isNaN(value);
36616 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36617 return nan ? '' : value;
36619 return parseFloat(value).toFixed(this.decimalPrecision);
36622 setValue : function(v)
36624 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36630 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36632 this.inputEl().dom.value = (v == '') ? '' :
36633 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36635 if(!this.allowZero && v === '0') {
36636 this.hiddenEl().dom.value = '';
36637 this.inputEl().dom.value = '';
36644 decimalPrecisionFcn : function(v)
36646 return Math.floor(v);
36649 beforeBlur : function()
36651 var v = this.parseValue(this.getRawValue());
36653 if(v || v === 0 || v === ''){
36658 hiddenEl : function()
36660 return this.el.select('input.hidden-number-input',true).first();
36672 * @class Roo.bootstrap.DocumentSlider
36673 * @extends Roo.bootstrap.Component
36674 * Bootstrap DocumentSlider class
36677 * Create a new DocumentViewer
36678 * @param {Object} config The config object
36681 Roo.bootstrap.DocumentSlider = function(config){
36682 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36689 * Fire after initEvent
36690 * @param {Roo.bootstrap.DocumentSlider} this
36695 * Fire after update
36696 * @param {Roo.bootstrap.DocumentSlider} this
36702 * @param {Roo.bootstrap.DocumentSlider} this
36708 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36714 getAutoCreate : function()
36718 cls : 'roo-document-slider',
36722 cls : 'roo-document-slider-header',
36726 cls : 'roo-document-slider-header-title'
36732 cls : 'roo-document-slider-body',
36736 cls : 'roo-document-slider-prev',
36740 cls : 'fa fa-chevron-left'
36746 cls : 'roo-document-slider-thumb',
36750 cls : 'roo-document-slider-image'
36756 cls : 'roo-document-slider-next',
36760 cls : 'fa fa-chevron-right'
36772 initEvents : function()
36774 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36775 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36777 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36778 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36780 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36781 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36783 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36784 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36786 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36787 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36789 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36790 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36792 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36793 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36795 this.thumbEl.on('click', this.onClick, this);
36797 this.prevIndicator.on('click', this.prev, this);
36799 this.nextIndicator.on('click', this.next, this);
36803 initial : function()
36805 if(this.files.length){
36806 this.indicator = 1;
36810 this.fireEvent('initial', this);
36813 update : function()
36815 this.imageEl.attr('src', this.files[this.indicator - 1]);
36817 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36819 this.prevIndicator.show();
36821 if(this.indicator == 1){
36822 this.prevIndicator.hide();
36825 this.nextIndicator.show();
36827 if(this.indicator == this.files.length){
36828 this.nextIndicator.hide();
36831 this.thumbEl.scrollTo('top');
36833 this.fireEvent('update', this);
36836 onClick : function(e)
36838 e.preventDefault();
36840 this.fireEvent('click', this);
36845 e.preventDefault();
36847 this.indicator = Math.max(1, this.indicator - 1);
36854 e.preventDefault();
36856 this.indicator = Math.min(this.files.length, this.indicator + 1);
36870 * @class Roo.bootstrap.RadioSet
36871 * @extends Roo.bootstrap.Input
36872 * Bootstrap RadioSet class
36873 * @cfg {String} indicatorpos (left|right) default left
36874 * @cfg {Boolean} inline (true|false) inline the element (default true)
36875 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36877 * Create a new RadioSet
36878 * @param {Object} config The config object
36881 Roo.bootstrap.RadioSet = function(config){
36883 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36887 Roo.bootstrap.RadioSet.register(this);
36892 * Fires when the element is checked or unchecked.
36893 * @param {Roo.bootstrap.RadioSet} this This radio
36894 * @param {Roo.bootstrap.Radio} item The checked item
36899 * Fires when the element is click.
36900 * @param {Roo.bootstrap.RadioSet} this This radio set
36901 * @param {Roo.bootstrap.Radio} item The checked item
36902 * @param {Roo.EventObject} e The event object
36909 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36917 indicatorpos : 'left',
36919 getAutoCreate : function()
36923 cls : 'roo-radio-set-label',
36927 html : this.fieldLabel
36931 if (Roo.bootstrap.version == 3) {
36934 if(this.indicatorpos == 'left'){
36937 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36938 tooltip : 'This field is required'
36943 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36944 tooltip : 'This field is required'
36950 cls : 'roo-radio-set-items'
36953 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36955 if (align === 'left' && this.fieldLabel.length) {
36958 cls : "roo-radio-set-right",
36964 if(this.labelWidth > 12){
36965 label.style = "width: " + this.labelWidth + 'px';
36968 if(this.labelWidth < 13 && this.labelmd == 0){
36969 this.labelmd = this.labelWidth;
36972 if(this.labellg > 0){
36973 label.cls += ' col-lg-' + this.labellg;
36974 items.cls += ' col-lg-' + (12 - this.labellg);
36977 if(this.labelmd > 0){
36978 label.cls += ' col-md-' + this.labelmd;
36979 items.cls += ' col-md-' + (12 - this.labelmd);
36982 if(this.labelsm > 0){
36983 label.cls += ' col-sm-' + this.labelsm;
36984 items.cls += ' col-sm-' + (12 - this.labelsm);
36987 if(this.labelxs > 0){
36988 label.cls += ' col-xs-' + this.labelxs;
36989 items.cls += ' col-xs-' + (12 - this.labelxs);
36995 cls : 'roo-radio-set',
36999 cls : 'roo-radio-set-input',
37002 value : this.value ? this.value : ''
37009 if(this.weight.length){
37010 cfg.cls += ' roo-radio-' + this.weight;
37014 cfg.cls += ' roo-radio-set-inline';
37018 ['xs','sm','md','lg'].map(function(size){
37019 if (settings[size]) {
37020 cfg.cls += ' col-' + size + '-' + settings[size];
37028 initEvents : function()
37030 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37031 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37033 if(!this.fieldLabel.length){
37034 this.labelEl.hide();
37037 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37038 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37040 this.indicator = this.indicatorEl();
37042 if(this.indicator){
37043 this.indicator.addClass('invisible');
37046 this.originalValue = this.getValue();
37050 inputEl: function ()
37052 return this.el.select('.roo-radio-set-input', true).first();
37055 getChildContainer : function()
37057 return this.itemsEl;
37060 register : function(item)
37062 this.radioes.push(item);
37066 validate : function()
37068 if(this.getVisibilityEl().hasClass('hidden')){
37074 Roo.each(this.radioes, function(i){
37083 if(this.allowBlank) {
37087 if(this.disabled || valid){
37092 this.markInvalid();
37097 markValid : function()
37099 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37100 this.indicatorEl().removeClass('visible');
37101 this.indicatorEl().addClass('invisible');
37105 if (Roo.bootstrap.version == 3) {
37106 this.el.removeClass([this.invalidClass, this.validClass]);
37107 this.el.addClass(this.validClass);
37109 this.el.removeClass(['is-invalid','is-valid']);
37110 this.el.addClass(['is-valid']);
37112 this.fireEvent('valid', this);
37115 markInvalid : function(msg)
37117 if(this.allowBlank || this.disabled){
37121 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37122 this.indicatorEl().removeClass('invisible');
37123 this.indicatorEl().addClass('visible');
37125 if (Roo.bootstrap.version == 3) {
37126 this.el.removeClass([this.invalidClass, this.validClass]);
37127 this.el.addClass(this.invalidClass);
37129 this.el.removeClass(['is-invalid','is-valid']);
37130 this.el.addClass(['is-invalid']);
37133 this.fireEvent('invalid', this, msg);
37137 setValue : function(v, suppressEvent)
37139 if(this.value === v){
37146 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37149 Roo.each(this.radioes, function(i){
37151 i.el.removeClass('checked');
37154 Roo.each(this.radioes, function(i){
37156 if(i.value === v || i.value.toString() === v.toString()){
37158 i.el.addClass('checked');
37160 if(suppressEvent !== true){
37161 this.fireEvent('check', this, i);
37172 clearInvalid : function(){
37174 if(!this.el || this.preventMark){
37178 this.el.removeClass([this.invalidClass]);
37180 this.fireEvent('valid', this);
37185 Roo.apply(Roo.bootstrap.RadioSet, {
37189 register : function(set)
37191 this.groups[set.name] = set;
37194 get: function(name)
37196 if (typeof(this.groups[name]) == 'undefined') {
37200 return this.groups[name] ;
37206 * Ext JS Library 1.1.1
37207 * Copyright(c) 2006-2007, Ext JS, LLC.
37209 * Originally Released Under LGPL - original licence link has changed is not relivant.
37212 * <script type="text/javascript">
37217 * @class Roo.bootstrap.SplitBar
37218 * @extends Roo.util.Observable
37219 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37223 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37224 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37225 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37226 split.minSize = 100;
37227 split.maxSize = 600;
37228 split.animate = true;
37229 split.on('moved', splitterMoved);
37232 * Create a new SplitBar
37233 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37234 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37235 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37236 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37237 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37238 position of the SplitBar).
37240 Roo.bootstrap.SplitBar = function(cfg){
37245 // dragElement : elm
37246 // resizingElement: el,
37248 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37249 // placement : Roo.bootstrap.SplitBar.LEFT ,
37250 // existingProxy ???
37253 this.el = Roo.get(cfg.dragElement, true);
37254 this.el.dom.unselectable = "on";
37256 this.resizingEl = Roo.get(cfg.resizingElement, true);
37260 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37261 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37264 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37267 * The minimum size of the resizing element. (Defaults to 0)
37273 * The maximum size of the resizing element. (Defaults to 2000)
37276 this.maxSize = 2000;
37279 * Whether to animate the transition to the new size
37282 this.animate = false;
37285 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37288 this.useShim = false;
37293 if(!cfg.existingProxy){
37295 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37297 this.proxy = Roo.get(cfg.existingProxy).dom;
37300 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37303 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37306 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37309 this.dragSpecs = {};
37312 * @private The adapter to use to positon and resize elements
37314 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37315 this.adapter.init(this);
37317 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37319 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37320 this.el.addClass("roo-splitbar-h");
37323 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37324 this.el.addClass("roo-splitbar-v");
37330 * Fires when the splitter is moved (alias for {@link #event-moved})
37331 * @param {Roo.bootstrap.SplitBar} this
37332 * @param {Number} newSize the new width or height
37337 * Fires when the splitter is moved
37338 * @param {Roo.bootstrap.SplitBar} this
37339 * @param {Number} newSize the new width or height
37343 * @event beforeresize
37344 * Fires before the splitter is dragged
37345 * @param {Roo.bootstrap.SplitBar} this
37347 "beforeresize" : true,
37349 "beforeapply" : true
37352 Roo.util.Observable.call(this);
37355 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37356 onStartProxyDrag : function(x, y){
37357 this.fireEvent("beforeresize", this);
37359 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37361 o.enableDisplayMode("block");
37362 // all splitbars share the same overlay
37363 Roo.bootstrap.SplitBar.prototype.overlay = o;
37365 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37366 this.overlay.show();
37367 Roo.get(this.proxy).setDisplayed("block");
37368 var size = this.adapter.getElementSize(this);
37369 this.activeMinSize = this.getMinimumSize();;
37370 this.activeMaxSize = this.getMaximumSize();;
37371 var c1 = size - this.activeMinSize;
37372 var c2 = Math.max(this.activeMaxSize - size, 0);
37373 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37374 this.dd.resetConstraints();
37375 this.dd.setXConstraint(
37376 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37377 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37379 this.dd.setYConstraint(0, 0);
37381 this.dd.resetConstraints();
37382 this.dd.setXConstraint(0, 0);
37383 this.dd.setYConstraint(
37384 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37385 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37388 this.dragSpecs.startSize = size;
37389 this.dragSpecs.startPoint = [x, y];
37390 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37394 * @private Called after the drag operation by the DDProxy
37396 onEndProxyDrag : function(e){
37397 Roo.get(this.proxy).setDisplayed(false);
37398 var endPoint = Roo.lib.Event.getXY(e);
37400 this.overlay.hide();
37403 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37404 newSize = this.dragSpecs.startSize +
37405 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37406 endPoint[0] - this.dragSpecs.startPoint[0] :
37407 this.dragSpecs.startPoint[0] - endPoint[0]
37410 newSize = this.dragSpecs.startSize +
37411 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37412 endPoint[1] - this.dragSpecs.startPoint[1] :
37413 this.dragSpecs.startPoint[1] - endPoint[1]
37416 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37417 if(newSize != this.dragSpecs.startSize){
37418 if(this.fireEvent('beforeapply', this, newSize) !== false){
37419 this.adapter.setElementSize(this, newSize);
37420 this.fireEvent("moved", this, newSize);
37421 this.fireEvent("resize", this, newSize);
37427 * Get the adapter this SplitBar uses
37428 * @return The adapter object
37430 getAdapter : function(){
37431 return this.adapter;
37435 * Set the adapter this SplitBar uses
37436 * @param {Object} adapter A SplitBar adapter object
37438 setAdapter : function(adapter){
37439 this.adapter = adapter;
37440 this.adapter.init(this);
37444 * Gets the minimum size for the resizing element
37445 * @return {Number} The minimum size
37447 getMinimumSize : function(){
37448 return this.minSize;
37452 * Sets the minimum size for the resizing element
37453 * @param {Number} minSize The minimum size
37455 setMinimumSize : function(minSize){
37456 this.minSize = minSize;
37460 * Gets the maximum size for the resizing element
37461 * @return {Number} The maximum size
37463 getMaximumSize : function(){
37464 return this.maxSize;
37468 * Sets the maximum size for the resizing element
37469 * @param {Number} maxSize The maximum size
37471 setMaximumSize : function(maxSize){
37472 this.maxSize = maxSize;
37476 * Sets the initialize size for the resizing element
37477 * @param {Number} size The initial size
37479 setCurrentSize : function(size){
37480 var oldAnimate = this.animate;
37481 this.animate = false;
37482 this.adapter.setElementSize(this, size);
37483 this.animate = oldAnimate;
37487 * Destroy this splitbar.
37488 * @param {Boolean} removeEl True to remove the element
37490 destroy : function(removeEl){
37492 this.shim.remove();
37495 this.proxy.parentNode.removeChild(this.proxy);
37503 * @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.
37505 Roo.bootstrap.SplitBar.createProxy = function(dir){
37506 var proxy = new Roo.Element(document.createElement("div"));
37507 proxy.unselectable();
37508 var cls = 'roo-splitbar-proxy';
37509 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37510 document.body.appendChild(proxy.dom);
37515 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37516 * Default Adapter. It assumes the splitter and resizing element are not positioned
37517 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37519 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37522 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37523 // do nothing for now
37524 init : function(s){
37528 * Called before drag operations to get the current size of the resizing element.
37529 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37531 getElementSize : function(s){
37532 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37533 return s.resizingEl.getWidth();
37535 return s.resizingEl.getHeight();
37540 * Called after drag operations to set the size of the resizing element.
37541 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37542 * @param {Number} newSize The new size to set
37543 * @param {Function} onComplete A function to be invoked when resizing is complete
37545 setElementSize : function(s, newSize, onComplete){
37546 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37548 s.resizingEl.setWidth(newSize);
37550 onComplete(s, newSize);
37553 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37558 s.resizingEl.setHeight(newSize);
37560 onComplete(s, newSize);
37563 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37570 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37571 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37572 * Adapter that moves the splitter element to align with the resized sizing element.
37573 * Used with an absolute positioned SplitBar.
37574 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37575 * document.body, make sure you assign an id to the body element.
37577 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37578 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37579 this.container = Roo.get(container);
37582 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37583 init : function(s){
37584 this.basic.init(s);
37587 getElementSize : function(s){
37588 return this.basic.getElementSize(s);
37591 setElementSize : function(s, newSize, onComplete){
37592 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37595 moveSplitter : function(s){
37596 var yes = Roo.bootstrap.SplitBar;
37597 switch(s.placement){
37599 s.el.setX(s.resizingEl.getRight());
37602 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37605 s.el.setY(s.resizingEl.getBottom());
37608 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37615 * Orientation constant - Create a vertical SplitBar
37619 Roo.bootstrap.SplitBar.VERTICAL = 1;
37622 * Orientation constant - Create a horizontal SplitBar
37626 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37629 * Placement constant - The resizing element is to the left of the splitter element
37633 Roo.bootstrap.SplitBar.LEFT = 1;
37636 * Placement constant - The resizing element is to the right of the splitter element
37640 Roo.bootstrap.SplitBar.RIGHT = 2;
37643 * Placement constant - The resizing element is positioned above the splitter element
37647 Roo.bootstrap.SplitBar.TOP = 3;
37650 * Placement constant - The resizing element is positioned under splitter element
37654 Roo.bootstrap.SplitBar.BOTTOM = 4;
37655 Roo.namespace("Roo.bootstrap.layout");/*
37657 * Ext JS Library 1.1.1
37658 * Copyright(c) 2006-2007, Ext JS, LLC.
37660 * Originally Released Under LGPL - original licence link has changed is not relivant.
37663 * <script type="text/javascript">
37667 * @class Roo.bootstrap.layout.Manager
37668 * @extends Roo.bootstrap.Component
37669 * Base class for layout managers.
37671 Roo.bootstrap.layout.Manager = function(config)
37673 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37679 /** false to disable window resize monitoring @type Boolean */
37680 this.monitorWindowResize = true;
37685 * Fires when a layout is performed.
37686 * @param {Roo.LayoutManager} this
37690 * @event regionresized
37691 * Fires when the user resizes a region.
37692 * @param {Roo.LayoutRegion} region The resized region
37693 * @param {Number} newSize The new size (width for east/west, height for north/south)
37695 "regionresized" : true,
37697 * @event regioncollapsed
37698 * Fires when a region is collapsed.
37699 * @param {Roo.LayoutRegion} region The collapsed region
37701 "regioncollapsed" : true,
37703 * @event regionexpanded
37704 * Fires when a region is expanded.
37705 * @param {Roo.LayoutRegion} region The expanded region
37707 "regionexpanded" : true
37709 this.updating = false;
37712 this.el = Roo.get(config.el);
37718 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37723 monitorWindowResize : true,
37729 onRender : function(ct, position)
37732 this.el = Roo.get(ct);
37735 //this.fireEvent('render',this);
37739 initEvents: function()
37743 // ie scrollbar fix
37744 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37745 document.body.scroll = "no";
37746 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37747 this.el.position('relative');
37749 this.id = this.el.id;
37750 this.el.addClass("roo-layout-container");
37751 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37752 if(this.el.dom != document.body ) {
37753 this.el.on('resize', this.layout,this);
37754 this.el.on('show', this.layout,this);
37760 * Returns true if this layout is currently being updated
37761 * @return {Boolean}
37763 isUpdating : function(){
37764 return this.updating;
37768 * Suspend the LayoutManager from doing auto-layouts while
37769 * making multiple add or remove calls
37771 beginUpdate : function(){
37772 this.updating = true;
37776 * Restore auto-layouts and optionally disable the manager from performing a layout
37777 * @param {Boolean} noLayout true to disable a layout update
37779 endUpdate : function(noLayout){
37780 this.updating = false;
37786 layout: function(){
37790 onRegionResized : function(region, newSize){
37791 this.fireEvent("regionresized", region, newSize);
37795 onRegionCollapsed : function(region){
37796 this.fireEvent("regioncollapsed", region);
37799 onRegionExpanded : function(region){
37800 this.fireEvent("regionexpanded", region);
37804 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37805 * performs box-model adjustments.
37806 * @return {Object} The size as an object {width: (the width), height: (the height)}
37808 getViewSize : function()
37811 if(this.el.dom != document.body){
37812 size = this.el.getSize();
37814 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37816 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37817 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37822 * Returns the Element this layout is bound to.
37823 * @return {Roo.Element}
37825 getEl : function(){
37830 * Returns the specified region.
37831 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37832 * @return {Roo.LayoutRegion}
37834 getRegion : function(target){
37835 return this.regions[target.toLowerCase()];
37838 onWindowResize : function(){
37839 if(this.monitorWindowResize){
37846 * Ext JS Library 1.1.1
37847 * Copyright(c) 2006-2007, Ext JS, LLC.
37849 * Originally Released Under LGPL - original licence link has changed is not relivant.
37852 * <script type="text/javascript">
37855 * @class Roo.bootstrap.layout.Border
37856 * @extends Roo.bootstrap.layout.Manager
37857 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37858 * please see: examples/bootstrap/nested.html<br><br>
37860 <b>The container the layout is rendered into can be either the body element or any other element.
37861 If it is not the body element, the container needs to either be an absolute positioned element,
37862 or you will need to add "position:relative" to the css of the container. You will also need to specify
37863 the container size if it is not the body element.</b>
37866 * Create a new Border
37867 * @param {Object} config Configuration options
37869 Roo.bootstrap.layout.Border = function(config){
37870 config = config || {};
37871 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37875 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37876 if(config[region]){
37877 config[region].region = region;
37878 this.addRegion(config[region]);
37884 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37886 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37888 parent : false, // this might point to a 'nest' or a ???
37891 * Creates and adds a new region if it doesn't already exist.
37892 * @param {String} target The target region key (north, south, east, west or center).
37893 * @param {Object} config The regions config object
37894 * @return {BorderLayoutRegion} The new region
37896 addRegion : function(config)
37898 if(!this.regions[config.region]){
37899 var r = this.factory(config);
37900 this.bindRegion(r);
37902 return this.regions[config.region];
37906 bindRegion : function(r){
37907 this.regions[r.config.region] = r;
37909 r.on("visibilitychange", this.layout, this);
37910 r.on("paneladded", this.layout, this);
37911 r.on("panelremoved", this.layout, this);
37912 r.on("invalidated", this.layout, this);
37913 r.on("resized", this.onRegionResized, this);
37914 r.on("collapsed", this.onRegionCollapsed, this);
37915 r.on("expanded", this.onRegionExpanded, this);
37919 * Performs a layout update.
37921 layout : function()
37923 if(this.updating) {
37927 // render all the rebions if they have not been done alreayd?
37928 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37929 if(this.regions[region] && !this.regions[region].bodyEl){
37930 this.regions[region].onRender(this.el)
37934 var size = this.getViewSize();
37935 var w = size.width;
37936 var h = size.height;
37941 //var x = 0, y = 0;
37943 var rs = this.regions;
37944 var north = rs["north"];
37945 var south = rs["south"];
37946 var west = rs["west"];
37947 var east = rs["east"];
37948 var center = rs["center"];
37949 //if(this.hideOnLayout){ // not supported anymore
37950 //c.el.setStyle("display", "none");
37952 if(north && north.isVisible()){
37953 var b = north.getBox();
37954 var m = north.getMargins();
37955 b.width = w - (m.left+m.right);
37958 centerY = b.height + b.y + m.bottom;
37959 centerH -= centerY;
37960 north.updateBox(this.safeBox(b));
37962 if(south && south.isVisible()){
37963 var b = south.getBox();
37964 var m = south.getMargins();
37965 b.width = w - (m.left+m.right);
37967 var totalHeight = (b.height + m.top + m.bottom);
37968 b.y = h - totalHeight + m.top;
37969 centerH -= totalHeight;
37970 south.updateBox(this.safeBox(b));
37972 if(west && west.isVisible()){
37973 var b = west.getBox();
37974 var m = west.getMargins();
37975 b.height = centerH - (m.top+m.bottom);
37977 b.y = centerY + m.top;
37978 var totalWidth = (b.width + m.left + m.right);
37979 centerX += totalWidth;
37980 centerW -= totalWidth;
37981 west.updateBox(this.safeBox(b));
37983 if(east && east.isVisible()){
37984 var b = east.getBox();
37985 var m = east.getMargins();
37986 b.height = centerH - (m.top+m.bottom);
37987 var totalWidth = (b.width + m.left + m.right);
37988 b.x = w - totalWidth + m.left;
37989 b.y = centerY + m.top;
37990 centerW -= totalWidth;
37991 east.updateBox(this.safeBox(b));
37994 var m = center.getMargins();
37996 x: centerX + m.left,
37997 y: centerY + m.top,
37998 width: centerW - (m.left+m.right),
37999 height: centerH - (m.top+m.bottom)
38001 //if(this.hideOnLayout){
38002 //center.el.setStyle("display", "block");
38004 center.updateBox(this.safeBox(centerBox));
38007 this.fireEvent("layout", this);
38011 safeBox : function(box){
38012 box.width = Math.max(0, box.width);
38013 box.height = Math.max(0, box.height);
38018 * Adds a ContentPanel (or subclass) to this layout.
38019 * @param {String} target The target region key (north, south, east, west or center).
38020 * @param {Roo.ContentPanel} panel The panel to add
38021 * @return {Roo.ContentPanel} The added panel
38023 add : function(target, panel){
38025 target = target.toLowerCase();
38026 return this.regions[target].add(panel);
38030 * Remove a ContentPanel (or subclass) to this layout.
38031 * @param {String} target The target region key (north, south, east, west or center).
38032 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38033 * @return {Roo.ContentPanel} The removed panel
38035 remove : function(target, panel){
38036 target = target.toLowerCase();
38037 return this.regions[target].remove(panel);
38041 * Searches all regions for a panel with the specified id
38042 * @param {String} panelId
38043 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38045 findPanel : function(panelId){
38046 var rs = this.regions;
38047 for(var target in rs){
38048 if(typeof rs[target] != "function"){
38049 var p = rs[target].getPanel(panelId);
38059 * Searches all regions for a panel with the specified id and activates (shows) it.
38060 * @param {String/ContentPanel} panelId The panels id or the panel itself
38061 * @return {Roo.ContentPanel} The shown panel or null
38063 showPanel : function(panelId) {
38064 var rs = this.regions;
38065 for(var target in rs){
38066 var r = rs[target];
38067 if(typeof r != "function"){
38068 if(r.hasPanel(panelId)){
38069 return r.showPanel(panelId);
38077 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38078 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38081 restoreState : function(provider){
38083 provider = Roo.state.Manager;
38085 var sm = new Roo.LayoutStateManager();
38086 sm.init(this, provider);
38092 * Adds a xtype elements to the layout.
38096 xtype : 'ContentPanel',
38103 xtype : 'NestedLayoutPanel',
38109 items : [ ... list of content panels or nested layout panels.. ]
38113 * @param {Object} cfg Xtype definition of item to add.
38115 addxtype : function(cfg)
38117 // basically accepts a pannel...
38118 // can accept a layout region..!?!?
38119 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38122 // theory? children can only be panels??
38124 //if (!cfg.xtype.match(/Panel$/)) {
38129 if (typeof(cfg.region) == 'undefined') {
38130 Roo.log("Failed to add Panel, region was not set");
38134 var region = cfg.region;
38140 xitems = cfg.items;
38145 if ( region == 'center') {
38146 Roo.log("Center: " + cfg.title);
38152 case 'Content': // ContentPanel (el, cfg)
38153 case 'Scroll': // ContentPanel (el, cfg)
38155 cfg.autoCreate = cfg.autoCreate || true;
38156 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38158 // var el = this.el.createChild();
38159 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38162 this.add(region, ret);
38166 case 'TreePanel': // our new panel!
38167 cfg.el = this.el.createChild();
38168 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38169 this.add(region, ret);
38174 // create a new Layout (which is a Border Layout...
38176 var clayout = cfg.layout;
38177 clayout.el = this.el.createChild();
38178 clayout.items = clayout.items || [];
38182 // replace this exitems with the clayout ones..
38183 xitems = clayout.items;
38185 // force background off if it's in center...
38186 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38187 cfg.background = false;
38189 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38192 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38193 //console.log('adding nested layout panel ' + cfg.toSource());
38194 this.add(region, ret);
38195 nb = {}; /// find first...
38200 // needs grid and region
38202 //var el = this.getRegion(region).el.createChild();
38204 *var el = this.el.createChild();
38205 // create the grid first...
38206 cfg.grid.container = el;
38207 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38210 if (region == 'center' && this.active ) {
38211 cfg.background = false;
38214 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38216 this.add(region, ret);
38218 if (cfg.background) {
38219 // render grid on panel activation (if panel background)
38220 ret.on('activate', function(gp) {
38221 if (!gp.grid.rendered) {
38222 // gp.grid.render(el);
38226 // cfg.grid.render(el);
38232 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38233 // it was the old xcomponent building that caused this before.
38234 // espeically if border is the top element in the tree.
38244 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38246 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38247 this.add(region, ret);
38251 throw "Can not add '" + cfg.xtype + "' to Border";
38257 this.beginUpdate();
38261 Roo.each(xitems, function(i) {
38262 region = nb && i.region ? i.region : false;
38264 var add = ret.addxtype(i);
38267 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38268 if (!i.background) {
38269 abn[region] = nb[region] ;
38276 // make the last non-background panel active..
38277 //if (nb) { Roo.log(abn); }
38280 for(var r in abn) {
38281 region = this.getRegion(r);
38283 // tried using nb[r], but it does not work..
38285 region.showPanel(abn[r]);
38296 factory : function(cfg)
38299 var validRegions = Roo.bootstrap.layout.Border.regions;
38301 var target = cfg.region;
38304 var r = Roo.bootstrap.layout;
38308 return new r.North(cfg);
38310 return new r.South(cfg);
38312 return new r.East(cfg);
38314 return new r.West(cfg);
38316 return new r.Center(cfg);
38318 throw 'Layout region "'+target+'" not supported.';
38325 * Ext JS Library 1.1.1
38326 * Copyright(c) 2006-2007, Ext JS, LLC.
38328 * Originally Released Under LGPL - original licence link has changed is not relivant.
38331 * <script type="text/javascript">
38335 * @class Roo.bootstrap.layout.Basic
38336 * @extends Roo.util.Observable
38337 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38338 * and does not have a titlebar, tabs or any other features. All it does is size and position
38339 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38340 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38341 * @cfg {string} region the region that it inhabits..
38342 * @cfg {bool} skipConfig skip config?
38346 Roo.bootstrap.layout.Basic = function(config){
38348 this.mgr = config.mgr;
38350 this.position = config.region;
38352 var skipConfig = config.skipConfig;
38356 * @scope Roo.BasicLayoutRegion
38360 * @event beforeremove
38361 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38362 * @param {Roo.LayoutRegion} this
38363 * @param {Roo.ContentPanel} panel The panel
38364 * @param {Object} e The cancel event object
38366 "beforeremove" : true,
38368 * @event invalidated
38369 * Fires when the layout for this region is changed.
38370 * @param {Roo.LayoutRegion} this
38372 "invalidated" : true,
38374 * @event visibilitychange
38375 * Fires when this region is shown or hidden
38376 * @param {Roo.LayoutRegion} this
38377 * @param {Boolean} visibility true or false
38379 "visibilitychange" : true,
38381 * @event paneladded
38382 * Fires when a panel is added.
38383 * @param {Roo.LayoutRegion} this
38384 * @param {Roo.ContentPanel} panel The panel
38386 "paneladded" : true,
38388 * @event panelremoved
38389 * Fires when a panel is removed.
38390 * @param {Roo.LayoutRegion} this
38391 * @param {Roo.ContentPanel} panel The panel
38393 "panelremoved" : true,
38395 * @event beforecollapse
38396 * Fires when this region before collapse.
38397 * @param {Roo.LayoutRegion} this
38399 "beforecollapse" : true,
38402 * Fires when this region is collapsed.
38403 * @param {Roo.LayoutRegion} this
38405 "collapsed" : true,
38408 * Fires when this region is expanded.
38409 * @param {Roo.LayoutRegion} this
38414 * Fires when this region is slid into view.
38415 * @param {Roo.LayoutRegion} this
38417 "slideshow" : true,
38420 * Fires when this region slides out of view.
38421 * @param {Roo.LayoutRegion} this
38423 "slidehide" : true,
38425 * @event panelactivated
38426 * Fires when a panel is activated.
38427 * @param {Roo.LayoutRegion} this
38428 * @param {Roo.ContentPanel} panel The activated panel
38430 "panelactivated" : true,
38433 * Fires when the user resizes this region.
38434 * @param {Roo.LayoutRegion} this
38435 * @param {Number} newSize The new size (width for east/west, height for north/south)
38439 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38440 this.panels = new Roo.util.MixedCollection();
38441 this.panels.getKey = this.getPanelId.createDelegate(this);
38443 this.activePanel = null;
38444 // ensure listeners are added...
38446 if (config.listeners || config.events) {
38447 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38448 listeners : config.listeners || {},
38449 events : config.events || {}
38453 if(skipConfig !== true){
38454 this.applyConfig(config);
38458 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38460 getPanelId : function(p){
38464 applyConfig : function(config){
38465 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38466 this.config = config;
38471 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38472 * the width, for horizontal (north, south) the height.
38473 * @param {Number} newSize The new width or height
38475 resizeTo : function(newSize){
38476 var el = this.el ? this.el :
38477 (this.activePanel ? this.activePanel.getEl() : null);
38479 switch(this.position){
38482 el.setWidth(newSize);
38483 this.fireEvent("resized", this, newSize);
38487 el.setHeight(newSize);
38488 this.fireEvent("resized", this, newSize);
38494 getBox : function(){
38495 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38498 getMargins : function(){
38499 return this.margins;
38502 updateBox : function(box){
38504 var el = this.activePanel.getEl();
38505 el.dom.style.left = box.x + "px";
38506 el.dom.style.top = box.y + "px";
38507 this.activePanel.setSize(box.width, box.height);
38511 * Returns the container element for this region.
38512 * @return {Roo.Element}
38514 getEl : function(){
38515 return this.activePanel;
38519 * Returns true if this region is currently visible.
38520 * @return {Boolean}
38522 isVisible : function(){
38523 return this.activePanel ? true : false;
38526 setActivePanel : function(panel){
38527 panel = this.getPanel(panel);
38528 if(this.activePanel && this.activePanel != panel){
38529 this.activePanel.setActiveState(false);
38530 this.activePanel.getEl().setLeftTop(-10000,-10000);
38532 this.activePanel = panel;
38533 panel.setActiveState(true);
38535 panel.setSize(this.box.width, this.box.height);
38537 this.fireEvent("panelactivated", this, panel);
38538 this.fireEvent("invalidated");
38542 * Show the specified panel.
38543 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38544 * @return {Roo.ContentPanel} The shown panel or null
38546 showPanel : function(panel){
38547 panel = this.getPanel(panel);
38549 this.setActivePanel(panel);
38555 * Get the active panel for this region.
38556 * @return {Roo.ContentPanel} The active panel or null
38558 getActivePanel : function(){
38559 return this.activePanel;
38563 * Add the passed ContentPanel(s)
38564 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38565 * @return {Roo.ContentPanel} The panel added (if only one was added)
38567 add : function(panel){
38568 if(arguments.length > 1){
38569 for(var i = 0, len = arguments.length; i < len; i++) {
38570 this.add(arguments[i]);
38574 if(this.hasPanel(panel)){
38575 this.showPanel(panel);
38578 var el = panel.getEl();
38579 if(el.dom.parentNode != this.mgr.el.dom){
38580 this.mgr.el.dom.appendChild(el.dom);
38582 if(panel.setRegion){
38583 panel.setRegion(this);
38585 this.panels.add(panel);
38586 el.setStyle("position", "absolute");
38587 if(!panel.background){
38588 this.setActivePanel(panel);
38589 if(this.config.initialSize && this.panels.getCount()==1){
38590 this.resizeTo(this.config.initialSize);
38593 this.fireEvent("paneladded", this, panel);
38598 * Returns true if the panel is in this region.
38599 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38600 * @return {Boolean}
38602 hasPanel : function(panel){
38603 if(typeof panel == "object"){ // must be panel obj
38604 panel = panel.getId();
38606 return this.getPanel(panel) ? true : false;
38610 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38611 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38612 * @param {Boolean} preservePanel Overrides the config preservePanel option
38613 * @return {Roo.ContentPanel} The panel that was removed
38615 remove : function(panel, preservePanel){
38616 panel = this.getPanel(panel);
38621 this.fireEvent("beforeremove", this, panel, e);
38622 if(e.cancel === true){
38625 var panelId = panel.getId();
38626 this.panels.removeKey(panelId);
38631 * Returns the panel specified or null if it's not in this region.
38632 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38633 * @return {Roo.ContentPanel}
38635 getPanel : function(id){
38636 if(typeof id == "object"){ // must be panel obj
38639 return this.panels.get(id);
38643 * Returns this regions position (north/south/east/west/center).
38646 getPosition: function(){
38647 return this.position;
38651 * Ext JS Library 1.1.1
38652 * Copyright(c) 2006-2007, Ext JS, LLC.
38654 * Originally Released Under LGPL - original licence link has changed is not relivant.
38657 * <script type="text/javascript">
38661 * @class Roo.bootstrap.layout.Region
38662 * @extends Roo.bootstrap.layout.Basic
38663 * This class represents a region in a layout manager.
38665 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38666 * @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})
38667 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38668 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38669 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38670 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38671 * @cfg {String} title The title for the region (overrides panel titles)
38672 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38673 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38674 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38675 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38676 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38677 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38678 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38679 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38680 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38681 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38683 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38684 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38685 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38686 * @cfg {Number} width For East/West panels
38687 * @cfg {Number} height For North/South panels
38688 * @cfg {Boolean} split To show the splitter
38689 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38691 * @cfg {string} cls Extra CSS classes to add to region
38693 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38694 * @cfg {string} region the region that it inhabits..
38697 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38698 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38700 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38701 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38702 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38704 Roo.bootstrap.layout.Region = function(config)
38706 this.applyConfig(config);
38708 var mgr = config.mgr;
38709 var pos = config.region;
38710 config.skipConfig = true;
38711 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38714 this.onRender(mgr.el);
38717 this.visible = true;
38718 this.collapsed = false;
38719 this.unrendered_panels = [];
38722 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38724 position: '', // set by wrapper (eg. north/south etc..)
38725 unrendered_panels : null, // unrendered panels.
38727 tabPosition : false,
38729 mgr: false, // points to 'Border'
38732 createBody : function(){
38733 /** This region's body element
38734 * @type Roo.Element */
38735 this.bodyEl = this.el.createChild({
38737 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38741 onRender: function(ctr, pos)
38743 var dh = Roo.DomHelper;
38744 /** This region's container element
38745 * @type Roo.Element */
38746 this.el = dh.append(ctr.dom, {
38748 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38750 /** This region's title element
38751 * @type Roo.Element */
38753 this.titleEl = dh.append(this.el.dom, {
38755 unselectable: "on",
38756 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38758 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38759 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38763 this.titleEl.enableDisplayMode();
38764 /** This region's title text element
38765 * @type HTMLElement */
38766 this.titleTextEl = this.titleEl.dom.firstChild;
38767 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38769 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38770 this.closeBtn.enableDisplayMode();
38771 this.closeBtn.on("click", this.closeClicked, this);
38772 this.closeBtn.hide();
38774 this.createBody(this.config);
38775 if(this.config.hideWhenEmpty){
38777 this.on("paneladded", this.validateVisibility, this);
38778 this.on("panelremoved", this.validateVisibility, this);
38780 if(this.autoScroll){
38781 this.bodyEl.setStyle("overflow", "auto");
38783 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38785 //if(c.titlebar !== false){
38786 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38787 this.titleEl.hide();
38789 this.titleEl.show();
38790 if(this.config.title){
38791 this.titleTextEl.innerHTML = this.config.title;
38795 if(this.config.collapsed){
38796 this.collapse(true);
38798 if(this.config.hidden){
38802 if (this.unrendered_panels && this.unrendered_panels.length) {
38803 for (var i =0;i< this.unrendered_panels.length; i++) {
38804 this.add(this.unrendered_panels[i]);
38806 this.unrendered_panels = null;
38812 applyConfig : function(c)
38815 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38816 var dh = Roo.DomHelper;
38817 if(c.titlebar !== false){
38818 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38819 this.collapseBtn.on("click", this.collapse, this);
38820 this.collapseBtn.enableDisplayMode();
38822 if(c.showPin === true || this.showPin){
38823 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38824 this.stickBtn.enableDisplayMode();
38825 this.stickBtn.on("click", this.expand, this);
38826 this.stickBtn.hide();
38831 /** This region's collapsed element
38832 * @type Roo.Element */
38835 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38836 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38839 if(c.floatable !== false){
38840 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38841 this.collapsedEl.on("click", this.collapseClick, this);
38844 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38845 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38846 id: "message", unselectable: "on", style:{"float":"left"}});
38847 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38849 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38850 this.expandBtn.on("click", this.expand, this);
38854 if(this.collapseBtn){
38855 this.collapseBtn.setVisible(c.collapsible == true);
38858 this.cmargins = c.cmargins || this.cmargins ||
38859 (this.position == "west" || this.position == "east" ?
38860 {top: 0, left: 2, right:2, bottom: 0} :
38861 {top: 2, left: 0, right:0, bottom: 2});
38863 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38866 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38868 this.autoScroll = c.autoScroll || false;
38873 this.duration = c.duration || .30;
38874 this.slideDuration = c.slideDuration || .45;
38879 * Returns true if this region is currently visible.
38880 * @return {Boolean}
38882 isVisible : function(){
38883 return this.visible;
38887 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38888 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38890 //setCollapsedTitle : function(title){
38891 // title = title || " ";
38892 // if(this.collapsedTitleTextEl){
38893 // this.collapsedTitleTextEl.innerHTML = title;
38897 getBox : function(){
38899 // if(!this.collapsed){
38900 b = this.el.getBox(false, true);
38902 // b = this.collapsedEl.getBox(false, true);
38907 getMargins : function(){
38908 return this.margins;
38909 //return this.collapsed ? this.cmargins : this.margins;
38912 highlight : function(){
38913 this.el.addClass("x-layout-panel-dragover");
38916 unhighlight : function(){
38917 this.el.removeClass("x-layout-panel-dragover");
38920 updateBox : function(box)
38922 if (!this.bodyEl) {
38923 return; // not rendered yet..
38927 if(!this.collapsed){
38928 this.el.dom.style.left = box.x + "px";
38929 this.el.dom.style.top = box.y + "px";
38930 this.updateBody(box.width, box.height);
38932 this.collapsedEl.dom.style.left = box.x + "px";
38933 this.collapsedEl.dom.style.top = box.y + "px";
38934 this.collapsedEl.setSize(box.width, box.height);
38937 this.tabs.autoSizeTabs();
38941 updateBody : function(w, h)
38944 this.el.setWidth(w);
38945 w -= this.el.getBorderWidth("rl");
38946 if(this.config.adjustments){
38947 w += this.config.adjustments[0];
38950 if(h !== null && h > 0){
38951 this.el.setHeight(h);
38952 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38953 h -= this.el.getBorderWidth("tb");
38954 if(this.config.adjustments){
38955 h += this.config.adjustments[1];
38957 this.bodyEl.setHeight(h);
38959 h = this.tabs.syncHeight(h);
38962 if(this.panelSize){
38963 w = w !== null ? w : this.panelSize.width;
38964 h = h !== null ? h : this.panelSize.height;
38966 if(this.activePanel){
38967 var el = this.activePanel.getEl();
38968 w = w !== null ? w : el.getWidth();
38969 h = h !== null ? h : el.getHeight();
38970 this.panelSize = {width: w, height: h};
38971 this.activePanel.setSize(w, h);
38973 if(Roo.isIE && this.tabs){
38974 this.tabs.el.repaint();
38979 * Returns the container element for this region.
38980 * @return {Roo.Element}
38982 getEl : function(){
38987 * Hides this region.
38990 //if(!this.collapsed){
38991 this.el.dom.style.left = "-2000px";
38994 // this.collapsedEl.dom.style.left = "-2000px";
38995 // this.collapsedEl.hide();
38997 this.visible = false;
38998 this.fireEvent("visibilitychange", this, false);
39002 * Shows this region if it was previously hidden.
39005 //if(!this.collapsed){
39008 // this.collapsedEl.show();
39010 this.visible = true;
39011 this.fireEvent("visibilitychange", this, true);
39014 closeClicked : function(){
39015 if(this.activePanel){
39016 this.remove(this.activePanel);
39020 collapseClick : function(e){
39022 e.stopPropagation();
39025 e.stopPropagation();
39031 * Collapses this region.
39032 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39035 collapse : function(skipAnim, skipCheck = false){
39036 if(this.collapsed) {
39040 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39042 this.collapsed = true;
39044 this.split.el.hide();
39046 if(this.config.animate && skipAnim !== true){
39047 this.fireEvent("invalidated", this);
39048 this.animateCollapse();
39050 this.el.setLocation(-20000,-20000);
39052 this.collapsedEl.show();
39053 this.fireEvent("collapsed", this);
39054 this.fireEvent("invalidated", this);
39060 animateCollapse : function(){
39065 * Expands this region if it was previously collapsed.
39066 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39067 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39070 expand : function(e, skipAnim){
39072 e.stopPropagation();
39074 if(!this.collapsed || this.el.hasActiveFx()) {
39078 this.afterSlideIn();
39081 this.collapsed = false;
39082 if(this.config.animate && skipAnim !== true){
39083 this.animateExpand();
39087 this.split.el.show();
39089 this.collapsedEl.setLocation(-2000,-2000);
39090 this.collapsedEl.hide();
39091 this.fireEvent("invalidated", this);
39092 this.fireEvent("expanded", this);
39096 animateExpand : function(){
39100 initTabs : function()
39102 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39104 var ts = new Roo.bootstrap.panel.Tabs({
39105 el: this.bodyEl.dom,
39107 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39108 disableTooltips: this.config.disableTabTips,
39109 toolbar : this.config.toolbar
39112 if(this.config.hideTabs){
39113 ts.stripWrap.setDisplayed(false);
39116 ts.resizeTabs = this.config.resizeTabs === true;
39117 ts.minTabWidth = this.config.minTabWidth || 40;
39118 ts.maxTabWidth = this.config.maxTabWidth || 250;
39119 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39120 ts.monitorResize = false;
39121 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39122 ts.bodyEl.addClass('roo-layout-tabs-body');
39123 this.panels.each(this.initPanelAsTab, this);
39126 initPanelAsTab : function(panel){
39127 var ti = this.tabs.addTab(
39131 this.config.closeOnTab && panel.isClosable(),
39134 if(panel.tabTip !== undefined){
39135 ti.setTooltip(panel.tabTip);
39137 ti.on("activate", function(){
39138 this.setActivePanel(panel);
39141 if(this.config.closeOnTab){
39142 ti.on("beforeclose", function(t, e){
39144 this.remove(panel);
39148 panel.tabItem = ti;
39153 updatePanelTitle : function(panel, title)
39155 if(this.activePanel == panel){
39156 this.updateTitle(title);
39159 var ti = this.tabs.getTab(panel.getEl().id);
39161 if(panel.tabTip !== undefined){
39162 ti.setTooltip(panel.tabTip);
39167 updateTitle : function(title){
39168 if(this.titleTextEl && !this.config.title){
39169 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39173 setActivePanel : function(panel)
39175 panel = this.getPanel(panel);
39176 if(this.activePanel && this.activePanel != panel){
39177 if(this.activePanel.setActiveState(false) === false){
39181 this.activePanel = panel;
39182 panel.setActiveState(true);
39183 if(this.panelSize){
39184 panel.setSize(this.panelSize.width, this.panelSize.height);
39187 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39189 this.updateTitle(panel.getTitle());
39191 this.fireEvent("invalidated", this);
39193 this.fireEvent("panelactivated", this, panel);
39197 * Shows the specified panel.
39198 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39199 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39201 showPanel : function(panel)
39203 panel = this.getPanel(panel);
39206 var tab = this.tabs.getTab(panel.getEl().id);
39207 if(tab.isHidden()){
39208 this.tabs.unhideTab(tab.id);
39212 this.setActivePanel(panel);
39219 * Get the active panel for this region.
39220 * @return {Roo.ContentPanel} The active panel or null
39222 getActivePanel : function(){
39223 return this.activePanel;
39226 validateVisibility : function(){
39227 if(this.panels.getCount() < 1){
39228 this.updateTitle(" ");
39229 this.closeBtn.hide();
39232 if(!this.isVisible()){
39239 * Adds the passed ContentPanel(s) to this region.
39240 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39241 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39243 add : function(panel)
39245 if(arguments.length > 1){
39246 for(var i = 0, len = arguments.length; i < len; i++) {
39247 this.add(arguments[i]);
39252 // if we have not been rendered yet, then we can not really do much of this..
39253 if (!this.bodyEl) {
39254 this.unrendered_panels.push(panel);
39261 if(this.hasPanel(panel)){
39262 this.showPanel(panel);
39265 panel.setRegion(this);
39266 this.panels.add(panel);
39267 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39268 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39269 // and hide them... ???
39270 this.bodyEl.dom.appendChild(panel.getEl().dom);
39271 if(panel.background !== true){
39272 this.setActivePanel(panel);
39274 this.fireEvent("paneladded", this, panel);
39281 this.initPanelAsTab(panel);
39285 if(panel.background !== true){
39286 this.tabs.activate(panel.getEl().id);
39288 this.fireEvent("paneladded", this, panel);
39293 * Hides the tab for the specified panel.
39294 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39296 hidePanel : function(panel){
39297 if(this.tabs && (panel = this.getPanel(panel))){
39298 this.tabs.hideTab(panel.getEl().id);
39303 * Unhides the tab for a previously hidden panel.
39304 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39306 unhidePanel : function(panel){
39307 if(this.tabs && (panel = this.getPanel(panel))){
39308 this.tabs.unhideTab(panel.getEl().id);
39312 clearPanels : function(){
39313 while(this.panels.getCount() > 0){
39314 this.remove(this.panels.first());
39319 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39320 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39321 * @param {Boolean} preservePanel Overrides the config preservePanel option
39322 * @return {Roo.ContentPanel} The panel that was removed
39324 remove : function(panel, preservePanel)
39326 panel = this.getPanel(panel);
39331 this.fireEvent("beforeremove", this, panel, e);
39332 if(e.cancel === true){
39335 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39336 var panelId = panel.getId();
39337 this.panels.removeKey(panelId);
39339 document.body.appendChild(panel.getEl().dom);
39342 this.tabs.removeTab(panel.getEl().id);
39343 }else if (!preservePanel){
39344 this.bodyEl.dom.removeChild(panel.getEl().dom);
39346 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39347 var p = this.panels.first();
39348 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39349 tempEl.appendChild(p.getEl().dom);
39350 this.bodyEl.update("");
39351 this.bodyEl.dom.appendChild(p.getEl().dom);
39353 this.updateTitle(p.getTitle());
39355 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39356 this.setActivePanel(p);
39358 panel.setRegion(null);
39359 if(this.activePanel == panel){
39360 this.activePanel = null;
39362 if(this.config.autoDestroy !== false && preservePanel !== true){
39363 try{panel.destroy();}catch(e){}
39365 this.fireEvent("panelremoved", this, panel);
39370 * Returns the TabPanel component used by this region
39371 * @return {Roo.TabPanel}
39373 getTabs : function(){
39377 createTool : function(parentEl, className){
39378 var btn = Roo.DomHelper.append(parentEl, {
39380 cls: "x-layout-tools-button",
39383 cls: "roo-layout-tools-button-inner " + className,
39387 btn.addClassOnOver("roo-layout-tools-button-over");
39392 * Ext JS Library 1.1.1
39393 * Copyright(c) 2006-2007, Ext JS, LLC.
39395 * Originally Released Under LGPL - original licence link has changed is not relivant.
39398 * <script type="text/javascript">
39404 * @class Roo.SplitLayoutRegion
39405 * @extends Roo.LayoutRegion
39406 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39408 Roo.bootstrap.layout.Split = function(config){
39409 this.cursor = config.cursor;
39410 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39413 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39415 splitTip : "Drag to resize.",
39416 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39417 useSplitTips : false,
39419 applyConfig : function(config){
39420 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39423 onRender : function(ctr,pos) {
39425 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39426 if(!this.config.split){
39431 var splitEl = Roo.DomHelper.append(ctr.dom, {
39433 id: this.el.id + "-split",
39434 cls: "roo-layout-split roo-layout-split-"+this.position,
39437 /** The SplitBar for this region
39438 * @type Roo.SplitBar */
39439 // does not exist yet...
39440 Roo.log([this.position, this.orientation]);
39442 this.split = new Roo.bootstrap.SplitBar({
39443 dragElement : splitEl,
39444 resizingElement: this.el,
39445 orientation : this.orientation
39448 this.split.on("moved", this.onSplitMove, this);
39449 this.split.useShim = this.config.useShim === true;
39450 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39451 if(this.useSplitTips){
39452 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39454 //if(config.collapsible){
39455 // this.split.el.on("dblclick", this.collapse, this);
39458 if(typeof this.config.minSize != "undefined"){
39459 this.split.minSize = this.config.minSize;
39461 if(typeof this.config.maxSize != "undefined"){
39462 this.split.maxSize = this.config.maxSize;
39464 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39465 this.hideSplitter();
39470 getHMaxSize : function(){
39471 var cmax = this.config.maxSize || 10000;
39472 var center = this.mgr.getRegion("center");
39473 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39476 getVMaxSize : function(){
39477 var cmax = this.config.maxSize || 10000;
39478 var center = this.mgr.getRegion("center");
39479 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39482 onSplitMove : function(split, newSize){
39483 this.fireEvent("resized", this, newSize);
39487 * Returns the {@link Roo.SplitBar} for this region.
39488 * @return {Roo.SplitBar}
39490 getSplitBar : function(){
39495 this.hideSplitter();
39496 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39499 hideSplitter : function(){
39501 this.split.el.setLocation(-2000,-2000);
39502 this.split.el.hide();
39508 this.split.el.show();
39510 Roo.bootstrap.layout.Split.superclass.show.call(this);
39513 beforeSlide: function(){
39514 if(Roo.isGecko){// firefox overflow auto bug workaround
39515 this.bodyEl.clip();
39517 this.tabs.bodyEl.clip();
39519 if(this.activePanel){
39520 this.activePanel.getEl().clip();
39522 if(this.activePanel.beforeSlide){
39523 this.activePanel.beforeSlide();
39529 afterSlide : function(){
39530 if(Roo.isGecko){// firefox overflow auto bug workaround
39531 this.bodyEl.unclip();
39533 this.tabs.bodyEl.unclip();
39535 if(this.activePanel){
39536 this.activePanel.getEl().unclip();
39537 if(this.activePanel.afterSlide){
39538 this.activePanel.afterSlide();
39544 initAutoHide : function(){
39545 if(this.autoHide !== false){
39546 if(!this.autoHideHd){
39547 var st = new Roo.util.DelayedTask(this.slideIn, this);
39548 this.autoHideHd = {
39549 "mouseout": function(e){
39550 if(!e.within(this.el, true)){
39554 "mouseover" : function(e){
39560 this.el.on(this.autoHideHd);
39564 clearAutoHide : function(){
39565 if(this.autoHide !== false){
39566 this.el.un("mouseout", this.autoHideHd.mouseout);
39567 this.el.un("mouseover", this.autoHideHd.mouseover);
39571 clearMonitor : function(){
39572 Roo.get(document).un("click", this.slideInIf, this);
39575 // these names are backwards but not changed for compat
39576 slideOut : function(){
39577 if(this.isSlid || this.el.hasActiveFx()){
39580 this.isSlid = true;
39581 if(this.collapseBtn){
39582 this.collapseBtn.hide();
39584 this.closeBtnState = this.closeBtn.getStyle('display');
39585 this.closeBtn.hide();
39587 this.stickBtn.show();
39590 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39591 this.beforeSlide();
39592 this.el.setStyle("z-index", 10001);
39593 this.el.slideIn(this.getSlideAnchor(), {
39594 callback: function(){
39596 this.initAutoHide();
39597 Roo.get(document).on("click", this.slideInIf, this);
39598 this.fireEvent("slideshow", this);
39605 afterSlideIn : function(){
39606 this.clearAutoHide();
39607 this.isSlid = false;
39608 this.clearMonitor();
39609 this.el.setStyle("z-index", "");
39610 if(this.collapseBtn){
39611 this.collapseBtn.show();
39613 this.closeBtn.setStyle('display', this.closeBtnState);
39615 this.stickBtn.hide();
39617 this.fireEvent("slidehide", this);
39620 slideIn : function(cb){
39621 if(!this.isSlid || this.el.hasActiveFx()){
39625 this.isSlid = false;
39626 this.beforeSlide();
39627 this.el.slideOut(this.getSlideAnchor(), {
39628 callback: function(){
39629 this.el.setLeftTop(-10000, -10000);
39631 this.afterSlideIn();
39639 slideInIf : function(e){
39640 if(!e.within(this.el)){
39645 animateCollapse : function(){
39646 this.beforeSlide();
39647 this.el.setStyle("z-index", 20000);
39648 var anchor = this.getSlideAnchor();
39649 this.el.slideOut(anchor, {
39650 callback : function(){
39651 this.el.setStyle("z-index", "");
39652 this.collapsedEl.slideIn(anchor, {duration:.3});
39654 this.el.setLocation(-10000,-10000);
39656 this.fireEvent("collapsed", this);
39663 animateExpand : function(){
39664 this.beforeSlide();
39665 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39666 this.el.setStyle("z-index", 20000);
39667 this.collapsedEl.hide({
39670 this.el.slideIn(this.getSlideAnchor(), {
39671 callback : function(){
39672 this.el.setStyle("z-index", "");
39675 this.split.el.show();
39677 this.fireEvent("invalidated", this);
39678 this.fireEvent("expanded", this);
39706 getAnchor : function(){
39707 return this.anchors[this.position];
39710 getCollapseAnchor : function(){
39711 return this.canchors[this.position];
39714 getSlideAnchor : function(){
39715 return this.sanchors[this.position];
39718 getAlignAdj : function(){
39719 var cm = this.cmargins;
39720 switch(this.position){
39736 getExpandAdj : function(){
39737 var c = this.collapsedEl, cm = this.cmargins;
39738 switch(this.position){
39740 return [-(cm.right+c.getWidth()+cm.left), 0];
39743 return [cm.right+c.getWidth()+cm.left, 0];
39746 return [0, -(cm.top+cm.bottom+c.getHeight())];
39749 return [0, cm.top+cm.bottom+c.getHeight()];
39755 * Ext JS Library 1.1.1
39756 * Copyright(c) 2006-2007, Ext JS, LLC.
39758 * Originally Released Under LGPL - original licence link has changed is not relivant.
39761 * <script type="text/javascript">
39764 * These classes are private internal classes
39766 Roo.bootstrap.layout.Center = function(config){
39767 config.region = "center";
39768 Roo.bootstrap.layout.Region.call(this, config);
39769 this.visible = true;
39770 this.minWidth = config.minWidth || 20;
39771 this.minHeight = config.minHeight || 20;
39774 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39776 // center panel can't be hidden
39780 // center panel can't be hidden
39783 getMinWidth: function(){
39784 return this.minWidth;
39787 getMinHeight: function(){
39788 return this.minHeight;
39802 Roo.bootstrap.layout.North = function(config)
39804 config.region = 'north';
39805 config.cursor = 'n-resize';
39807 Roo.bootstrap.layout.Split.call(this, config);
39811 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39812 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39813 this.split.el.addClass("roo-layout-split-v");
39815 //var size = config.initialSize || config.height;
39816 //if(this.el && typeof size != "undefined"){
39817 // this.el.setHeight(size);
39820 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39822 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39825 onRender : function(ctr, pos)
39827 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39828 var size = this.config.initialSize || this.config.height;
39829 if(this.el && typeof size != "undefined"){
39830 this.el.setHeight(size);
39835 getBox : function(){
39836 if(this.collapsed){
39837 return this.collapsedEl.getBox();
39839 var box = this.el.getBox();
39841 box.height += this.split.el.getHeight();
39846 updateBox : function(box){
39847 if(this.split && !this.collapsed){
39848 box.height -= this.split.el.getHeight();
39849 this.split.el.setLeft(box.x);
39850 this.split.el.setTop(box.y+box.height);
39851 this.split.el.setWidth(box.width);
39853 if(this.collapsed){
39854 this.updateBody(box.width, null);
39856 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39864 Roo.bootstrap.layout.South = function(config){
39865 config.region = 'south';
39866 config.cursor = 's-resize';
39867 Roo.bootstrap.layout.Split.call(this, config);
39869 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39870 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39871 this.split.el.addClass("roo-layout-split-v");
39876 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39877 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39879 onRender : function(ctr, pos)
39881 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39882 var size = this.config.initialSize || this.config.height;
39883 if(this.el && typeof size != "undefined"){
39884 this.el.setHeight(size);
39889 getBox : function(){
39890 if(this.collapsed){
39891 return this.collapsedEl.getBox();
39893 var box = this.el.getBox();
39895 var sh = this.split.el.getHeight();
39902 updateBox : function(box){
39903 if(this.split && !this.collapsed){
39904 var sh = this.split.el.getHeight();
39907 this.split.el.setLeft(box.x);
39908 this.split.el.setTop(box.y-sh);
39909 this.split.el.setWidth(box.width);
39911 if(this.collapsed){
39912 this.updateBody(box.width, null);
39914 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39918 Roo.bootstrap.layout.East = function(config){
39919 config.region = "east";
39920 config.cursor = "e-resize";
39921 Roo.bootstrap.layout.Split.call(this, config);
39923 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39924 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39925 this.split.el.addClass("roo-layout-split-h");
39929 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39930 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39932 onRender : function(ctr, pos)
39934 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39935 var size = this.config.initialSize || this.config.width;
39936 if(this.el && typeof size != "undefined"){
39937 this.el.setWidth(size);
39942 getBox : function(){
39943 if(this.collapsed){
39944 return this.collapsedEl.getBox();
39946 var box = this.el.getBox();
39948 var sw = this.split.el.getWidth();
39955 updateBox : function(box){
39956 if(this.split && !this.collapsed){
39957 var sw = this.split.el.getWidth();
39959 this.split.el.setLeft(box.x);
39960 this.split.el.setTop(box.y);
39961 this.split.el.setHeight(box.height);
39964 if(this.collapsed){
39965 this.updateBody(null, box.height);
39967 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39971 Roo.bootstrap.layout.West = function(config){
39972 config.region = "west";
39973 config.cursor = "w-resize";
39975 Roo.bootstrap.layout.Split.call(this, config);
39977 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39978 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39979 this.split.el.addClass("roo-layout-split-h");
39983 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39984 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39986 onRender: function(ctr, pos)
39988 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39989 var size = this.config.initialSize || this.config.width;
39990 if(typeof size != "undefined"){
39991 this.el.setWidth(size);
39995 getBox : function(){
39996 if(this.collapsed){
39997 return this.collapsedEl.getBox();
39999 var box = this.el.getBox();
40000 if (box.width == 0) {
40001 box.width = this.config.width; // kludge?
40004 box.width += this.split.el.getWidth();
40009 updateBox : function(box){
40010 if(this.split && !this.collapsed){
40011 var sw = this.split.el.getWidth();
40013 this.split.el.setLeft(box.x+box.width);
40014 this.split.el.setTop(box.y);
40015 this.split.el.setHeight(box.height);
40017 if(this.collapsed){
40018 this.updateBody(null, box.height);
40020 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40022 });Roo.namespace("Roo.bootstrap.panel");/*
40024 * Ext JS Library 1.1.1
40025 * Copyright(c) 2006-2007, Ext JS, LLC.
40027 * Originally Released Under LGPL - original licence link has changed is not relivant.
40030 * <script type="text/javascript">
40033 * @class Roo.ContentPanel
40034 * @extends Roo.util.Observable
40035 * A basic ContentPanel element.
40036 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40037 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40038 * @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
40039 * @cfg {Boolean} closable True if the panel can be closed/removed
40040 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40041 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40042 * @cfg {Toolbar} toolbar A toolbar for this panel
40043 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40044 * @cfg {String} title The title for this panel
40045 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40046 * @cfg {String} url Calls {@link #setUrl} with this value
40047 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40048 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40049 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40050 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40051 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40052 * @cfg {Boolean} badges render the badges
40053 * @cfg {String} cls extra classes to use
40054 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40057 * Create a new ContentPanel.
40058 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40059 * @param {String/Object} config A string to set only the title or a config object
40060 * @param {String} content (optional) Set the HTML content for this panel
40061 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40063 Roo.bootstrap.panel.Content = function( config){
40065 this.tpl = config.tpl || false;
40067 var el = config.el;
40068 var content = config.content;
40070 if(config.autoCreate){ // xtype is available if this is called from factory
40073 this.el = Roo.get(el);
40074 if(!this.el && config && config.autoCreate){
40075 if(typeof config.autoCreate == "object"){
40076 if(!config.autoCreate.id){
40077 config.autoCreate.id = config.id||el;
40079 this.el = Roo.DomHelper.append(document.body,
40080 config.autoCreate, true);
40084 cls: (config.cls || '') +
40085 (config.background ? ' bg-' + config.background : '') +
40086 " roo-layout-inactive-content",
40089 if (config.iframe) {
40093 style : 'border: 0px',
40094 src : 'about:blank'
40100 elcfg.html = config.html;
40104 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40105 if (config.iframe) {
40106 this.iframeEl = this.el.select('iframe',true).first();
40111 this.closable = false;
40112 this.loaded = false;
40113 this.active = false;
40116 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40118 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40120 this.wrapEl = this.el; //this.el.wrap();
40122 if (config.toolbar.items) {
40123 ti = config.toolbar.items ;
40124 delete config.toolbar.items ;
40128 this.toolbar.render(this.wrapEl, 'before');
40129 for(var i =0;i < ti.length;i++) {
40130 // Roo.log(['add child', items[i]]);
40131 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40133 this.toolbar.items = nitems;
40134 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40135 delete config.toolbar;
40139 // xtype created footer. - not sure if will work as we normally have to render first..
40140 if (this.footer && !this.footer.el && this.footer.xtype) {
40141 if (!this.wrapEl) {
40142 this.wrapEl = this.el.wrap();
40145 this.footer.container = this.wrapEl.createChild();
40147 this.footer = Roo.factory(this.footer, Roo);
40152 if(typeof config == "string"){
40153 this.title = config;
40155 Roo.apply(this, config);
40159 this.resizeEl = Roo.get(this.resizeEl, true);
40161 this.resizeEl = this.el;
40163 // handle view.xtype
40171 * Fires when this panel is activated.
40172 * @param {Roo.ContentPanel} this
40176 * @event deactivate
40177 * Fires when this panel is activated.
40178 * @param {Roo.ContentPanel} this
40180 "deactivate" : true,
40184 * Fires when this panel is resized if fitToFrame is true.
40185 * @param {Roo.ContentPanel} this
40186 * @param {Number} width The width after any component adjustments
40187 * @param {Number} height The height after any component adjustments
40193 * Fires when this tab is created
40194 * @param {Roo.ContentPanel} this
40205 if(this.autoScroll && !this.iframe){
40206 this.resizeEl.setStyle("overflow", "auto");
40208 // fix randome scrolling
40209 //this.el.on('scroll', function() {
40210 // Roo.log('fix random scolling');
40211 // this.scrollTo('top',0);
40214 content = content || this.content;
40216 this.setContent(content);
40218 if(config && config.url){
40219 this.setUrl(this.url, this.params, this.loadOnce);
40224 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40226 if (this.view && typeof(this.view.xtype) != 'undefined') {
40227 this.view.el = this.el.appendChild(document.createElement("div"));
40228 this.view = Roo.factory(this.view);
40229 this.view.render && this.view.render(false, '');
40233 this.fireEvent('render', this);
40236 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40246 setRegion : function(region){
40247 this.region = region;
40248 this.setActiveClass(region && !this.background);
40252 setActiveClass: function(state)
40255 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40256 this.el.setStyle('position','relative');
40258 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40259 this.el.setStyle('position', 'absolute');
40264 * Returns the toolbar for this Panel if one was configured.
40265 * @return {Roo.Toolbar}
40267 getToolbar : function(){
40268 return this.toolbar;
40271 setActiveState : function(active)
40273 this.active = active;
40274 this.setActiveClass(active);
40276 if(this.fireEvent("deactivate", this) === false){
40281 this.fireEvent("activate", this);
40285 * Updates this panel's element (not for iframe)
40286 * @param {String} content The new content
40287 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40289 setContent : function(content, loadScripts){
40294 this.el.update(content, loadScripts);
40297 ignoreResize : function(w, h){
40298 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40301 this.lastSize = {width: w, height: h};
40306 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40307 * @return {Roo.UpdateManager} The UpdateManager
40309 getUpdateManager : function(){
40313 return this.el.getUpdateManager();
40316 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40317 * Does not work with IFRAME contents
40318 * @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:
40321 url: "your-url.php",
40322 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40323 callback: yourFunction,
40324 scope: yourObject, //(optional scope)
40327 text: "Loading...",
40333 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40334 * 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.
40335 * @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}
40336 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40337 * @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.
40338 * @return {Roo.ContentPanel} this
40346 var um = this.el.getUpdateManager();
40347 um.update.apply(um, arguments);
40353 * 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.
40354 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40355 * @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)
40356 * @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)
40357 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40359 setUrl : function(url, params, loadOnce){
40361 this.iframeEl.dom.src = url;
40365 if(this.refreshDelegate){
40366 this.removeListener("activate", this.refreshDelegate);
40368 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40369 this.on("activate", this.refreshDelegate);
40370 return this.el.getUpdateManager();
40373 _handleRefresh : function(url, params, loadOnce){
40374 if(!loadOnce || !this.loaded){
40375 var updater = this.el.getUpdateManager();
40376 updater.update(url, params, this._setLoaded.createDelegate(this));
40380 _setLoaded : function(){
40381 this.loaded = true;
40385 * Returns this panel's id
40388 getId : function(){
40393 * Returns this panel's element - used by regiosn to add.
40394 * @return {Roo.Element}
40396 getEl : function(){
40397 return this.wrapEl || this.el;
40402 adjustForComponents : function(width, height)
40404 //Roo.log('adjustForComponents ');
40405 if(this.resizeEl != this.el){
40406 width -= this.el.getFrameWidth('lr');
40407 height -= this.el.getFrameWidth('tb');
40410 var te = this.toolbar.getEl();
40411 te.setWidth(width);
40412 height -= te.getHeight();
40415 var te = this.footer.getEl();
40416 te.setWidth(width);
40417 height -= te.getHeight();
40421 if(this.adjustments){
40422 width += this.adjustments[0];
40423 height += this.adjustments[1];
40425 return {"width": width, "height": height};
40428 setSize : function(width, height){
40429 if(this.fitToFrame && !this.ignoreResize(width, height)){
40430 if(this.fitContainer && this.resizeEl != this.el){
40431 this.el.setSize(width, height);
40433 var size = this.adjustForComponents(width, height);
40435 this.iframeEl.setSize(width,height);
40438 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40439 this.fireEvent('resize', this, size.width, size.height);
40446 * Returns this panel's title
40449 getTitle : function(){
40451 if (typeof(this.title) != 'object') {
40456 for (var k in this.title) {
40457 if (!this.title.hasOwnProperty(k)) {
40461 if (k.indexOf('-') >= 0) {
40462 var s = k.split('-');
40463 for (var i = 0; i<s.length; i++) {
40464 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40467 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40474 * Set this panel's title
40475 * @param {String} title
40477 setTitle : function(title){
40478 this.title = title;
40480 this.region.updatePanelTitle(this, title);
40485 * Returns true is this panel was configured to be closable
40486 * @return {Boolean}
40488 isClosable : function(){
40489 return this.closable;
40492 beforeSlide : function(){
40494 this.resizeEl.clip();
40497 afterSlide : function(){
40499 this.resizeEl.unclip();
40503 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40504 * Will fail silently if the {@link #setUrl} method has not been called.
40505 * This does not activate the panel, just updates its content.
40507 refresh : function(){
40508 if(this.refreshDelegate){
40509 this.loaded = false;
40510 this.refreshDelegate();
40515 * Destroys this panel
40517 destroy : function(){
40518 this.el.removeAllListeners();
40519 var tempEl = document.createElement("span");
40520 tempEl.appendChild(this.el.dom);
40521 tempEl.innerHTML = "";
40527 * form - if the content panel contains a form - this is a reference to it.
40528 * @type {Roo.form.Form}
40532 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40533 * This contains a reference to it.
40539 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40549 * @param {Object} cfg Xtype definition of item to add.
40553 getChildContainer: function () {
40554 return this.getEl();
40559 var ret = new Roo.factory(cfg);
40564 if (cfg.xtype.match(/^Form$/)) {
40567 //if (this.footer) {
40568 // el = this.footer.container.insertSibling(false, 'before');
40570 el = this.el.createChild();
40573 this.form = new Roo.form.Form(cfg);
40576 if ( this.form.allItems.length) {
40577 this.form.render(el.dom);
40581 // should only have one of theses..
40582 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40583 // views.. should not be just added - used named prop 'view''
40585 cfg.el = this.el.appendChild(document.createElement("div"));
40588 var ret = new Roo.factory(cfg);
40590 ret.render && ret.render(false, ''); // render blank..
40600 * @class Roo.bootstrap.panel.Grid
40601 * @extends Roo.bootstrap.panel.Content
40603 * Create a new GridPanel.
40604 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40605 * @param {Object} config A the config object
40611 Roo.bootstrap.panel.Grid = function(config)
40615 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40616 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40618 config.el = this.wrapper;
40619 //this.el = this.wrapper;
40621 if (config.container) {
40622 // ctor'ed from a Border/panel.grid
40625 this.wrapper.setStyle("overflow", "hidden");
40626 this.wrapper.addClass('roo-grid-container');
40631 if(config.toolbar){
40632 var tool_el = this.wrapper.createChild();
40633 this.toolbar = Roo.factory(config.toolbar);
40635 if (config.toolbar.items) {
40636 ti = config.toolbar.items ;
40637 delete config.toolbar.items ;
40641 this.toolbar.render(tool_el);
40642 for(var i =0;i < ti.length;i++) {
40643 // Roo.log(['add child', items[i]]);
40644 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40646 this.toolbar.items = nitems;
40648 delete config.toolbar;
40651 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40652 config.grid.scrollBody = true;;
40653 config.grid.monitorWindowResize = false; // turn off autosizing
40654 config.grid.autoHeight = false;
40655 config.grid.autoWidth = false;
40657 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40659 if (config.background) {
40660 // render grid on panel activation (if panel background)
40661 this.on('activate', function(gp) {
40662 if (!gp.grid.rendered) {
40663 gp.grid.render(this.wrapper);
40664 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40669 this.grid.render(this.wrapper);
40670 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40673 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40674 // ??? needed ??? config.el = this.wrapper;
40679 // xtype created footer. - not sure if will work as we normally have to render first..
40680 if (this.footer && !this.footer.el && this.footer.xtype) {
40682 var ctr = this.grid.getView().getFooterPanel(true);
40683 this.footer.dataSource = this.grid.dataSource;
40684 this.footer = Roo.factory(this.footer, Roo);
40685 this.footer.render(ctr);
40695 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40696 getId : function(){
40697 return this.grid.id;
40701 * Returns the grid for this panel
40702 * @return {Roo.bootstrap.Table}
40704 getGrid : function(){
40708 setSize : function(width, height){
40709 if(!this.ignoreResize(width, height)){
40710 var grid = this.grid;
40711 var size = this.adjustForComponents(width, height);
40712 // tfoot is not a footer?
40715 var gridel = grid.getGridEl();
40716 gridel.setSize(size.width, size.height);
40718 var tbd = grid.getGridEl().select('tbody', true).first();
40719 var thd = grid.getGridEl().select('thead',true).first();
40720 var tbf= grid.getGridEl().select('tfoot', true).first();
40723 size.height -= tbf.getHeight();
40726 size.height -= thd.getHeight();
40729 tbd.setSize(size.width, size.height );
40730 // this is for the account management tab -seems to work there.
40731 var thd = grid.getGridEl().select('thead',true).first();
40733 // tbd.setSize(size.width, size.height - thd.getHeight());
40742 beforeSlide : function(){
40743 this.grid.getView().scroller.clip();
40746 afterSlide : function(){
40747 this.grid.getView().scroller.unclip();
40750 destroy : function(){
40751 this.grid.destroy();
40753 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40758 * @class Roo.bootstrap.panel.Nest
40759 * @extends Roo.bootstrap.panel.Content
40761 * Create a new Panel, that can contain a layout.Border.
40764 * @param {Roo.BorderLayout} layout The layout for this panel
40765 * @param {String/Object} config A string to set only the title or a config object
40767 Roo.bootstrap.panel.Nest = function(config)
40769 // construct with only one argument..
40770 /* FIXME - implement nicer consturctors
40771 if (layout.layout) {
40773 layout = config.layout;
40774 delete config.layout;
40776 if (layout.xtype && !layout.getEl) {
40777 // then layout needs constructing..
40778 layout = Roo.factory(layout, Roo);
40782 config.el = config.layout.getEl();
40784 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40786 config.layout.monitorWindowResize = false; // turn off autosizing
40787 this.layout = config.layout;
40788 this.layout.getEl().addClass("roo-layout-nested-layout");
40789 this.layout.parent = this;
40796 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40798 setSize : function(width, height){
40799 if(!this.ignoreResize(width, height)){
40800 var size = this.adjustForComponents(width, height);
40801 var el = this.layout.getEl();
40802 if (size.height < 1) {
40803 el.setWidth(size.width);
40805 el.setSize(size.width, size.height);
40807 var touch = el.dom.offsetWidth;
40808 this.layout.layout();
40809 // ie requires a double layout on the first pass
40810 if(Roo.isIE && !this.initialized){
40811 this.initialized = true;
40812 this.layout.layout();
40817 // activate all subpanels if not currently active..
40819 setActiveState : function(active){
40820 this.active = active;
40821 this.setActiveClass(active);
40824 this.fireEvent("deactivate", this);
40828 this.fireEvent("activate", this);
40829 // not sure if this should happen before or after..
40830 if (!this.layout) {
40831 return; // should not happen..
40834 for (var r in this.layout.regions) {
40835 reg = this.layout.getRegion(r);
40836 if (reg.getActivePanel()) {
40837 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40838 reg.setActivePanel(reg.getActivePanel());
40841 if (!reg.panels.length) {
40844 reg.showPanel(reg.getPanel(0));
40853 * Returns the nested BorderLayout for this panel
40854 * @return {Roo.BorderLayout}
40856 getLayout : function(){
40857 return this.layout;
40861 * Adds a xtype elements to the layout of the nested panel
40865 xtype : 'ContentPanel',
40872 xtype : 'NestedLayoutPanel',
40878 items : [ ... list of content panels or nested layout panels.. ]
40882 * @param {Object} cfg Xtype definition of item to add.
40884 addxtype : function(cfg) {
40885 return this.layout.addxtype(cfg);
40890 * Ext JS Library 1.1.1
40891 * Copyright(c) 2006-2007, Ext JS, LLC.
40893 * Originally Released Under LGPL - original licence link has changed is not relivant.
40896 * <script type="text/javascript">
40899 * @class Roo.TabPanel
40900 * @extends Roo.util.Observable
40901 * A lightweight tab container.
40905 // basic tabs 1, built from existing content
40906 var tabs = new Roo.TabPanel("tabs1");
40907 tabs.addTab("script", "View Script");
40908 tabs.addTab("markup", "View Markup");
40909 tabs.activate("script");
40911 // more advanced tabs, built from javascript
40912 var jtabs = new Roo.TabPanel("jtabs");
40913 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40915 // set up the UpdateManager
40916 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40917 var updater = tab2.getUpdateManager();
40918 updater.setDefaultUrl("ajax1.htm");
40919 tab2.on('activate', updater.refresh, updater, true);
40921 // Use setUrl for Ajax loading
40922 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40923 tab3.setUrl("ajax2.htm", null, true);
40926 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40929 jtabs.activate("jtabs-1");
40932 * Create a new TabPanel.
40933 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40934 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40936 Roo.bootstrap.panel.Tabs = function(config){
40938 * The container element for this TabPanel.
40939 * @type Roo.Element
40941 this.el = Roo.get(config.el);
40944 if(typeof config == "boolean"){
40945 this.tabPosition = config ? "bottom" : "top";
40947 Roo.apply(this, config);
40951 if(this.tabPosition == "bottom"){
40952 // if tabs are at the bottom = create the body first.
40953 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40954 this.el.addClass("roo-tabs-bottom");
40956 // next create the tabs holders
40958 if (this.tabPosition == "west"){
40960 var reg = this.region; // fake it..
40962 if (!reg.mgr.parent) {
40965 reg = reg.mgr.parent.region;
40967 Roo.log("got nest?");
40969 if (reg.mgr.getRegion('west')) {
40970 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40971 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40972 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40973 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40974 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40982 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40983 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40984 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40985 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40990 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40993 // finally - if tabs are at the top, then create the body last..
40994 if(this.tabPosition != "bottom"){
40995 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40996 * @type Roo.Element
40998 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40999 this.el.addClass("roo-tabs-top");
41003 this.bodyEl.setStyle("position", "relative");
41005 this.active = null;
41006 this.activateDelegate = this.activate.createDelegate(this);
41011 * Fires when the active tab changes
41012 * @param {Roo.TabPanel} this
41013 * @param {Roo.TabPanelItem} activePanel The new active tab
41017 * @event beforetabchange
41018 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41019 * @param {Roo.TabPanel} this
41020 * @param {Object} e Set cancel to true on this object to cancel the tab change
41021 * @param {Roo.TabPanelItem} tab The tab being changed to
41023 "beforetabchange" : true
41026 Roo.EventManager.onWindowResize(this.onResize, this);
41027 this.cpad = this.el.getPadding("lr");
41028 this.hiddenCount = 0;
41031 // toolbar on the tabbar support...
41032 if (this.toolbar) {
41033 alert("no toolbar support yet");
41034 this.toolbar = false;
41036 var tcfg = this.toolbar;
41037 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41038 this.toolbar = new Roo.Toolbar(tcfg);
41039 if (Roo.isSafari) {
41040 var tbl = tcfg.container.child('table', true);
41041 tbl.setAttribute('width', '100%');
41049 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41052 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41054 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41056 tabPosition : "top",
41058 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41060 currentTabWidth : 0,
41062 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41066 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41070 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41072 preferredTabWidth : 175,
41074 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41076 resizeTabs : false,
41078 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41080 monitorResize : true,
41082 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41084 toolbar : false, // set by caller..
41086 region : false, /// set by caller
41088 disableTooltips : true, // not used yet...
41091 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41092 * @param {String} id The id of the div to use <b>or create</b>
41093 * @param {String} text The text for the tab
41094 * @param {String} content (optional) Content to put in the TabPanelItem body
41095 * @param {Boolean} closable (optional) True to create a close icon on the tab
41096 * @return {Roo.TabPanelItem} The created TabPanelItem
41098 addTab : function(id, text, content, closable, tpl)
41100 var item = new Roo.bootstrap.panel.TabItem({
41104 closable : closable,
41107 this.addTabItem(item);
41109 item.setContent(content);
41115 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41116 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41117 * @return {Roo.TabPanelItem}
41119 getTab : function(id){
41120 return this.items[id];
41124 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41125 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41127 hideTab : function(id){
41128 var t = this.items[id];
41131 this.hiddenCount++;
41132 this.autoSizeTabs();
41137 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41138 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41140 unhideTab : function(id){
41141 var t = this.items[id];
41143 t.setHidden(false);
41144 this.hiddenCount--;
41145 this.autoSizeTabs();
41150 * Adds an existing {@link Roo.TabPanelItem}.
41151 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41153 addTabItem : function(item)
41155 this.items[item.id] = item;
41156 this.items.push(item);
41157 this.autoSizeTabs();
41158 // if(this.resizeTabs){
41159 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41160 // this.autoSizeTabs();
41162 // item.autoSize();
41167 * Removes a {@link Roo.TabPanelItem}.
41168 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41170 removeTab : function(id){
41171 var items = this.items;
41172 var tab = items[id];
41173 if(!tab) { return; }
41174 var index = items.indexOf(tab);
41175 if(this.active == tab && items.length > 1){
41176 var newTab = this.getNextAvailable(index);
41181 this.stripEl.dom.removeChild(tab.pnode.dom);
41182 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41183 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41185 items.splice(index, 1);
41186 delete this.items[tab.id];
41187 tab.fireEvent("close", tab);
41188 tab.purgeListeners();
41189 this.autoSizeTabs();
41192 getNextAvailable : function(start){
41193 var items = this.items;
41195 // look for a next tab that will slide over to
41196 // replace the one being removed
41197 while(index < items.length){
41198 var item = items[++index];
41199 if(item && !item.isHidden()){
41203 // if one isn't found select the previous tab (on the left)
41206 var item = items[--index];
41207 if(item && !item.isHidden()){
41215 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41216 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41218 disableTab : function(id){
41219 var tab = this.items[id];
41220 if(tab && this.active != tab){
41226 * Enables a {@link Roo.TabPanelItem} that is disabled.
41227 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41229 enableTab : function(id){
41230 var tab = this.items[id];
41235 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41236 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41237 * @return {Roo.TabPanelItem} The TabPanelItem.
41239 activate : function(id)
41241 //Roo.log('activite:' + id);
41243 var tab = this.items[id];
41247 if(tab == this.active || tab.disabled){
41251 this.fireEvent("beforetabchange", this, e, tab);
41252 if(e.cancel !== true && !tab.disabled){
41254 this.active.hide();
41256 this.active = this.items[id];
41257 this.active.show();
41258 this.fireEvent("tabchange", this, this.active);
41264 * Gets the active {@link Roo.TabPanelItem}.
41265 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41267 getActiveTab : function(){
41268 return this.active;
41272 * Updates the tab body element to fit the height of the container element
41273 * for overflow scrolling
41274 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41276 syncHeight : function(targetHeight){
41277 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41278 var bm = this.bodyEl.getMargins();
41279 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41280 this.bodyEl.setHeight(newHeight);
41284 onResize : function(){
41285 if(this.monitorResize){
41286 this.autoSizeTabs();
41291 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41293 beginUpdate : function(){
41294 this.updating = true;
41298 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41300 endUpdate : function(){
41301 this.updating = false;
41302 this.autoSizeTabs();
41306 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41308 autoSizeTabs : function()
41310 var count = this.items.length;
41311 var vcount = count - this.hiddenCount;
41314 this.stripEl.hide();
41316 this.stripEl.show();
41319 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41324 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41325 var availWidth = Math.floor(w / vcount);
41326 var b = this.stripBody;
41327 if(b.getWidth() > w){
41328 var tabs = this.items;
41329 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41330 if(availWidth < this.minTabWidth){
41331 /*if(!this.sleft){ // incomplete scrolling code
41332 this.createScrollButtons();
41335 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41338 if(this.currentTabWidth < this.preferredTabWidth){
41339 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41345 * Returns the number of tabs in this TabPanel.
41348 getCount : function(){
41349 return this.items.length;
41353 * Resizes all the tabs to the passed width
41354 * @param {Number} The new width
41356 setTabWidth : function(width){
41357 this.currentTabWidth = width;
41358 for(var i = 0, len = this.items.length; i < len; i++) {
41359 if(!this.items[i].isHidden()) {
41360 this.items[i].setWidth(width);
41366 * Destroys this TabPanel
41367 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41369 destroy : function(removeEl){
41370 Roo.EventManager.removeResizeListener(this.onResize, this);
41371 for(var i = 0, len = this.items.length; i < len; i++){
41372 this.items[i].purgeListeners();
41374 if(removeEl === true){
41375 this.el.update("");
41380 createStrip : function(container)
41382 var strip = document.createElement("nav");
41383 strip.className = Roo.bootstrap.version == 4 ?
41384 "navbar-light bg-light" :
41385 "navbar navbar-default"; //"x-tabs-wrap";
41386 container.appendChild(strip);
41390 createStripList : function(strip)
41392 // div wrapper for retard IE
41393 // returns the "tr" element.
41394 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41395 //'<div class="x-tabs-strip-wrap">'+
41396 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41397 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41398 return strip.firstChild; //.firstChild.firstChild.firstChild;
41400 createBody : function(container)
41402 var body = document.createElement("div");
41403 Roo.id(body, "tab-body");
41404 //Roo.fly(body).addClass("x-tabs-body");
41405 Roo.fly(body).addClass("tab-content");
41406 container.appendChild(body);
41409 createItemBody :function(bodyEl, id){
41410 var body = Roo.getDom(id);
41412 body = document.createElement("div");
41415 //Roo.fly(body).addClass("x-tabs-item-body");
41416 Roo.fly(body).addClass("tab-pane");
41417 bodyEl.insertBefore(body, bodyEl.firstChild);
41421 createStripElements : function(stripEl, text, closable, tpl)
41423 var td = document.createElement("li"); // was td..
41424 td.className = 'nav-item';
41426 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41429 stripEl.appendChild(td);
41431 td.className = "x-tabs-closable";
41432 if(!this.closeTpl){
41433 this.closeTpl = new Roo.Template(
41434 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41435 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41436 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41439 var el = this.closeTpl.overwrite(td, {"text": text});
41440 var close = el.getElementsByTagName("div")[0];
41441 var inner = el.getElementsByTagName("em")[0];
41442 return {"el": el, "close": close, "inner": inner};
41445 // not sure what this is..
41446 // if(!this.tabTpl){
41447 //this.tabTpl = new Roo.Template(
41448 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41449 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41451 // this.tabTpl = new Roo.Template(
41452 // '<a href="#">' +
41453 // '<span unselectable="on"' +
41454 // (this.disableTooltips ? '' : ' title="{text}"') +
41455 // ' >{text}</span></a>'
41461 var template = tpl || this.tabTpl || false;
41464 template = new Roo.Template(
41465 Roo.bootstrap.version == 4 ?
41467 '<a class="nav-link" href="#" unselectable="on"' +
41468 (this.disableTooltips ? '' : ' title="{text}"') +
41471 '<a class="nav-link" href="#">' +
41472 '<span unselectable="on"' +
41473 (this.disableTooltips ? '' : ' title="{text}"') +
41474 ' >{text}</span></a>'
41479 switch (typeof(template)) {
41483 template = new Roo.Template(template);
41489 var el = template.overwrite(td, {"text": text});
41491 var inner = el.getElementsByTagName("span")[0];
41493 return {"el": el, "inner": inner};
41501 * @class Roo.TabPanelItem
41502 * @extends Roo.util.Observable
41503 * Represents an individual item (tab plus body) in a TabPanel.
41504 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41505 * @param {String} id The id of this TabPanelItem
41506 * @param {String} text The text for the tab of this TabPanelItem
41507 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41509 Roo.bootstrap.panel.TabItem = function(config){
41511 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41512 * @type Roo.TabPanel
41514 this.tabPanel = config.panel;
41516 * The id for this TabPanelItem
41519 this.id = config.id;
41521 this.disabled = false;
41523 this.text = config.text;
41525 this.loaded = false;
41526 this.closable = config.closable;
41529 * The body element for this TabPanelItem.
41530 * @type Roo.Element
41532 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41533 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41534 this.bodyEl.setStyle("display", "block");
41535 this.bodyEl.setStyle("zoom", "1");
41536 //this.hideAction();
41538 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41540 this.el = Roo.get(els.el);
41541 this.inner = Roo.get(els.inner, true);
41542 this.textEl = Roo.bootstrap.version == 4 ?
41543 this.el : Roo.get(this.el.dom.firstChild, true);
41545 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41546 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41549 // this.el.on("mousedown", this.onTabMouseDown, this);
41550 this.el.on("click", this.onTabClick, this);
41552 if(config.closable){
41553 var c = Roo.get(els.close, true);
41554 c.dom.title = this.closeText;
41555 c.addClassOnOver("close-over");
41556 c.on("click", this.closeClick, this);
41562 * Fires when this tab becomes the active tab.
41563 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41564 * @param {Roo.TabPanelItem} this
41568 * @event beforeclose
41569 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41570 * @param {Roo.TabPanelItem} this
41571 * @param {Object} e Set cancel to true on this object to cancel the close.
41573 "beforeclose": true,
41576 * Fires when this tab is closed.
41577 * @param {Roo.TabPanelItem} this
41581 * @event deactivate
41582 * Fires when this tab is no longer the active tab.
41583 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41584 * @param {Roo.TabPanelItem} this
41586 "deactivate" : true
41588 this.hidden = false;
41590 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41593 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41595 purgeListeners : function(){
41596 Roo.util.Observable.prototype.purgeListeners.call(this);
41597 this.el.removeAllListeners();
41600 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41603 this.status_node.addClass("active");
41606 this.tabPanel.stripWrap.repaint();
41608 this.fireEvent("activate", this.tabPanel, this);
41612 * Returns true if this tab is the active tab.
41613 * @return {Boolean}
41615 isActive : function(){
41616 return this.tabPanel.getActiveTab() == this;
41620 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41623 this.status_node.removeClass("active");
41625 this.fireEvent("deactivate", this.tabPanel, this);
41628 hideAction : function(){
41629 this.bodyEl.hide();
41630 this.bodyEl.setStyle("position", "absolute");
41631 this.bodyEl.setLeft("-20000px");
41632 this.bodyEl.setTop("-20000px");
41635 showAction : function(){
41636 this.bodyEl.setStyle("position", "relative");
41637 this.bodyEl.setTop("");
41638 this.bodyEl.setLeft("");
41639 this.bodyEl.show();
41643 * Set the tooltip for the tab.
41644 * @param {String} tooltip The tab's tooltip
41646 setTooltip : function(text){
41647 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41648 this.textEl.dom.qtip = text;
41649 this.textEl.dom.removeAttribute('title');
41651 this.textEl.dom.title = text;
41655 onTabClick : function(e){
41656 e.preventDefault();
41657 this.tabPanel.activate(this.id);
41660 onTabMouseDown : function(e){
41661 e.preventDefault();
41662 this.tabPanel.activate(this.id);
41665 getWidth : function(){
41666 return this.inner.getWidth();
41669 setWidth : function(width){
41670 var iwidth = width - this.linode.getPadding("lr");
41671 this.inner.setWidth(iwidth);
41672 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41673 this.linode.setWidth(width);
41677 * Show or hide the tab
41678 * @param {Boolean} hidden True to hide or false to show.
41680 setHidden : function(hidden){
41681 this.hidden = hidden;
41682 this.linode.setStyle("display", hidden ? "none" : "");
41686 * Returns true if this tab is "hidden"
41687 * @return {Boolean}
41689 isHidden : function(){
41690 return this.hidden;
41694 * Returns the text for this tab
41697 getText : function(){
41701 autoSize : function(){
41702 //this.el.beginMeasure();
41703 this.textEl.setWidth(1);
41705 * #2804 [new] Tabs in Roojs
41706 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41708 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41709 //this.el.endMeasure();
41713 * Sets the text for the tab (Note: this also sets the tooltip text)
41714 * @param {String} text The tab's text and tooltip
41716 setText : function(text){
41718 this.textEl.update(text);
41719 this.setTooltip(text);
41720 //if(!this.tabPanel.resizeTabs){
41721 // this.autoSize();
41725 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41727 activate : function(){
41728 this.tabPanel.activate(this.id);
41732 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41734 disable : function(){
41735 if(this.tabPanel.active != this){
41736 this.disabled = true;
41737 this.status_node.addClass("disabled");
41742 * Enables this TabPanelItem if it was previously disabled.
41744 enable : function(){
41745 this.disabled = false;
41746 this.status_node.removeClass("disabled");
41750 * Sets the content for this TabPanelItem.
41751 * @param {String} content The content
41752 * @param {Boolean} loadScripts true to look for and load scripts
41754 setContent : function(content, loadScripts){
41755 this.bodyEl.update(content, loadScripts);
41759 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41760 * @return {Roo.UpdateManager} The UpdateManager
41762 getUpdateManager : function(){
41763 return this.bodyEl.getUpdateManager();
41767 * Set a URL to be used to load the content for this TabPanelItem.
41768 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41769 * @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)
41770 * @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)
41771 * @return {Roo.UpdateManager} The UpdateManager
41773 setUrl : function(url, params, loadOnce){
41774 if(this.refreshDelegate){
41775 this.un('activate', this.refreshDelegate);
41777 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41778 this.on("activate", this.refreshDelegate);
41779 return this.bodyEl.getUpdateManager();
41783 _handleRefresh : function(url, params, loadOnce){
41784 if(!loadOnce || !this.loaded){
41785 var updater = this.bodyEl.getUpdateManager();
41786 updater.update(url, params, this._setLoaded.createDelegate(this));
41791 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41792 * Will fail silently if the setUrl method has not been called.
41793 * This does not activate the panel, just updates its content.
41795 refresh : function(){
41796 if(this.refreshDelegate){
41797 this.loaded = false;
41798 this.refreshDelegate();
41803 _setLoaded : function(){
41804 this.loaded = true;
41808 closeClick : function(e){
41811 this.fireEvent("beforeclose", this, o);
41812 if(o.cancel !== true){
41813 this.tabPanel.removeTab(this.id);
41817 * The text displayed in the tooltip for the close icon.
41820 closeText : "Close this tab"
41823 * This script refer to:
41824 * Title: International Telephone Input
41825 * Author: Jack O'Connor
41826 * Code version: v12.1.12
41827 * Availability: https://github.com/jackocnr/intl-tel-input.git
41830 Roo.bootstrap.PhoneInputData = function() {
41833 "Afghanistan (افغانستان)",
41838 "Albania (Shqipëri)",
41843 "Algeria (الجزائر)",
41868 "Antigua and Barbuda",
41878 "Armenia (Հայաստան)",
41894 "Austria (Österreich)",
41899 "Azerbaijan (Azərbaycan)",
41909 "Bahrain (البحرين)",
41914 "Bangladesh (বাংলাদেশ)",
41924 "Belarus (Беларусь)",
41929 "Belgium (België)",
41959 "Bosnia and Herzegovina (Босна и Херцеговина)",
41974 "British Indian Ocean Territory",
41979 "British Virgin Islands",
41989 "Bulgaria (България)",
41999 "Burundi (Uburundi)",
42004 "Cambodia (កម្ពុជា)",
42009 "Cameroon (Cameroun)",
42018 ["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"]
42021 "Cape Verde (Kabu Verdi)",
42026 "Caribbean Netherlands",
42037 "Central African Republic (République centrafricaine)",
42057 "Christmas Island",
42063 "Cocos (Keeling) Islands",
42074 "Comoros (جزر القمر)",
42079 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42084 "Congo (Republic) (Congo-Brazzaville)",
42104 "Croatia (Hrvatska)",
42125 "Czech Republic (Česká republika)",
42130 "Denmark (Danmark)",
42145 "Dominican Republic (República Dominicana)",
42149 ["809", "829", "849"]
42167 "Equatorial Guinea (Guinea Ecuatorial)",
42187 "Falkland Islands (Islas Malvinas)",
42192 "Faroe Islands (Føroyar)",
42213 "French Guiana (Guyane française)",
42218 "French Polynesia (Polynésie française)",
42233 "Georgia (საქართველო)",
42238 "Germany (Deutschland)",
42258 "Greenland (Kalaallit Nunaat)",
42295 "Guinea-Bissau (Guiné Bissau)",
42320 "Hungary (Magyarország)",
42325 "Iceland (Ísland)",
42345 "Iraq (العراق)",
42361 "Israel (ישראל)",
42388 "Jordan (الأردن)",
42393 "Kazakhstan (Казахстан)",
42414 "Kuwait (الكويت)",
42419 "Kyrgyzstan (Кыргызстан)",
42429 "Latvia (Latvija)",
42434 "Lebanon (لبنان)",
42449 "Libya (ليبيا)",
42459 "Lithuania (Lietuva)",
42474 "Macedonia (FYROM) (Македонија)",
42479 "Madagascar (Madagasikara)",
42509 "Marshall Islands",
42519 "Mauritania (موريتانيا)",
42524 "Mauritius (Moris)",
42545 "Moldova (Republica Moldova)",
42555 "Mongolia (Монгол)",
42560 "Montenegro (Crna Gora)",
42570 "Morocco (المغرب)",
42576 "Mozambique (Moçambique)",
42581 "Myanmar (Burma) (မြန်မာ)",
42586 "Namibia (Namibië)",
42601 "Netherlands (Nederland)",
42606 "New Caledonia (Nouvelle-Calédonie)",
42641 "North Korea (조선 민주주의 인민 공화국)",
42646 "Northern Mariana Islands",
42662 "Pakistan (پاکستان)",
42672 "Palestine (فلسطين)",
42682 "Papua New Guinea",
42724 "Réunion (La Réunion)",
42730 "Romania (România)",
42746 "Saint Barthélemy",
42757 "Saint Kitts and Nevis",
42767 "Saint Martin (Saint-Martin (partie française))",
42773 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42778 "Saint Vincent and the Grenadines",
42793 "São Tomé and Príncipe (São Tomé e Príncipe)",
42798 "Saudi Arabia (المملكة العربية السعودية)",
42803 "Senegal (Sénégal)",
42833 "Slovakia (Slovensko)",
42838 "Slovenia (Slovenija)",
42848 "Somalia (Soomaaliya)",
42858 "South Korea (대한민국)",
42863 "South Sudan (جنوب السودان)",
42873 "Sri Lanka (ශ්රී ලංකාව)",
42878 "Sudan (السودان)",
42888 "Svalbard and Jan Mayen",
42899 "Sweden (Sverige)",
42904 "Switzerland (Schweiz)",
42909 "Syria (سوريا)",
42954 "Trinidad and Tobago",
42959 "Tunisia (تونس)",
42964 "Turkey (Türkiye)",
42974 "Turks and Caicos Islands",
42984 "U.S. Virgin Islands",
42994 "Ukraine (Україна)",
42999 "United Arab Emirates (الإمارات العربية المتحدة)",
43021 "Uzbekistan (Oʻzbekiston)",
43031 "Vatican City (Città del Vaticano)",
43042 "Vietnam (Việt Nam)",
43047 "Wallis and Futuna (Wallis-et-Futuna)",
43052 "Western Sahara (الصحراء الغربية)",
43058 "Yemen (اليمن)",
43082 * This script refer to:
43083 * Title: International Telephone Input
43084 * Author: Jack O'Connor
43085 * Code version: v12.1.12
43086 * Availability: https://github.com/jackocnr/intl-tel-input.git
43090 * @class Roo.bootstrap.PhoneInput
43091 * @extends Roo.bootstrap.TriggerField
43092 * An input with International dial-code selection
43094 * @cfg {String} defaultDialCode default '+852'
43095 * @cfg {Array} preferedCountries default []
43098 * Create a new PhoneInput.
43099 * @param {Object} config Configuration options
43102 Roo.bootstrap.PhoneInput = function(config) {
43103 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43106 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43108 listWidth: undefined,
43110 selectedClass: 'active',
43112 invalidClass : "has-warning",
43114 validClass: 'has-success',
43116 allowed: '0123456789',
43121 * @cfg {String} defaultDialCode The default dial code when initializing the input
43123 defaultDialCode: '+852',
43126 * @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
43128 preferedCountries: false,
43130 getAutoCreate : function()
43132 var data = Roo.bootstrap.PhoneInputData();
43133 var align = this.labelAlign || this.parentLabelAlign();
43136 this.allCountries = [];
43137 this.dialCodeMapping = [];
43139 for (var i = 0; i < data.length; i++) {
43141 this.allCountries[i] = {
43145 priority: c[3] || 0,
43146 areaCodes: c[4] || null
43148 this.dialCodeMapping[c[2]] = {
43151 priority: c[3] || 0,
43152 areaCodes: c[4] || null
43164 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43165 maxlength: this.max_length,
43166 cls : 'form-control tel-input',
43167 autocomplete: 'new-password'
43170 var hiddenInput = {
43173 cls: 'hidden-tel-input'
43177 hiddenInput.name = this.name;
43180 if (this.disabled) {
43181 input.disabled = true;
43184 var flag_container = {
43201 cls: this.hasFeedback ? 'has-feedback' : '',
43207 cls: 'dial-code-holder',
43214 cls: 'roo-select2-container input-group',
43221 if (this.fieldLabel.length) {
43224 tooltip: 'This field is required'
43230 cls: 'control-label',
43236 html: this.fieldLabel
43239 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43245 if(this.indicatorpos == 'right') {
43246 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43253 if(align == 'left') {
43261 if(this.labelWidth > 12){
43262 label.style = "width: " + this.labelWidth + 'px';
43264 if(this.labelWidth < 13 && this.labelmd == 0){
43265 this.labelmd = this.labelWidth;
43267 if(this.labellg > 0){
43268 label.cls += ' col-lg-' + this.labellg;
43269 input.cls += ' col-lg-' + (12 - this.labellg);
43271 if(this.labelmd > 0){
43272 label.cls += ' col-md-' + this.labelmd;
43273 container.cls += ' col-md-' + (12 - this.labelmd);
43275 if(this.labelsm > 0){
43276 label.cls += ' col-sm-' + this.labelsm;
43277 container.cls += ' col-sm-' + (12 - this.labelsm);
43279 if(this.labelxs > 0){
43280 label.cls += ' col-xs-' + this.labelxs;
43281 container.cls += ' col-xs-' + (12 - this.labelxs);
43291 var settings = this;
43293 ['xs','sm','md','lg'].map(function(size){
43294 if (settings[size]) {
43295 cfg.cls += ' col-' + size + '-' + settings[size];
43299 this.store = new Roo.data.Store({
43300 proxy : new Roo.data.MemoryProxy({}),
43301 reader : new Roo.data.JsonReader({
43312 'name' : 'dialCode',
43316 'name' : 'priority',
43320 'name' : 'areaCodes',
43327 if(!this.preferedCountries) {
43328 this.preferedCountries = [
43335 var p = this.preferedCountries.reverse();
43338 for (var i = 0; i < p.length; i++) {
43339 for (var j = 0; j < this.allCountries.length; j++) {
43340 if(this.allCountries[j].iso2 == p[i]) {
43341 var t = this.allCountries[j];
43342 this.allCountries.splice(j,1);
43343 this.allCountries.unshift(t);
43349 this.store.proxy.data = {
43351 data: this.allCountries
43357 initEvents : function()
43360 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43362 this.indicator = this.indicatorEl();
43363 this.flag = this.flagEl();
43364 this.dialCodeHolder = this.dialCodeHolderEl();
43366 this.trigger = this.el.select('div.flag-box',true).first();
43367 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43372 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43373 _this.list.setWidth(lw);
43376 this.list.on('mouseover', this.onViewOver, this);
43377 this.list.on('mousemove', this.onViewMove, this);
43378 this.inputEl().on("keyup", this.onKeyUp, this);
43379 this.inputEl().on("keypress", this.onKeyPress, this);
43381 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43383 this.view = new Roo.View(this.list, this.tpl, {
43384 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43387 this.view.on('click', this.onViewClick, this);
43388 this.setValue(this.defaultDialCode);
43391 onTriggerClick : function(e)
43393 Roo.log('trigger click');
43398 if(this.isExpanded()){
43400 this.hasFocus = false;
43402 this.store.load({});
43403 this.hasFocus = true;
43408 isExpanded : function()
43410 return this.list.isVisible();
43413 collapse : function()
43415 if(!this.isExpanded()){
43419 Roo.get(document).un('mousedown', this.collapseIf, this);
43420 Roo.get(document).un('mousewheel', this.collapseIf, this);
43421 this.fireEvent('collapse', this);
43425 expand : function()
43429 if(this.isExpanded() || !this.hasFocus){
43433 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43434 this.list.setWidth(lw);
43437 this.restrictHeight();
43439 Roo.get(document).on('mousedown', this.collapseIf, this);
43440 Roo.get(document).on('mousewheel', this.collapseIf, this);
43442 this.fireEvent('expand', this);
43445 restrictHeight : function()
43447 this.list.alignTo(this.inputEl(), this.listAlign);
43448 this.list.alignTo(this.inputEl(), this.listAlign);
43451 onViewOver : function(e, t)
43453 if(this.inKeyMode){
43456 var item = this.view.findItemFromChild(t);
43459 var index = this.view.indexOf(item);
43460 this.select(index, false);
43465 onViewClick : function(view, doFocus, el, e)
43467 var index = this.view.getSelectedIndexes()[0];
43469 var r = this.store.getAt(index);
43472 this.onSelect(r, index);
43474 if(doFocus !== false && !this.blockFocus){
43475 this.inputEl().focus();
43479 onViewMove : function(e, t)
43481 this.inKeyMode = false;
43484 select : function(index, scrollIntoView)
43486 this.selectedIndex = index;
43487 this.view.select(index);
43488 if(scrollIntoView !== false){
43489 var el = this.view.getNode(index);
43491 this.list.scrollChildIntoView(el, false);
43496 createList : function()
43498 this.list = Roo.get(document.body).createChild({
43500 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43501 style: 'display:none'
43504 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43507 collapseIf : function(e)
43509 var in_combo = e.within(this.el);
43510 var in_list = e.within(this.list);
43511 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43513 if (in_combo || in_list || is_list) {
43519 onSelect : function(record, index)
43521 if(this.fireEvent('beforeselect', this, record, index) !== false){
43523 this.setFlagClass(record.data.iso2);
43524 this.setDialCode(record.data.dialCode);
43525 this.hasFocus = false;
43527 this.fireEvent('select', this, record, index);
43531 flagEl : function()
43533 var flag = this.el.select('div.flag',true).first();
43540 dialCodeHolderEl : function()
43542 var d = this.el.select('input.dial-code-holder',true).first();
43549 setDialCode : function(v)
43551 this.dialCodeHolder.dom.value = '+'+v;
43554 setFlagClass : function(n)
43556 this.flag.dom.className = 'flag '+n;
43559 getValue : function()
43561 var v = this.inputEl().getValue();
43562 if(this.dialCodeHolder) {
43563 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43568 setValue : function(v)
43570 var d = this.getDialCode(v);
43572 //invalid dial code
43573 if(v.length == 0 || !d || d.length == 0) {
43575 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43576 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43582 this.setFlagClass(this.dialCodeMapping[d].iso2);
43583 this.setDialCode(d);
43584 this.inputEl().dom.value = v.replace('+'+d,'');
43585 this.hiddenEl().dom.value = this.getValue();
43590 getDialCode : function(v)
43594 if (v.length == 0) {
43595 return this.dialCodeHolder.dom.value;
43599 if (v.charAt(0) != "+") {
43602 var numericChars = "";
43603 for (var i = 1; i < v.length; i++) {
43604 var c = v.charAt(i);
43607 if (this.dialCodeMapping[numericChars]) {
43608 dialCode = v.substr(1, i);
43610 if (numericChars.length == 4) {
43620 this.setValue(this.defaultDialCode);
43624 hiddenEl : function()
43626 return this.el.select('input.hidden-tel-input',true).first();
43629 // after setting val
43630 onKeyUp : function(e){
43631 this.setValue(this.getValue());
43634 onKeyPress : function(e){
43635 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43642 * @class Roo.bootstrap.MoneyField
43643 * @extends Roo.bootstrap.ComboBox
43644 * Bootstrap MoneyField class
43647 * Create a new MoneyField.
43648 * @param {Object} config Configuration options
43651 Roo.bootstrap.MoneyField = function(config) {
43653 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43657 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43660 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43662 allowDecimals : true,
43664 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43666 decimalSeparator : ".",
43668 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43670 decimalPrecision : 0,
43672 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43674 allowNegative : true,
43676 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43680 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43682 minValue : Number.NEGATIVE_INFINITY,
43684 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43686 maxValue : Number.MAX_VALUE,
43688 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43690 minText : "The minimum value for this field is {0}",
43692 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43694 maxText : "The maximum value for this field is {0}",
43696 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43697 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43699 nanText : "{0} is not a valid number",
43701 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43705 * @cfg {String} defaults currency of the MoneyField
43706 * value should be in lkey
43708 defaultCurrency : false,
43710 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43712 thousandsDelimiter : false,
43714 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43725 getAutoCreate : function()
43727 var align = this.labelAlign || this.parentLabelAlign();
43739 cls : 'form-control roo-money-amount-input',
43740 autocomplete: 'new-password'
43743 var hiddenInput = {
43747 cls: 'hidden-number-input'
43750 if(this.max_length) {
43751 input.maxlength = this.max_length;
43755 hiddenInput.name = this.name;
43758 if (this.disabled) {
43759 input.disabled = true;
43762 var clg = 12 - this.inputlg;
43763 var cmd = 12 - this.inputmd;
43764 var csm = 12 - this.inputsm;
43765 var cxs = 12 - this.inputxs;
43769 cls : 'row roo-money-field',
43773 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43777 cls: 'roo-select2-container input-group',
43781 cls : 'form-control roo-money-currency-input',
43782 autocomplete: 'new-password',
43784 name : this.currencyName
43788 cls : 'input-group-addon',
43802 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43806 cls: this.hasFeedback ? 'has-feedback' : '',
43817 if (this.fieldLabel.length) {
43820 tooltip: 'This field is required'
43826 cls: 'control-label',
43832 html: this.fieldLabel
43835 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43841 if(this.indicatorpos == 'right') {
43842 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43849 if(align == 'left') {
43857 if(this.labelWidth > 12){
43858 label.style = "width: " + this.labelWidth + 'px';
43860 if(this.labelWidth < 13 && this.labelmd == 0){
43861 this.labelmd = this.labelWidth;
43863 if(this.labellg > 0){
43864 label.cls += ' col-lg-' + this.labellg;
43865 input.cls += ' col-lg-' + (12 - this.labellg);
43867 if(this.labelmd > 0){
43868 label.cls += ' col-md-' + this.labelmd;
43869 container.cls += ' col-md-' + (12 - this.labelmd);
43871 if(this.labelsm > 0){
43872 label.cls += ' col-sm-' + this.labelsm;
43873 container.cls += ' col-sm-' + (12 - this.labelsm);
43875 if(this.labelxs > 0){
43876 label.cls += ' col-xs-' + this.labelxs;
43877 container.cls += ' col-xs-' + (12 - this.labelxs);
43888 var settings = this;
43890 ['xs','sm','md','lg'].map(function(size){
43891 if (settings[size]) {
43892 cfg.cls += ' col-' + size + '-' + settings[size];
43899 initEvents : function()
43901 this.indicator = this.indicatorEl();
43903 this.initCurrencyEvent();
43905 this.initNumberEvent();
43908 initCurrencyEvent : function()
43911 throw "can not find store for combo";
43914 this.store = Roo.factory(this.store, Roo.data);
43915 this.store.parent = this;
43919 this.triggerEl = this.el.select('.input-group-addon', true).first();
43921 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43926 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43927 _this.list.setWidth(lw);
43930 this.list.on('mouseover', this.onViewOver, this);
43931 this.list.on('mousemove', this.onViewMove, this);
43932 this.list.on('scroll', this.onViewScroll, this);
43935 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43938 this.view = new Roo.View(this.list, this.tpl, {
43939 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43942 this.view.on('click', this.onViewClick, this);
43944 this.store.on('beforeload', this.onBeforeLoad, this);
43945 this.store.on('load', this.onLoad, this);
43946 this.store.on('loadexception', this.onLoadException, this);
43948 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43949 "up" : function(e){
43950 this.inKeyMode = true;
43954 "down" : function(e){
43955 if(!this.isExpanded()){
43956 this.onTriggerClick();
43958 this.inKeyMode = true;
43963 "enter" : function(e){
43966 if(this.fireEvent("specialkey", this, e)){
43967 this.onViewClick(false);
43973 "esc" : function(e){
43977 "tab" : function(e){
43980 if(this.fireEvent("specialkey", this, e)){
43981 this.onViewClick(false);
43989 doRelay : function(foo, bar, hname){
43990 if(hname == 'down' || this.scope.isExpanded()){
43991 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43999 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44003 initNumberEvent : function(e)
44005 this.inputEl().on("keydown" , this.fireKey, this);
44006 this.inputEl().on("focus", this.onFocus, this);
44007 this.inputEl().on("blur", this.onBlur, this);
44009 this.inputEl().relayEvent('keyup', this);
44011 if(this.indicator){
44012 this.indicator.addClass('invisible');
44015 this.originalValue = this.getValue();
44017 if(this.validationEvent == 'keyup'){
44018 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44019 this.inputEl().on('keyup', this.filterValidation, this);
44021 else if(this.validationEvent !== false){
44022 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44025 if(this.selectOnFocus){
44026 this.on("focus", this.preFocus, this);
44029 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44030 this.inputEl().on("keypress", this.filterKeys, this);
44032 this.inputEl().relayEvent('keypress', this);
44035 var allowed = "0123456789";
44037 if(this.allowDecimals){
44038 allowed += this.decimalSeparator;
44041 if(this.allowNegative){
44045 if(this.thousandsDelimiter) {
44049 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44051 var keyPress = function(e){
44053 var k = e.getKey();
44055 var c = e.getCharCode();
44058 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44059 allowed.indexOf(String.fromCharCode(c)) === -1
44065 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44069 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44074 this.inputEl().on("keypress", keyPress, this);
44078 onTriggerClick : function(e)
44085 this.loadNext = false;
44087 if(this.isExpanded()){
44092 this.hasFocus = true;
44094 if(this.triggerAction == 'all') {
44095 this.doQuery(this.allQuery, true);
44099 this.doQuery(this.getRawValue());
44102 getCurrency : function()
44104 var v = this.currencyEl().getValue();
44109 restrictHeight : function()
44111 this.list.alignTo(this.currencyEl(), this.listAlign);
44112 this.list.alignTo(this.currencyEl(), this.listAlign);
44115 onViewClick : function(view, doFocus, el, e)
44117 var index = this.view.getSelectedIndexes()[0];
44119 var r = this.store.getAt(index);
44122 this.onSelect(r, index);
44126 onSelect : function(record, index){
44128 if(this.fireEvent('beforeselect', this, record, index) !== false){
44130 this.setFromCurrencyData(index > -1 ? record.data : false);
44134 this.fireEvent('select', this, record, index);
44138 setFromCurrencyData : function(o)
44142 this.lastCurrency = o;
44144 if (this.currencyField) {
44145 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44147 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44150 this.lastSelectionText = currency;
44152 //setting default currency
44153 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44154 this.setCurrency(this.defaultCurrency);
44158 this.setCurrency(currency);
44161 setFromData : function(o)
44165 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44167 this.setFromCurrencyData(c);
44172 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44174 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44177 this.setValue(value);
44181 setCurrency : function(v)
44183 this.currencyValue = v;
44186 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44191 setValue : function(v)
44193 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44199 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44201 this.inputEl().dom.value = (v == '') ? '' :
44202 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44204 if(!this.allowZero && v === '0') {
44205 this.hiddenEl().dom.value = '';
44206 this.inputEl().dom.value = '';
44213 getRawValue : function()
44215 var v = this.inputEl().getValue();
44220 getValue : function()
44222 return this.fixPrecision(this.parseValue(this.getRawValue()));
44225 parseValue : function(value)
44227 if(this.thousandsDelimiter) {
44229 r = new RegExp(",", "g");
44230 value = value.replace(r, "");
44233 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44234 return isNaN(value) ? '' : value;
44238 fixPrecision : function(value)
44240 if(this.thousandsDelimiter) {
44242 r = new RegExp(",", "g");
44243 value = value.replace(r, "");
44246 var nan = isNaN(value);
44248 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44249 return nan ? '' : value;
44251 return parseFloat(value).toFixed(this.decimalPrecision);
44254 decimalPrecisionFcn : function(v)
44256 return Math.floor(v);
44259 validateValue : function(value)
44261 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44265 var num = this.parseValue(value);
44268 this.markInvalid(String.format(this.nanText, value));
44272 if(num < this.minValue){
44273 this.markInvalid(String.format(this.minText, this.minValue));
44277 if(num > this.maxValue){
44278 this.markInvalid(String.format(this.maxText, this.maxValue));
44285 validate : function()
44287 if(this.disabled || this.allowBlank){
44292 var currency = this.getCurrency();
44294 if(this.validateValue(this.getRawValue()) && currency.length){
44299 this.markInvalid();
44303 getName: function()
44308 beforeBlur : function()
44314 var v = this.parseValue(this.getRawValue());
44321 onBlur : function()
44325 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44326 //this.el.removeClass(this.focusClass);
44329 this.hasFocus = false;
44331 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44335 var v = this.getValue();
44337 if(String(v) !== String(this.startValue)){
44338 this.fireEvent('change', this, v, this.startValue);
44341 this.fireEvent("blur", this);
44344 inputEl : function()
44346 return this.el.select('.roo-money-amount-input', true).first();
44349 currencyEl : function()
44351 return this.el.select('.roo-money-currency-input', true).first();
44354 hiddenEl : function()
44356 return this.el.select('input.hidden-number-input',true).first();
44360 * @class Roo.bootstrap.BezierSignature
44361 * @extends Roo.bootstrap.Component
44362 * Bootstrap BezierSignature class
44363 * This script refer to:
44364 * Title: Signature Pad
44366 * Availability: https://github.com/szimek/signature_pad
44369 * Create a new BezierSignature
44370 * @param {Object} config The config object
44373 Roo.bootstrap.BezierSignature = function(config){
44374 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44380 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44387 mouse_btn_down: true,
44390 * @cfg {int} canvas height
44392 canvas_height: '200px',
44395 * @cfg {float|function} Radius of a single dot.
44400 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44405 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44410 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44415 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44420 * @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.
44422 bg_color: 'rgba(0, 0, 0, 0)',
44425 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44427 dot_color: 'black',
44430 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44432 velocity_filter_weight: 0.7,
44435 * @cfg {function} Callback when stroke begin.
44440 * @cfg {function} Callback when stroke end.
44444 getAutoCreate : function()
44446 var cls = 'roo-signature column';
44449 cls += ' ' + this.cls;
44459 for(var i = 0; i < col_sizes.length; i++) {
44460 if(this[col_sizes[i]]) {
44461 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44471 cls: 'roo-signature-body',
44475 cls: 'roo-signature-body-canvas',
44476 height: this.canvas_height,
44477 width: this.canvas_width
44484 style: 'display: none'
44492 initEvents: function()
44494 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44496 var canvas = this.canvasEl();
44498 // mouse && touch event swapping...
44499 canvas.dom.style.touchAction = 'none';
44500 canvas.dom.style.msTouchAction = 'none';
44502 this.mouse_btn_down = false;
44503 canvas.on('mousedown', this._handleMouseDown, this);
44504 canvas.on('mousemove', this._handleMouseMove, this);
44505 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44507 if (window.PointerEvent) {
44508 canvas.on('pointerdown', this._handleMouseDown, this);
44509 canvas.on('pointermove', this._handleMouseMove, this);
44510 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44513 if ('ontouchstart' in window) {
44514 canvas.on('touchstart', this._handleTouchStart, this);
44515 canvas.on('touchmove', this._handleTouchMove, this);
44516 canvas.on('touchend', this._handleTouchEnd, this);
44519 Roo.EventManager.onWindowResize(this.resize, this, true);
44521 // file input event
44522 this.fileEl().on('change', this.uploadImage, this);
44529 resize: function(){
44531 var canvas = this.canvasEl().dom;
44532 var ctx = this.canvasElCtx();
44533 var img_data = false;
44535 if(canvas.width > 0) {
44536 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44538 // setting canvas width will clean img data
44541 var style = window.getComputedStyle ?
44542 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44544 var padding_left = parseInt(style.paddingLeft) || 0;
44545 var padding_right = parseInt(style.paddingRight) || 0;
44547 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44550 ctx.putImageData(img_data, 0, 0);
44554 _handleMouseDown: function(e)
44556 if (e.browserEvent.which === 1) {
44557 this.mouse_btn_down = true;
44558 this.strokeBegin(e);
44562 _handleMouseMove: function (e)
44564 if (this.mouse_btn_down) {
44565 this.strokeMoveUpdate(e);
44569 _handleMouseUp: function (e)
44571 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44572 this.mouse_btn_down = false;
44577 _handleTouchStart: function (e) {
44579 e.preventDefault();
44580 if (e.browserEvent.targetTouches.length === 1) {
44581 // var touch = e.browserEvent.changedTouches[0];
44582 // this.strokeBegin(touch);
44584 this.strokeBegin(e); // assume e catching the correct xy...
44588 _handleTouchMove: function (e) {
44589 e.preventDefault();
44590 // var touch = event.targetTouches[0];
44591 // _this._strokeMoveUpdate(touch);
44592 this.strokeMoveUpdate(e);
44595 _handleTouchEnd: function (e) {
44596 var wasCanvasTouched = e.target === this.canvasEl().dom;
44597 if (wasCanvasTouched) {
44598 e.preventDefault();
44599 // var touch = event.changedTouches[0];
44600 // _this._strokeEnd(touch);
44605 reset: function () {
44606 this._lastPoints = [];
44607 this._lastVelocity = 0;
44608 this._lastWidth = (this.min_width + this.max_width) / 2;
44609 this.canvasElCtx().fillStyle = this.dot_color;
44612 strokeMoveUpdate: function(e)
44614 this.strokeUpdate(e);
44616 if (this.throttle) {
44617 this.throttleStroke(this.strokeUpdate, this.throttle);
44620 this.strokeUpdate(e);
44624 strokeBegin: function(e)
44626 var newPointGroup = {
44627 color: this.dot_color,
44631 if (typeof this.onBegin === 'function') {
44635 this.curve_data.push(newPointGroup);
44637 this.strokeUpdate(e);
44640 strokeUpdate: function(e)
44642 var rect = this.canvasEl().dom.getBoundingClientRect();
44643 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44644 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44645 var lastPoints = lastPointGroup.points;
44646 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44647 var isLastPointTooClose = lastPoint
44648 ? point.distanceTo(lastPoint) <= this.min_distance
44650 var color = lastPointGroup.color;
44651 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44652 var curve = this.addPoint(point);
44654 this.drawDot({color: color, point: point});
44657 this.drawCurve({color: color, curve: curve});
44667 strokeEnd: function(e)
44669 this.strokeUpdate(e);
44670 if (typeof this.onEnd === 'function') {
44675 addPoint: function (point) {
44676 var _lastPoints = this._lastPoints;
44677 _lastPoints.push(point);
44678 if (_lastPoints.length > 2) {
44679 if (_lastPoints.length === 3) {
44680 _lastPoints.unshift(_lastPoints[0]);
44682 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44683 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44684 _lastPoints.shift();
44690 calculateCurveWidths: function (startPoint, endPoint) {
44691 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44692 (1 - this.velocity_filter_weight) * this._lastVelocity;
44694 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44697 start: this._lastWidth
44700 this._lastVelocity = velocity;
44701 this._lastWidth = newWidth;
44705 drawDot: function (_a) {
44706 var color = _a.color, point = _a.point;
44707 var ctx = this.canvasElCtx();
44708 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44710 this.drawCurveSegment(point.x, point.y, width);
44712 ctx.fillStyle = color;
44716 drawCurve: function (_a) {
44717 var color = _a.color, curve = _a.curve;
44718 var ctx = this.canvasElCtx();
44719 var widthDelta = curve.endWidth - curve.startWidth;
44720 var drawSteps = Math.floor(curve.length()) * 2;
44722 ctx.fillStyle = color;
44723 for (var i = 0; i < drawSteps; i += 1) {
44724 var t = i / drawSteps;
44730 var x = uuu * curve.startPoint.x;
44731 x += 3 * uu * t * curve.control1.x;
44732 x += 3 * u * tt * curve.control2.x;
44733 x += ttt * curve.endPoint.x;
44734 var y = uuu * curve.startPoint.y;
44735 y += 3 * uu * t * curve.control1.y;
44736 y += 3 * u * tt * curve.control2.y;
44737 y += ttt * curve.endPoint.y;
44738 var width = curve.startWidth + ttt * widthDelta;
44739 this.drawCurveSegment(x, y, width);
44745 drawCurveSegment: function (x, y, width) {
44746 var ctx = this.canvasElCtx();
44748 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44749 this.is_empty = false;
44754 var ctx = this.canvasElCtx();
44755 var canvas = this.canvasEl().dom;
44756 ctx.fillStyle = this.bg_color;
44757 ctx.clearRect(0, 0, canvas.width, canvas.height);
44758 ctx.fillRect(0, 0, canvas.width, canvas.height);
44759 this.curve_data = [];
44761 this.is_empty = true;
44766 return this.el.select('input',true).first();
44769 canvasEl: function()
44771 return this.el.select('canvas',true).first();
44774 canvasElCtx: function()
44776 return this.el.select('canvas',true).first().dom.getContext('2d');
44779 getImage: function(type)
44781 if(this.is_empty) {
44786 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44789 drawFromImage: function(img_src)
44791 var img = new Image();
44793 img.onload = function(){
44794 this.canvasElCtx().drawImage(img, 0, 0);
44799 this.is_empty = false;
44802 selectImage: function()
44804 this.fileEl().dom.click();
44807 uploadImage: function(e)
44809 var reader = new FileReader();
44811 reader.onload = function(e){
44812 var img = new Image();
44813 img.onload = function(){
44815 this.canvasElCtx().drawImage(img, 0, 0);
44817 img.src = e.target.result;
44820 reader.readAsDataURL(e.target.files[0]);
44823 // Bezier Point Constructor
44824 Point: (function () {
44825 function Point(x, y, time) {
44828 this.time = time || Date.now();
44830 Point.prototype.distanceTo = function (start) {
44831 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44833 Point.prototype.equals = function (other) {
44834 return this.x === other.x && this.y === other.y && this.time === other.time;
44836 Point.prototype.velocityFrom = function (start) {
44837 return this.time !== start.time
44838 ? this.distanceTo(start) / (this.time - start.time)
44845 // Bezier Constructor
44846 Bezier: (function () {
44847 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44848 this.startPoint = startPoint;
44849 this.control2 = control2;
44850 this.control1 = control1;
44851 this.endPoint = endPoint;
44852 this.startWidth = startWidth;
44853 this.endWidth = endWidth;
44855 Bezier.fromPoints = function (points, widths, scope) {
44856 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44857 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44858 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44860 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44861 var dx1 = s1.x - s2.x;
44862 var dy1 = s1.y - s2.y;
44863 var dx2 = s2.x - s3.x;
44864 var dy2 = s2.y - s3.y;
44865 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44866 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44867 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44868 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44869 var dxm = m1.x - m2.x;
44870 var dym = m1.y - m2.y;
44871 var k = l2 / (l1 + l2);
44872 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44873 var tx = s2.x - cm.x;
44874 var ty = s2.y - cm.y;
44876 c1: new scope.Point(m1.x + tx, m1.y + ty),
44877 c2: new scope.Point(m2.x + tx, m2.y + ty)
44880 Bezier.prototype.length = function () {
44885 for (var i = 0; i <= steps; i += 1) {
44887 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44888 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44890 var xdiff = cx - px;
44891 var ydiff = cy - py;
44892 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44899 Bezier.prototype.point = function (t, start, c1, c2, end) {
44900 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44901 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44902 + (3.0 * c2 * (1.0 - t) * t * t)
44903 + (end * t * t * t);
44908 throttleStroke: function(fn, wait) {
44909 if (wait === void 0) { wait = 250; }
44911 var timeout = null;
44915 var later = function () {
44916 previous = Date.now();
44918 result = fn.apply(storedContext, storedArgs);
44920 storedContext = null;
44924 return function wrapper() {
44926 for (var _i = 0; _i < arguments.length; _i++) {
44927 args[_i] = arguments[_i];
44929 var now = Date.now();
44930 var remaining = wait - (now - previous);
44931 storedContext = this;
44933 if (remaining <= 0 || remaining > wait) {
44935 clearTimeout(timeout);
44939 result = fn.apply(storedContext, storedArgs);
44941 storedContext = null;
44945 else if (!timeout) {
44946 timeout = window.setTimeout(later, remaining);