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
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8458 onDblClick : function(e,el)
8460 var cell = Roo.get(el);
8462 if(!cell || (!this.cellSelection && !this.rowSelection)){
8466 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467 cell = cell.findParent('td', false, true);
8470 if(!cell || typeof(cell) == 'undefined'){
8474 var row = cell.findParent('tr', false, true);
8476 if(!row || typeof(row) == 'undefined'){
8480 var cellIndex = cell.dom.cellIndex;
8481 var rowIndex = this.getRowIndex(row);
8483 if(this.cellSelection){
8484 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8487 if(this.rowSelection){
8488 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8492 sort : function(e,el)
8494 var col = Roo.get(el);
8496 if(!col.hasClass('sortable')){
8500 var sort = col.attr('sort');
8503 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8507 this.store.sortInfo = {field : sort, direction : dir};
8510 Roo.log("calling footer first");
8511 this.footer.onClick('first');
8514 this.store.load({ params : { start : 0 } });
8518 renderHeader : function()
8526 this.totalWidth = 0;
8528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530 var config = cm.config[i];
8534 cls : 'x-hcol-' + i,
8537 html: cm.getColumnHeader(i)
8540 var tooltip = cm.getColumnTooltip(i);
8542 c.tooltip = tooltip;
8548 if(typeof(config.sortable) != 'undefined' && config.sortable){
8550 c.html = '<i class="fa"></i>' + c.html;
8553 // could use BS4 hidden-..-down
8555 if(typeof(config.lgHeader) != 'undefined'){
8556 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8559 if(typeof(config.mdHeader) != 'undefined'){
8560 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8563 if(typeof(config.smHeader) != 'undefined'){
8564 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8567 if(typeof(config.xsHeader) != 'undefined'){
8568 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8575 if(typeof(config.tooltip) != 'undefined'){
8576 c.tooltip = config.tooltip;
8579 if(typeof(config.colspan) != 'undefined'){
8580 c.colspan = config.colspan;
8583 if(typeof(config.hidden) != 'undefined' && config.hidden){
8584 c.style += ' display:none;';
8587 if(typeof(config.dataIndex) != 'undefined'){
8588 c.sort = config.dataIndex;
8593 if(typeof(config.align) != 'undefined' && config.align.length){
8594 c.style += ' text-align:' + config.align + ';';
8597 if(typeof(config.width) != 'undefined'){
8598 c.style += ' width:' + config.width + 'px;';
8599 this.totalWidth += config.width;
8601 this.totalWidth += 100; // assume minimum of 100 per column?
8604 if(typeof(config.cls) != 'undefined'){
8605 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8608 ['xs','sm','md','lg'].map(function(size){
8610 if(typeof(config[size]) == 'undefined'){
8614 if (!config[size]) { // 0 = hidden
8615 // BS 4 '0' is treated as hide that column and below.
8616 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8620 c.cls += ' col-' + size + '-' + config[size] + (
8621 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8633 renderBody : function()
8643 colspan : this.cm.getColumnCount()
8653 renderFooter : function()
8663 colspan : this.cm.getColumnCount()
8677 // Roo.log('ds onload');
8682 var ds = this.store;
8684 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8685 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8686 if (_this.store.sortInfo) {
8688 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8689 e.select('i', true).addClass(['fa-arrow-up']);
8692 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8693 e.select('i', true).addClass(['fa-arrow-down']);
8698 var tbody = this.mainBody;
8700 if(ds.getCount() > 0){
8701 ds.data.each(function(d,rowIndex){
8702 var row = this.renderRow(cm, ds, rowIndex);
8704 tbody.createChild(row);
8708 if(row.cellObjects.length){
8709 Roo.each(row.cellObjects, function(r){
8710 _this.renderCellObject(r);
8717 var tfoot = this.el.select('tfoot', true).first();
8719 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8721 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8723 var total = this.ds.getTotalCount();
8725 if(this.footer.pageSize < total){
8726 this.mainFoot.show();
8730 Roo.each(this.el.select('tbody td', true).elements, function(e){
8731 e.on('mouseover', _this.onMouseover, _this);
8734 Roo.each(this.el.select('tbody td', true).elements, function(e){
8735 e.on('mouseout', _this.onMouseout, _this);
8737 this.fireEvent('rowsrendered', this);
8743 onUpdate : function(ds,record)
8745 this.refreshRow(record);
8749 onRemove : function(ds, record, index, isUpdate){
8750 if(isUpdate !== true){
8751 this.fireEvent("beforerowremoved", this, index, record);
8753 var bt = this.mainBody.dom;
8755 var rows = this.el.select('tbody > tr', true).elements;
8757 if(typeof(rows[index]) != 'undefined'){
8758 bt.removeChild(rows[index].dom);
8761 // if(bt.rows[index]){
8762 // bt.removeChild(bt.rows[index]);
8765 if(isUpdate !== true){
8766 //this.stripeRows(index);
8767 //this.syncRowHeights(index, index);
8769 this.fireEvent("rowremoved", this, index, record);
8773 onAdd : function(ds, records, rowIndex)
8775 //Roo.log('on Add called');
8776 // - note this does not handle multiple adding very well..
8777 var bt = this.mainBody.dom;
8778 for (var i =0 ; i < records.length;i++) {
8779 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8780 //Roo.log(records[i]);
8781 //Roo.log(this.store.getAt(rowIndex+i));
8782 this.insertRow(this.store, rowIndex + i, false);
8789 refreshRow : function(record){
8790 var ds = this.store, index;
8791 if(typeof record == 'number'){
8793 record = ds.getAt(index);
8795 index = ds.indexOf(record);
8797 return; // should not happen - but seems to
8800 this.insertRow(ds, index, true);
8802 this.onRemove(ds, record, index+1, true);
8804 //this.syncRowHeights(index, index);
8806 this.fireEvent("rowupdated", this, index, record);
8809 insertRow : function(dm, rowIndex, isUpdate){
8812 this.fireEvent("beforerowsinserted", this, rowIndex);
8814 //var s = this.getScrollState();
8815 var row = this.renderRow(this.cm, this.store, rowIndex);
8816 // insert before rowIndex..
8817 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8821 if(row.cellObjects.length){
8822 Roo.each(row.cellObjects, function(r){
8823 _this.renderCellObject(r);
8828 this.fireEvent("rowsinserted", this, rowIndex);
8829 //this.syncRowHeights(firstRow, lastRow);
8830 //this.stripeRows(firstRow);
8837 getRowDom : function(rowIndex)
8839 var rows = this.el.select('tbody > tr', true).elements;
8841 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8844 // returns the object tree for a tr..
8847 renderRow : function(cm, ds, rowIndex)
8849 var d = ds.getAt(rowIndex);
8853 cls : 'x-row-' + rowIndex,
8857 var cellObjects = [];
8859 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8860 var config = cm.config[i];
8862 var renderer = cm.getRenderer(i);
8866 if(typeof(renderer) !== 'undefined'){
8867 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8869 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8870 // and are rendered into the cells after the row is rendered - using the id for the element.
8872 if(typeof(value) === 'object'){
8882 rowIndex : rowIndex,
8887 this.fireEvent('rowclass', this, rowcfg);
8891 // this might end up displaying HTML?
8892 // this is too messy... - better to only do it on columsn you know are going to be too long
8893 //tooltip : (typeof(value) === 'object') ? '' : value,
8894 cls : rowcfg.rowClass + ' x-col-' + i,
8896 html: (typeof(value) === 'object') ? '' : value
8903 if(typeof(config.colspan) != 'undefined'){
8904 td.colspan = config.colspan;
8907 if(typeof(config.hidden) != 'undefined' && config.hidden){
8908 td.style += ' display:none;';
8911 if(typeof(config.align) != 'undefined' && config.align.length){
8912 td.style += ' text-align:' + config.align + ';';
8914 if(typeof(config.valign) != 'undefined' && config.valign.length){
8915 td.style += ' vertical-align:' + config.valign + ';';
8918 if(typeof(config.width) != 'undefined'){
8919 td.style += ' width:' + config.width + 'px;';
8922 if(typeof(config.cursor) != 'undefined'){
8923 td.style += ' cursor:' + config.cursor + ';';
8926 if(typeof(config.cls) != 'undefined'){
8927 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8930 ['xs','sm','md','lg'].map(function(size){
8932 if(typeof(config[size]) == 'undefined'){
8938 if (!config[size]) { // 0 = hidden
8939 // BS 4 '0' is treated as hide that column and below.
8940 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8944 td.cls += ' col-' + size + '-' + config[size] + (
8945 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8955 row.cellObjects = cellObjects;
8963 onBeforeLoad : function()
8972 this.el.select('tbody', true).first().dom.innerHTML = '';
8975 * Show or hide a row.
8976 * @param {Number} rowIndex to show or hide
8977 * @param {Boolean} state hide
8979 setRowVisibility : function(rowIndex, state)
8981 var bt = this.mainBody.dom;
8983 var rows = this.el.select('tbody > tr', true).elements;
8985 if(typeof(rows[rowIndex]) == 'undefined'){
8988 rows[rowIndex].dom.style.display = state ? '' : 'none';
8992 getSelectionModel : function(){
8994 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8996 return this.selModel;
8999 * Render the Roo.bootstrap object from renderder
9001 renderCellObject : function(r)
9005 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9007 var t = r.cfg.render(r.container);
9010 Roo.each(r.cfg.cn, function(c){
9012 container: t.getChildContainer(),
9015 _this.renderCellObject(child);
9020 getRowIndex : function(row)
9024 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9035 * Returns the grid's underlying element = used by panel.Grid
9036 * @return {Element} The element
9038 getGridEl : function(){
9042 * Forces a resize - used by panel.Grid
9043 * @return {Element} The element
9045 autoSize : function()
9047 //var ctr = Roo.get(this.container.dom.parentElement);
9048 var ctr = Roo.get(this.el.dom);
9050 var thd = this.getGridEl().select('thead',true).first();
9051 var tbd = this.getGridEl().select('tbody', true).first();
9052 var tfd = this.getGridEl().select('tfoot', true).first();
9054 var cw = ctr.getWidth();
9055 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9059 tbd.setWidth(ctr.getWidth());
9060 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9061 // this needs fixing for various usage - currently only hydra job advers I think..
9063 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9065 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9068 cw = Math.max(cw, this.totalWidth);
9069 this.getGridEl().select('tbody tr',true).setWidth(cw);
9071 // resize 'expandable coloumn?
9073 return; // we doe not have a view in this design..
9076 onBodyScroll: function()
9078 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9080 this.mainHead.setStyle({
9081 'position' : 'relative',
9082 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9088 var scrollHeight = this.mainBody.dom.scrollHeight;
9090 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9092 var height = this.mainBody.getHeight();
9094 if(scrollHeight - height == scrollTop) {
9096 var total = this.ds.getTotalCount();
9098 if(this.footer.cursor + this.footer.pageSize < total){
9100 this.footer.ds.load({
9102 start : this.footer.cursor + this.footer.pageSize,
9103 limit : this.footer.pageSize
9113 onHeaderChange : function()
9115 var header = this.renderHeader();
9116 var table = this.el.select('table', true).first();
9118 this.mainHead.remove();
9119 this.mainHead = table.createChild(header, this.mainBody, false);
9121 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9122 e.on('click', this.sort, this);
9128 onHiddenChange : function(colModel, colIndex, hidden)
9130 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9131 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9133 this.CSS.updateRule(thSelector, "display", "");
9134 this.CSS.updateRule(tdSelector, "display", "");
9137 this.CSS.updateRule(thSelector, "display", "none");
9138 this.CSS.updateRule(tdSelector, "display", "none");
9141 this.onHeaderChange();
9145 setColumnWidth: function(col_index, width)
9147 // width = "md-2 xs-2..."
9148 if(!this.colModel.config[col_index]) {
9152 var w = width.split(" ");
9154 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9156 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9159 for(var j = 0; j < w.length; j++) {
9165 var size_cls = w[j].split("-");
9167 if(!Number.isInteger(size_cls[1] * 1)) {
9171 if(!this.colModel.config[col_index][size_cls[0]]) {
9175 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9179 h_row[0].classList.replace(
9180 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9181 "col-"+size_cls[0]+"-"+size_cls[1]
9184 for(var i = 0; i < rows.length; i++) {
9186 var size_cls = w[j].split("-");
9188 if(!Number.isInteger(size_cls[1] * 1)) {
9192 if(!this.colModel.config[col_index][size_cls[0]]) {
9196 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9200 rows[i].classList.replace(
9201 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9202 "col-"+size_cls[0]+"-"+size_cls[1]
9206 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9221 * @class Roo.bootstrap.TableCell
9222 * @extends Roo.bootstrap.Component
9223 * Bootstrap TableCell class
9224 * @cfg {String} html cell contain text
9225 * @cfg {String} cls cell class
9226 * @cfg {String} tag cell tag (td|th) default td
9227 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9228 * @cfg {String} align Aligns the content in a cell
9229 * @cfg {String} axis Categorizes cells
9230 * @cfg {String} bgcolor Specifies the background color of a cell
9231 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9232 * @cfg {Number} colspan Specifies the number of columns a cell should span
9233 * @cfg {String} headers Specifies one or more header cells a cell is related to
9234 * @cfg {Number} height Sets the height of a cell
9235 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9236 * @cfg {Number} rowspan Sets the number of rows a cell should span
9237 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9238 * @cfg {String} valign Vertical aligns the content in a cell
9239 * @cfg {Number} width Specifies the width of a cell
9242 * Create a new TableCell
9243 * @param {Object} config The config object
9246 Roo.bootstrap.TableCell = function(config){
9247 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9250 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9270 getAutoCreate : function(){
9271 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9291 cfg.align=this.align
9297 cfg.bgcolor=this.bgcolor
9300 cfg.charoff=this.charoff
9303 cfg.colspan=this.colspan
9306 cfg.headers=this.headers
9309 cfg.height=this.height
9312 cfg.nowrap=this.nowrap
9315 cfg.rowspan=this.rowspan
9318 cfg.scope=this.scope
9321 cfg.valign=this.valign
9324 cfg.width=this.width
9343 * @class Roo.bootstrap.TableRow
9344 * @extends Roo.bootstrap.Component
9345 * Bootstrap TableRow class
9346 * @cfg {String} cls row class
9347 * @cfg {String} align Aligns the content in a table row
9348 * @cfg {String} bgcolor Specifies a background color for a table row
9349 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9350 * @cfg {String} valign Vertical aligns the content in a table row
9353 * Create a new TableRow
9354 * @param {Object} config The config object
9357 Roo.bootstrap.TableRow = function(config){
9358 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9361 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9369 getAutoCreate : function(){
9370 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9380 cfg.align = this.align;
9383 cfg.bgcolor = this.bgcolor;
9386 cfg.charoff = this.charoff;
9389 cfg.valign = this.valign;
9407 * @class Roo.bootstrap.TableBody
9408 * @extends Roo.bootstrap.Component
9409 * Bootstrap TableBody class
9410 * @cfg {String} cls element class
9411 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9412 * @cfg {String} align Aligns the content inside the element
9413 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9414 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9417 * Create a new TableBody
9418 * @param {Object} config The config object
9421 Roo.bootstrap.TableBody = function(config){
9422 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9425 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9433 getAutoCreate : function(){
9434 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9448 cfg.align = this.align;
9451 cfg.charoff = this.charoff;
9454 cfg.valign = this.valign;
9461 // initEvents : function()
9468 // this.store = Roo.factory(this.store, Roo.data);
9469 // this.store.on('load', this.onLoad, this);
9471 // this.store.load();
9475 // onLoad: function ()
9477 // this.fireEvent('load', this);
9487 * Ext JS Library 1.1.1
9488 * Copyright(c) 2006-2007, Ext JS, LLC.
9490 * Originally Released Under LGPL - original licence link has changed is not relivant.
9493 * <script type="text/javascript">
9496 // as we use this in bootstrap.
9497 Roo.namespace('Roo.form');
9499 * @class Roo.form.Action
9500 * Internal Class used to handle form actions
9502 * @param {Roo.form.BasicForm} el The form element or its id
9503 * @param {Object} config Configuration options
9508 // define the action interface
9509 Roo.form.Action = function(form, options){
9511 this.options = options || {};
9514 * Client Validation Failed
9517 Roo.form.Action.CLIENT_INVALID = 'client';
9519 * Server Validation Failed
9522 Roo.form.Action.SERVER_INVALID = 'server';
9524 * Connect to Server Failed
9527 Roo.form.Action.CONNECT_FAILURE = 'connect';
9529 * Reading Data from Server Failed
9532 Roo.form.Action.LOAD_FAILURE = 'load';
9534 Roo.form.Action.prototype = {
9536 failureType : undefined,
9537 response : undefined,
9541 run : function(options){
9546 success : function(response){
9551 handleResponse : function(response){
9555 // default connection failure
9556 failure : function(response){
9558 this.response = response;
9559 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9560 this.form.afterAction(this, false);
9563 processResponse : function(response){
9564 this.response = response;
9565 if(!response.responseText){
9568 this.result = this.handleResponse(response);
9572 // utility functions used internally
9573 getUrl : function(appendParams){
9574 var url = this.options.url || this.form.url || this.form.el.dom.action;
9576 var p = this.getParams();
9578 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9584 getMethod : function(){
9585 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9588 getParams : function(){
9589 var bp = this.form.baseParams;
9590 var p = this.options.params;
9592 if(typeof p == "object"){
9593 p = Roo.urlEncode(Roo.applyIf(p, bp));
9594 }else if(typeof p == 'string' && bp){
9595 p += '&' + Roo.urlEncode(bp);
9598 p = Roo.urlEncode(bp);
9603 createCallback : function(){
9605 success: this.success,
9606 failure: this.failure,
9608 timeout: (this.form.timeout*1000),
9609 upload: this.form.fileUpload ? this.success : undefined
9614 Roo.form.Action.Submit = function(form, options){
9615 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9618 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9621 haveProgress : false,
9622 uploadComplete : false,
9624 // uploadProgress indicator.
9625 uploadProgress : function()
9627 if (!this.form.progressUrl) {
9631 if (!this.haveProgress) {
9632 Roo.MessageBox.progress("Uploading", "Uploading");
9634 if (this.uploadComplete) {
9635 Roo.MessageBox.hide();
9639 this.haveProgress = true;
9641 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9643 var c = new Roo.data.Connection();
9645 url : this.form.progressUrl,
9650 success : function(req){
9651 //console.log(data);
9655 rdata = Roo.decode(req.responseText)
9657 Roo.log("Invalid data from server..");
9661 if (!rdata || !rdata.success) {
9663 Roo.MessageBox.alert(Roo.encode(rdata));
9666 var data = rdata.data;
9668 if (this.uploadComplete) {
9669 Roo.MessageBox.hide();
9674 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9675 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9678 this.uploadProgress.defer(2000,this);
9681 failure: function(data) {
9682 Roo.log('progress url failed ');
9693 // run get Values on the form, so it syncs any secondary forms.
9694 this.form.getValues();
9696 var o = this.options;
9697 var method = this.getMethod();
9698 var isPost = method == 'POST';
9699 if(o.clientValidation === false || this.form.isValid()){
9701 if (this.form.progressUrl) {
9702 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9703 (new Date() * 1) + '' + Math.random());
9708 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9709 form:this.form.el.dom,
9710 url:this.getUrl(!isPost),
9712 params:isPost ? this.getParams() : null,
9713 isUpload: this.form.fileUpload,
9714 formData : this.form.formData
9717 this.uploadProgress();
9719 }else if (o.clientValidation !== false){ // client validation failed
9720 this.failureType = Roo.form.Action.CLIENT_INVALID;
9721 this.form.afterAction(this, false);
9725 success : function(response)
9727 this.uploadComplete= true;
9728 if (this.haveProgress) {
9729 Roo.MessageBox.hide();
9733 var result = this.processResponse(response);
9734 if(result === true || result.success){
9735 this.form.afterAction(this, true);
9739 this.form.markInvalid(result.errors);
9740 this.failureType = Roo.form.Action.SERVER_INVALID;
9742 this.form.afterAction(this, false);
9744 failure : function(response)
9746 this.uploadComplete= true;
9747 if (this.haveProgress) {
9748 Roo.MessageBox.hide();
9751 this.response = response;
9752 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9753 this.form.afterAction(this, false);
9756 handleResponse : function(response){
9757 if(this.form.errorReader){
9758 var rs = this.form.errorReader.read(response);
9761 for(var i = 0, len = rs.records.length; i < len; i++) {
9762 var r = rs.records[i];
9766 if(errors.length < 1){
9770 success : rs.success,
9776 ret = Roo.decode(response.responseText);
9780 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9790 Roo.form.Action.Load = function(form, options){
9791 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9792 this.reader = this.form.reader;
9795 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9800 Roo.Ajax.request(Roo.apply(
9801 this.createCallback(), {
9802 method:this.getMethod(),
9803 url:this.getUrl(false),
9804 params:this.getParams()
9808 success : function(response){
9810 var result = this.processResponse(response);
9811 if(result === true || !result.success || !result.data){
9812 this.failureType = Roo.form.Action.LOAD_FAILURE;
9813 this.form.afterAction(this, false);
9816 this.form.clearInvalid();
9817 this.form.setValues(result.data);
9818 this.form.afterAction(this, true);
9821 handleResponse : function(response){
9822 if(this.form.reader){
9823 var rs = this.form.reader.read(response);
9824 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9826 success : rs.success,
9830 return Roo.decode(response.responseText);
9834 Roo.form.Action.ACTION_TYPES = {
9835 'load' : Roo.form.Action.Load,
9836 'submit' : Roo.form.Action.Submit
9845 * @class Roo.bootstrap.Form
9846 * @extends Roo.bootstrap.Component
9847 * Bootstrap Form class
9848 * @cfg {String} method GET | POST (default POST)
9849 * @cfg {String} labelAlign top | left (default top)
9850 * @cfg {String} align left | right - for navbars
9851 * @cfg {Boolean} loadMask load mask when submit (default true)
9856 * @param {Object} config The config object
9860 Roo.bootstrap.Form = function(config){
9862 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9864 Roo.bootstrap.Form.popover.apply();
9868 * @event clientvalidation
9869 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9870 * @param {Form} this
9871 * @param {Boolean} valid true if the form has passed client-side validation
9873 clientvalidation: true,
9875 * @event beforeaction
9876 * Fires before any action is performed. Return false to cancel the action.
9877 * @param {Form} this
9878 * @param {Action} action The action to be performed
9882 * @event actionfailed
9883 * Fires when an action fails.
9884 * @param {Form} this
9885 * @param {Action} action The action that failed
9887 actionfailed : true,
9889 * @event actioncomplete
9890 * Fires when an action is completed.
9891 * @param {Form} this
9892 * @param {Action} action The action that completed
9894 actioncomplete : true
9898 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9901 * @cfg {String} method
9902 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9907 * The URL to use for form actions if one isn't supplied in the action options.
9910 * @cfg {Boolean} fileUpload
9911 * Set to true if this form is a file upload.
9915 * @cfg {Object} baseParams
9916 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9920 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9924 * @cfg {Sting} align (left|right) for navbar forms
9929 activeAction : null,
9932 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9933 * element by passing it or its id or mask the form itself by passing in true.
9936 waitMsgTarget : false,
9941 * @cfg {Boolean} errorMask (true|false) default false
9946 * @cfg {Number} maskOffset Default 100
9951 * @cfg {Boolean} maskBody
9955 getAutoCreate : function(){
9959 method : this.method || 'POST',
9960 id : this.id || Roo.id(),
9963 if (this.parent().xtype.match(/^Nav/)) {
9964 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9968 if (this.labelAlign == 'left' ) {
9969 cfg.cls += ' form-horizontal';
9975 initEvents : function()
9977 this.el.on('submit', this.onSubmit, this);
9978 // this was added as random key presses on the form where triggering form submit.
9979 this.el.on('keypress', function(e) {
9980 if (e.getCharCode() != 13) {
9983 // we might need to allow it for textareas.. and some other items.
9984 // check e.getTarget().
9986 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9990 Roo.log("keypress blocked");
9998 onSubmit : function(e){
10003 * Returns true if client-side validation on the form is successful.
10006 isValid : function(){
10007 var items = this.getItems();
10009 var target = false;
10011 items.each(function(f){
10017 Roo.log('invalid field: ' + f.name);
10021 if(!target && f.el.isVisible(true)){
10027 if(this.errorMask && !valid){
10028 Roo.bootstrap.Form.popover.mask(this, target);
10035 * Returns true if any fields in this form have changed since their original load.
10038 isDirty : function(){
10040 var items = this.getItems();
10041 items.each(function(f){
10051 * Performs a predefined action (submit or load) or custom actions you define on this form.
10052 * @param {String} actionName The name of the action type
10053 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10054 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10055 * accept other config options):
10057 Property Type Description
10058 ---------------- --------------- ----------------------------------------------------------------------------------
10059 url String The url for the action (defaults to the form's url)
10060 method String The form method to use (defaults to the form's method, or POST if not defined)
10061 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10062 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10063 validate the form on the client (defaults to false)
10065 * @return {BasicForm} this
10067 doAction : function(action, options){
10068 if(typeof action == 'string'){
10069 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10071 if(this.fireEvent('beforeaction', this, action) !== false){
10072 this.beforeAction(action);
10073 action.run.defer(100, action);
10079 beforeAction : function(action){
10080 var o = action.options;
10085 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10087 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10090 // not really supported yet.. ??
10092 //if(this.waitMsgTarget === true){
10093 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10094 //}else if(this.waitMsgTarget){
10095 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10096 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10098 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10104 afterAction : function(action, success){
10105 this.activeAction = null;
10106 var o = action.options;
10111 Roo.get(document.body).unmask();
10117 //if(this.waitMsgTarget === true){
10118 // this.el.unmask();
10119 //}else if(this.waitMsgTarget){
10120 // this.waitMsgTarget.unmask();
10122 // Roo.MessageBox.updateProgress(1);
10123 // Roo.MessageBox.hide();
10130 Roo.callback(o.success, o.scope, [this, action]);
10131 this.fireEvent('actioncomplete', this, action);
10135 // failure condition..
10136 // we have a scenario where updates need confirming.
10137 // eg. if a locking scenario exists..
10138 // we look for { errors : { needs_confirm : true }} in the response.
10140 (typeof(action.result) != 'undefined') &&
10141 (typeof(action.result.errors) != 'undefined') &&
10142 (typeof(action.result.errors.needs_confirm) != 'undefined')
10145 Roo.log("not supported yet");
10148 Roo.MessageBox.confirm(
10149 "Change requires confirmation",
10150 action.result.errorMsg,
10155 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10165 Roo.callback(o.failure, o.scope, [this, action]);
10166 // show an error message if no failed handler is set..
10167 if (!this.hasListener('actionfailed')) {
10168 Roo.log("need to add dialog support");
10170 Roo.MessageBox.alert("Error",
10171 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10172 action.result.errorMsg :
10173 "Saving Failed, please check your entries or try again"
10178 this.fireEvent('actionfailed', this, action);
10183 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10184 * @param {String} id The value to search for
10187 findField : function(id){
10188 var items = this.getItems();
10189 var field = items.get(id);
10191 items.each(function(f){
10192 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10199 return field || null;
10202 * Mark fields in this form invalid in bulk.
10203 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10204 * @return {BasicForm} this
10206 markInvalid : function(errors){
10207 if(errors instanceof Array){
10208 for(var i = 0, len = errors.length; i < len; i++){
10209 var fieldError = errors[i];
10210 var f = this.findField(fieldError.id);
10212 f.markInvalid(fieldError.msg);
10218 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10219 field.markInvalid(errors[id]);
10223 //Roo.each(this.childForms || [], function (f) {
10224 // f.markInvalid(errors);
10231 * Set values for fields in this form in bulk.
10232 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10233 * @return {BasicForm} this
10235 setValues : function(values){
10236 if(values instanceof Array){ // array of objects
10237 for(var i = 0, len = values.length; i < len; i++){
10239 var f = this.findField(v.id);
10241 f.setValue(v.value);
10242 if(this.trackResetOnLoad){
10243 f.originalValue = f.getValue();
10247 }else{ // object hash
10250 if(typeof values[id] != 'function' && (field = this.findField(id))){
10252 if (field.setFromData &&
10253 field.valueField &&
10254 field.displayField &&
10255 // combos' with local stores can
10256 // be queried via setValue()
10257 // to set their value..
10258 (field.store && !field.store.isLocal)
10262 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10263 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10264 field.setFromData(sd);
10266 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10268 field.setFromData(values);
10271 field.setValue(values[id]);
10275 if(this.trackResetOnLoad){
10276 field.originalValue = field.getValue();
10282 //Roo.each(this.childForms || [], function (f) {
10283 // f.setValues(values);
10290 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10291 * they are returned as an array.
10292 * @param {Boolean} asString
10295 getValues : function(asString){
10296 //if (this.childForms) {
10297 // copy values from the child forms
10298 // Roo.each(this.childForms, function (f) {
10299 // this.setValues(f.getValues());
10305 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10306 if(asString === true){
10309 return Roo.urlDecode(fs);
10313 * Returns the fields in this form as an object with key/value pairs.
10314 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10317 getFieldValues : function(with_hidden)
10319 var items = this.getItems();
10321 items.each(function(f){
10323 if (!f.getName()) {
10327 var v = f.getValue();
10329 if (f.inputType =='radio') {
10330 if (typeof(ret[f.getName()]) == 'undefined') {
10331 ret[f.getName()] = ''; // empty..
10334 if (!f.el.dom.checked) {
10338 v = f.el.dom.value;
10342 if(f.xtype == 'MoneyField'){
10343 ret[f.currencyName] = f.getCurrency();
10346 // not sure if this supported any more..
10347 if ((typeof(v) == 'object') && f.getRawValue) {
10348 v = f.getRawValue() ; // dates..
10350 // combo boxes where name != hiddenName...
10351 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10352 ret[f.name] = f.getRawValue();
10354 ret[f.getName()] = v;
10361 * Clears all invalid messages in this form.
10362 * @return {BasicForm} this
10364 clearInvalid : function(){
10365 var items = this.getItems();
10367 items.each(function(f){
10375 * Resets this form.
10376 * @return {BasicForm} this
10378 reset : function(){
10379 var items = this.getItems();
10380 items.each(function(f){
10384 Roo.each(this.childForms || [], function (f) {
10392 getItems : function()
10394 var r=new Roo.util.MixedCollection(false, function(o){
10395 return o.id || (o.id = Roo.id());
10397 var iter = function(el) {
10404 Roo.each(el.items,function(e) {
10413 hideFields : function(items)
10415 Roo.each(items, function(i){
10417 var f = this.findField(i);
10428 showFields : function(items)
10430 Roo.each(items, function(i){
10432 var f = this.findField(i);
10445 Roo.apply(Roo.bootstrap.Form, {
10461 intervalID : false,
10467 if(this.isApplied){
10472 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10473 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10474 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10475 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10478 this.maskEl.top.enableDisplayMode("block");
10479 this.maskEl.left.enableDisplayMode("block");
10480 this.maskEl.bottom.enableDisplayMode("block");
10481 this.maskEl.right.enableDisplayMode("block");
10483 this.toolTip = new Roo.bootstrap.Tooltip({
10484 cls : 'roo-form-error-popover',
10486 'left' : ['r-l', [-2,0], 'right'],
10487 'right' : ['l-r', [2,0], 'left'],
10488 'bottom' : ['tl-bl', [0,2], 'top'],
10489 'top' : [ 'bl-tl', [0,-2], 'bottom']
10493 this.toolTip.render(Roo.get(document.body));
10495 this.toolTip.el.enableDisplayMode("block");
10497 Roo.get(document.body).on('click', function(){
10501 Roo.get(document.body).on('touchstart', function(){
10505 this.isApplied = true
10508 mask : function(form, target)
10512 this.target = target;
10514 if(!this.form.errorMask || !target.el){
10518 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10520 Roo.log(scrollable);
10522 var ot = this.target.el.calcOffsetsTo(scrollable);
10524 var scrollTo = ot[1] - this.form.maskOffset;
10526 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10528 scrollable.scrollTo('top', scrollTo);
10530 var box = this.target.el.getBox();
10532 var zIndex = Roo.bootstrap.Modal.zIndex++;
10535 this.maskEl.top.setStyle('position', 'absolute');
10536 this.maskEl.top.setStyle('z-index', zIndex);
10537 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10538 this.maskEl.top.setLeft(0);
10539 this.maskEl.top.setTop(0);
10540 this.maskEl.top.show();
10542 this.maskEl.left.setStyle('position', 'absolute');
10543 this.maskEl.left.setStyle('z-index', zIndex);
10544 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10545 this.maskEl.left.setLeft(0);
10546 this.maskEl.left.setTop(box.y - this.padding);
10547 this.maskEl.left.show();
10549 this.maskEl.bottom.setStyle('position', 'absolute');
10550 this.maskEl.bottom.setStyle('z-index', zIndex);
10551 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10552 this.maskEl.bottom.setLeft(0);
10553 this.maskEl.bottom.setTop(box.bottom + this.padding);
10554 this.maskEl.bottom.show();
10556 this.maskEl.right.setStyle('position', 'absolute');
10557 this.maskEl.right.setStyle('z-index', zIndex);
10558 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10559 this.maskEl.right.setLeft(box.right + this.padding);
10560 this.maskEl.right.setTop(box.y - this.padding);
10561 this.maskEl.right.show();
10563 this.toolTip.bindEl = this.target.el;
10565 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10567 var tip = this.target.blankText;
10569 if(this.target.getValue() !== '' ) {
10571 if (this.target.invalidText.length) {
10572 tip = this.target.invalidText;
10573 } else if (this.target.regexText.length){
10574 tip = this.target.regexText;
10578 this.toolTip.show(tip);
10580 this.intervalID = window.setInterval(function() {
10581 Roo.bootstrap.Form.popover.unmask();
10584 window.onwheel = function(){ return false;};
10586 (function(){ this.isMasked = true; }).defer(500, this);
10590 unmask : function()
10592 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10596 this.maskEl.top.setStyle('position', 'absolute');
10597 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10598 this.maskEl.top.hide();
10600 this.maskEl.left.setStyle('position', 'absolute');
10601 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10602 this.maskEl.left.hide();
10604 this.maskEl.bottom.setStyle('position', 'absolute');
10605 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10606 this.maskEl.bottom.hide();
10608 this.maskEl.right.setStyle('position', 'absolute');
10609 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10610 this.maskEl.right.hide();
10612 this.toolTip.hide();
10614 this.toolTip.el.hide();
10616 window.onwheel = function(){ return true;};
10618 if(this.intervalID){
10619 window.clearInterval(this.intervalID);
10620 this.intervalID = false;
10623 this.isMasked = false;
10633 * Ext JS Library 1.1.1
10634 * Copyright(c) 2006-2007, Ext JS, LLC.
10636 * Originally Released Under LGPL - original licence link has changed is not relivant.
10639 * <script type="text/javascript">
10642 * @class Roo.form.VTypes
10643 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10646 Roo.form.VTypes = function(){
10647 // closure these in so they are only created once.
10648 var alpha = /^[a-zA-Z_]+$/;
10649 var alphanum = /^[a-zA-Z0-9_]+$/;
10650 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10651 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10653 // All these messages and functions are configurable
10656 * The function used to validate email addresses
10657 * @param {String} value The email address
10659 'email' : function(v){
10660 return email.test(v);
10663 * The error text to display when the email validation function returns false
10666 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10668 * The keystroke filter mask to be applied on email input
10671 'emailMask' : /[a-z0-9_\.\-@]/i,
10674 * The function used to validate URLs
10675 * @param {String} value The URL
10677 'url' : function(v){
10678 return url.test(v);
10681 * The error text to display when the url validation function returns false
10684 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10687 * The function used to validate alpha values
10688 * @param {String} value The value
10690 'alpha' : function(v){
10691 return alpha.test(v);
10694 * The error text to display when the alpha validation function returns false
10697 'alphaText' : 'This field should only contain letters and _',
10699 * The keystroke filter mask to be applied on alpha input
10702 'alphaMask' : /[a-z_]/i,
10705 * The function used to validate alphanumeric values
10706 * @param {String} value The value
10708 'alphanum' : function(v){
10709 return alphanum.test(v);
10712 * The error text to display when the alphanumeric validation function returns false
10715 'alphanumText' : 'This field should only contain letters, numbers and _',
10717 * The keystroke filter mask to be applied on alphanumeric input
10720 'alphanumMask' : /[a-z0-9_]/i
10730 * @class Roo.bootstrap.Input
10731 * @extends Roo.bootstrap.Component
10732 * Bootstrap Input class
10733 * @cfg {Boolean} disabled is it disabled
10734 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10735 * @cfg {String} name name of the input
10736 * @cfg {string} fieldLabel - the label associated
10737 * @cfg {string} placeholder - placeholder to put in text.
10738 * @cfg {string} before - input group add on before
10739 * @cfg {string} after - input group add on after
10740 * @cfg {string} size - (lg|sm) or leave empty..
10741 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10742 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10743 * @cfg {Number} md colspan out of 12 for computer-sized screens
10744 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10745 * @cfg {string} value default value of the input
10746 * @cfg {Number} labelWidth set the width of label
10747 * @cfg {Number} labellg set the width of label (1-12)
10748 * @cfg {Number} labelmd set the width of label (1-12)
10749 * @cfg {Number} labelsm set the width of label (1-12)
10750 * @cfg {Number} labelxs set the width of label (1-12)
10751 * @cfg {String} labelAlign (top|left)
10752 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10753 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10754 * @cfg {String} indicatorpos (left|right) default left
10755 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10756 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10757 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10759 * @cfg {String} align (left|center|right) Default left
10760 * @cfg {Boolean} forceFeedback (true|false) Default false
10763 * Create a new Input
10764 * @param {Object} config The config object
10767 Roo.bootstrap.Input = function(config){
10769 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10774 * Fires when this field receives input focus.
10775 * @param {Roo.form.Field} this
10780 * Fires when this field loses input focus.
10781 * @param {Roo.form.Field} this
10785 * @event specialkey
10786 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10787 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10788 * @param {Roo.form.Field} this
10789 * @param {Roo.EventObject} e The event object
10794 * Fires just before the field blurs if the field value has changed.
10795 * @param {Roo.form.Field} this
10796 * @param {Mixed} newValue The new value
10797 * @param {Mixed} oldValue The original value
10802 * Fires after the field has been marked as invalid.
10803 * @param {Roo.form.Field} this
10804 * @param {String} msg The validation message
10809 * Fires after the field has been validated with no errors.
10810 * @param {Roo.form.Field} this
10815 * Fires after the key up
10816 * @param {Roo.form.Field} this
10817 * @param {Roo.EventObject} e The event Object
10822 * Fires after the user pastes into input
10823 * @param {Roo.form.Field} this
10824 * @param {Roo.EventObject} e The event Object
10830 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10832 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10833 automatic validation (defaults to "keyup").
10835 validationEvent : "keyup",
10837 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10839 validateOnBlur : true,
10841 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10843 validationDelay : 250,
10845 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10847 focusClass : "x-form-focus", // not needed???
10851 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10853 invalidClass : "has-warning",
10856 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10858 validClass : "has-success",
10861 * @cfg {Boolean} hasFeedback (true|false) default true
10863 hasFeedback : true,
10866 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10868 invalidFeedbackClass : "glyphicon-warning-sign",
10871 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10873 validFeedbackClass : "glyphicon-ok",
10876 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10878 selectOnFocus : false,
10881 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10885 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10890 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10892 disableKeyFilter : false,
10895 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10899 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10903 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10905 blankText : "Please complete this mandatory field",
10908 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10912 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10914 maxLength : Number.MAX_VALUE,
10916 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10918 minLengthText : "The minimum length for this field is {0}",
10920 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10922 maxLengthText : "The maximum length for this field is {0}",
10926 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10927 * If available, this function will be called only after the basic validators all return true, and will be passed the
10928 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10932 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10933 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10934 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10938 * @cfg {String} regexText -- Depricated - use Invalid Text
10943 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10949 autocomplete: false,
10953 inputType : 'text',
10956 placeholder: false,
10961 preventMark: false,
10962 isFormField : true,
10965 labelAlign : false,
10968 formatedValue : false,
10969 forceFeedback : false,
10971 indicatorpos : 'left',
10981 parentLabelAlign : function()
10984 while (parent.parent()) {
10985 parent = parent.parent();
10986 if (typeof(parent.labelAlign) !='undefined') {
10987 return parent.labelAlign;
10994 getAutoCreate : function()
10996 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11002 if(this.inputType != 'hidden'){
11003 cfg.cls = 'form-group' //input-group
11009 type : this.inputType,
11010 value : this.value,
11011 cls : 'form-control',
11012 placeholder : this.placeholder || '',
11013 autocomplete : this.autocomplete || 'new-password'
11015 if (this.inputType == 'file') {
11016 input.style = 'overflow:hidden'; // why not in CSS?
11019 if(this.capture.length){
11020 input.capture = this.capture;
11023 if(this.accept.length){
11024 input.accept = this.accept + "/*";
11028 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11031 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11032 input.maxLength = this.maxLength;
11035 if (this.disabled) {
11036 input.disabled=true;
11039 if (this.readOnly) {
11040 input.readonly=true;
11044 input.name = this.name;
11048 input.cls += ' input-' + this.size;
11052 ['xs','sm','md','lg'].map(function(size){
11053 if (settings[size]) {
11054 cfg.cls += ' col-' + size + '-' + settings[size];
11058 var inputblock = input;
11062 cls: 'glyphicon form-control-feedback'
11065 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11068 cls : 'has-feedback',
11076 if (this.before || this.after) {
11079 cls : 'input-group',
11083 if (this.before && typeof(this.before) == 'string') {
11085 inputblock.cn.push({
11087 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11091 if (this.before && typeof(this.before) == 'object') {
11092 this.before = Roo.factory(this.before);
11094 inputblock.cn.push({
11096 cls : 'roo-input-before input-group-prepend input-group-' +
11097 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11101 inputblock.cn.push(input);
11103 if (this.after && typeof(this.after) == 'string') {
11104 inputblock.cn.push({
11106 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11110 if (this.after && typeof(this.after) == 'object') {
11111 this.after = Roo.factory(this.after);
11113 inputblock.cn.push({
11115 cls : 'roo-input-after input-group-append input-group-' +
11116 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11120 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11121 inputblock.cls += ' has-feedback';
11122 inputblock.cn.push(feedback);
11127 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11128 tooltip : 'This field is required'
11130 if (this.allowBlank ) {
11131 indicator.style = this.allowBlank ? ' display:none' : '';
11133 if (align ==='left' && this.fieldLabel.length) {
11135 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11142 cls : 'control-label col-form-label',
11143 html : this.fieldLabel
11154 var labelCfg = cfg.cn[1];
11155 var contentCfg = cfg.cn[2];
11157 if(this.indicatorpos == 'right'){
11162 cls : 'control-label col-form-label',
11166 html : this.fieldLabel
11180 labelCfg = cfg.cn[0];
11181 contentCfg = cfg.cn[1];
11185 if(this.labelWidth > 12){
11186 labelCfg.style = "width: " + this.labelWidth + 'px';
11189 if(this.labelWidth < 13 && this.labelmd == 0){
11190 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11193 if(this.labellg > 0){
11194 labelCfg.cls += ' col-lg-' + this.labellg;
11195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11198 if(this.labelmd > 0){
11199 labelCfg.cls += ' col-md-' + this.labelmd;
11200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11203 if(this.labelsm > 0){
11204 labelCfg.cls += ' col-sm-' + this.labelsm;
11205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11208 if(this.labelxs > 0){
11209 labelCfg.cls += ' col-xs-' + this.labelxs;
11210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11214 } else if ( this.fieldLabel.length) {
11221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11222 tooltip : 'This field is required',
11223 style : this.allowBlank ? ' display:none' : ''
11227 //cls : 'input-group-addon',
11228 html : this.fieldLabel
11236 if(this.indicatorpos == 'right'){
11241 //cls : 'input-group-addon',
11242 html : this.fieldLabel
11247 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11248 tooltip : 'This field is required',
11249 style : this.allowBlank ? ' display:none' : ''
11269 if (this.parentType === 'Navbar' && this.parent().bar) {
11270 cfg.cls += ' navbar-form';
11273 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11274 // on BS4 we do this only if not form
11275 cfg.cls += ' navbar-form';
11283 * return the real input element.
11285 inputEl: function ()
11287 return this.el.select('input.form-control',true).first();
11290 tooltipEl : function()
11292 return this.inputEl();
11295 indicatorEl : function()
11297 if (Roo.bootstrap.version == 4) {
11298 return false; // not enabled in v4 yet.
11301 var indicator = this.el.select('i.roo-required-indicator',true).first();
11311 setDisabled : function(v)
11313 var i = this.inputEl().dom;
11315 i.removeAttribute('disabled');
11319 i.setAttribute('disabled','true');
11321 initEvents : function()
11324 this.inputEl().on("keydown" , this.fireKey, this);
11325 this.inputEl().on("focus", this.onFocus, this);
11326 this.inputEl().on("blur", this.onBlur, this);
11328 this.inputEl().relayEvent('keyup', this);
11329 this.inputEl().relayEvent('paste', this);
11331 this.indicator = this.indicatorEl();
11333 if(this.indicator){
11334 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11337 // reference to original value for reset
11338 this.originalValue = this.getValue();
11339 //Roo.form.TextField.superclass.initEvents.call(this);
11340 if(this.validationEvent == 'keyup'){
11341 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11342 this.inputEl().on('keyup', this.filterValidation, this);
11344 else if(this.validationEvent !== false){
11345 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11348 if(this.selectOnFocus){
11349 this.on("focus", this.preFocus, this);
11352 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11353 this.inputEl().on("keypress", this.filterKeys, this);
11355 this.inputEl().relayEvent('keypress', this);
11358 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11359 this.el.on("click", this.autoSize, this);
11362 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11363 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11366 if (typeof(this.before) == 'object') {
11367 this.before.render(this.el.select('.roo-input-before',true).first());
11369 if (typeof(this.after) == 'object') {
11370 this.after.render(this.el.select('.roo-input-after',true).first());
11373 this.inputEl().on('change', this.onChange, this);
11376 filterValidation : function(e){
11377 if(!e.isNavKeyPress()){
11378 this.validationTask.delay(this.validationDelay);
11382 * Validates the field value
11383 * @return {Boolean} True if the value is valid, else false
11385 validate : function(){
11386 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11387 if(this.disabled || this.validateValue(this.getRawValue())){
11392 this.markInvalid();
11398 * Validates a value according to the field's validation rules and marks the field as invalid
11399 * if the validation fails
11400 * @param {Mixed} value The value to validate
11401 * @return {Boolean} True if the value is valid, else false
11403 validateValue : function(value)
11405 if(this.getVisibilityEl().hasClass('hidden')){
11409 if(value.length < 1) { // if it's blank
11410 if(this.allowBlank){
11416 if(value.length < this.minLength){
11419 if(value.length > this.maxLength){
11423 var vt = Roo.form.VTypes;
11424 if(!vt[this.vtype](value, this)){
11428 if(typeof this.validator == "function"){
11429 var msg = this.validator(value);
11433 if (typeof(msg) == 'string') {
11434 this.invalidText = msg;
11438 if(this.regex && !this.regex.test(value)){
11446 fireKey : function(e){
11447 //Roo.log('field ' + e.getKey());
11448 if(e.isNavKeyPress()){
11449 this.fireEvent("specialkey", this, e);
11452 focus : function (selectText){
11454 this.inputEl().focus();
11455 if(selectText === true){
11456 this.inputEl().dom.select();
11462 onFocus : function(){
11463 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11464 // this.el.addClass(this.focusClass);
11466 if(!this.hasFocus){
11467 this.hasFocus = true;
11468 this.startValue = this.getValue();
11469 this.fireEvent("focus", this);
11473 beforeBlur : Roo.emptyFn,
11477 onBlur : function(){
11479 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11480 //this.el.removeClass(this.focusClass);
11482 this.hasFocus = false;
11483 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11486 var v = this.getValue();
11487 if(String(v) !== String(this.startValue)){
11488 this.fireEvent('change', this, v, this.startValue);
11490 this.fireEvent("blur", this);
11493 onChange : function(e)
11495 var v = this.getValue();
11496 if(String(v) !== String(this.startValue)){
11497 this.fireEvent('change', this, v, this.startValue);
11503 * Resets the current field value to the originally loaded value and clears any validation messages
11505 reset : function(){
11506 this.setValue(this.originalValue);
11510 * Returns the name of the field
11511 * @return {Mixed} name The name field
11513 getName: function(){
11517 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11518 * @return {Mixed} value The field value
11520 getValue : function(){
11522 var v = this.inputEl().getValue();
11527 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11528 * @return {Mixed} value The field value
11530 getRawValue : function(){
11531 var v = this.inputEl().getValue();
11537 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11538 * @param {Mixed} value The value to set
11540 setRawValue : function(v){
11541 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11544 selectText : function(start, end){
11545 var v = this.getRawValue();
11547 start = start === undefined ? 0 : start;
11548 end = end === undefined ? v.length : end;
11549 var d = this.inputEl().dom;
11550 if(d.setSelectionRange){
11551 d.setSelectionRange(start, end);
11552 }else if(d.createTextRange){
11553 var range = d.createTextRange();
11554 range.moveStart("character", start);
11555 range.moveEnd("character", v.length-end);
11562 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11563 * @param {Mixed} value The value to set
11565 setValue : function(v){
11568 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11574 processValue : function(value){
11575 if(this.stripCharsRe){
11576 var newValue = value.replace(this.stripCharsRe, '');
11577 if(newValue !== value){
11578 this.setRawValue(newValue);
11585 preFocus : function(){
11587 if(this.selectOnFocus){
11588 this.inputEl().dom.select();
11591 filterKeys : function(e){
11592 var k = e.getKey();
11593 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11596 var c = e.getCharCode(), cc = String.fromCharCode(c);
11597 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11600 if(!this.maskRe.test(cc)){
11605 * Clear any invalid styles/messages for this field
11607 clearInvalid : function(){
11609 if(!this.el || this.preventMark){ // not rendered
11614 this.el.removeClass([this.invalidClass, 'is-invalid']);
11616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11626 if(this.indicator){
11627 this.indicator.removeClass('visible');
11628 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11631 this.fireEvent('valid', this);
11635 * Mark this field as valid
11637 markValid : function()
11639 if(!this.el || this.preventMark){ // not rendered...
11643 this.el.removeClass([this.invalidClass, this.validClass]);
11644 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11646 var feedback = this.el.select('.form-control-feedback', true).first();
11649 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11652 if(this.indicator){
11653 this.indicator.removeClass('visible');
11654 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11662 if(this.allowBlank && !this.getRawValue().length){
11665 if (Roo.bootstrap.version == 3) {
11666 this.el.addClass(this.validClass);
11668 this.inputEl().addClass('is-valid');
11671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11673 var feedback = this.el.select('.form-control-feedback', true).first();
11676 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11677 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11682 this.fireEvent('valid', this);
11686 * Mark this field as invalid
11687 * @param {String} msg The validation message
11689 markInvalid : function(msg)
11691 if(!this.el || this.preventMark){ // not rendered
11695 this.el.removeClass([this.invalidClass, this.validClass]);
11696 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11698 var feedback = this.el.select('.form-control-feedback', true).first();
11701 this.el.select('.form-control-feedback', true).first().removeClass(
11702 [this.invalidFeedbackClass, this.validFeedbackClass]);
11709 if(this.allowBlank && !this.getRawValue().length){
11713 if(this.indicator){
11714 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11715 this.indicator.addClass('visible');
11717 if (Roo.bootstrap.version == 3) {
11718 this.el.addClass(this.invalidClass);
11720 this.inputEl().addClass('is-invalid');
11725 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11727 var feedback = this.el.select('.form-control-feedback', true).first();
11730 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11732 if(this.getValue().length || this.forceFeedback){
11733 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11740 this.fireEvent('invalid', this, msg);
11743 SafariOnKeyDown : function(event)
11745 // this is a workaround for a password hang bug on chrome/ webkit.
11746 if (this.inputEl().dom.type != 'password') {
11750 var isSelectAll = false;
11752 if(this.inputEl().dom.selectionEnd > 0){
11753 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11755 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11756 event.preventDefault();
11761 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11763 event.preventDefault();
11764 // this is very hacky as keydown always get's upper case.
11766 var cc = String.fromCharCode(event.getCharCode());
11767 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11771 adjustWidth : function(tag, w){
11772 tag = tag.toLowerCase();
11773 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11774 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11775 if(tag == 'input'){
11778 if(tag == 'textarea'){
11781 }else if(Roo.isOpera){
11782 if(tag == 'input'){
11785 if(tag == 'textarea'){
11793 setFieldLabel : function(v)
11795 if(!this.rendered){
11799 if(this.indicatorEl()){
11800 var ar = this.el.select('label > span',true);
11802 if (ar.elements.length) {
11803 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804 this.fieldLabel = v;
11808 var br = this.el.select('label',true);
11810 if(br.elements.length) {
11811 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812 this.fieldLabel = v;
11816 Roo.log('Cannot Found any of label > span || label in input');
11820 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11821 this.fieldLabel = v;
11836 * @class Roo.bootstrap.TextArea
11837 * @extends Roo.bootstrap.Input
11838 * Bootstrap TextArea class
11839 * @cfg {Number} cols Specifies the visible width of a text area
11840 * @cfg {Number} rows Specifies the visible number of lines in a text area
11841 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11842 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11843 * @cfg {string} html text
11846 * Create a new TextArea
11847 * @param {Object} config The config object
11850 Roo.bootstrap.TextArea = function(config){
11851 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11855 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11865 getAutoCreate : function(){
11867 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11873 if(this.inputType != 'hidden'){
11874 cfg.cls = 'form-group' //input-group
11882 value : this.value || '',
11883 html: this.html || '',
11884 cls : 'form-control',
11885 placeholder : this.placeholder || ''
11889 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11890 input.maxLength = this.maxLength;
11894 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11898 input.cols = this.cols;
11901 if (this.readOnly) {
11902 input.readonly = true;
11906 input.name = this.name;
11910 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11914 ['xs','sm','md','lg'].map(function(size){
11915 if (settings[size]) {
11916 cfg.cls += ' col-' + size + '-' + settings[size];
11920 var inputblock = input;
11922 if(this.hasFeedback && !this.allowBlank){
11926 cls: 'glyphicon form-control-feedback'
11930 cls : 'has-feedback',
11939 if (this.before || this.after) {
11942 cls : 'input-group',
11946 inputblock.cn.push({
11948 cls : 'input-group-addon',
11953 inputblock.cn.push(input);
11955 if(this.hasFeedback && !this.allowBlank){
11956 inputblock.cls += ' has-feedback';
11957 inputblock.cn.push(feedback);
11961 inputblock.cn.push({
11963 cls : 'input-group-addon',
11970 if (align ==='left' && this.fieldLabel.length) {
11975 cls : 'control-label',
11976 html : this.fieldLabel
11987 if(this.labelWidth > 12){
11988 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11991 if(this.labelWidth < 13 && this.labelmd == 0){
11992 this.labelmd = this.labelWidth;
11995 if(this.labellg > 0){
11996 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11997 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12000 if(this.labelmd > 0){
12001 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12002 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12005 if(this.labelsm > 0){
12006 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12007 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12010 if(this.labelxs > 0){
12011 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12012 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12015 } else if ( this.fieldLabel.length) {
12020 //cls : 'input-group-addon',
12021 html : this.fieldLabel
12039 if (this.disabled) {
12040 input.disabled=true;
12047 * return the real textarea element.
12049 inputEl: function ()
12051 return this.el.select('textarea.form-control',true).first();
12055 * Clear any invalid styles/messages for this field
12057 clearInvalid : function()
12060 if(!this.el || this.preventMark){ // not rendered
12064 var label = this.el.select('label', true).first();
12065 var icon = this.el.select('i.fa-star', true).first();
12070 this.el.removeClass( this.validClass);
12071 this.inputEl().removeClass('is-invalid');
12073 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12075 var feedback = this.el.select('.form-control-feedback', true).first();
12078 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12083 this.fireEvent('valid', this);
12087 * Mark this field as valid
12089 markValid : function()
12091 if(!this.el || this.preventMark){ // not rendered
12095 this.el.removeClass([this.invalidClass, this.validClass]);
12096 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12098 var feedback = this.el.select('.form-control-feedback', true).first();
12101 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12104 if(this.disabled || this.allowBlank){
12108 var label = this.el.select('label', true).first();
12109 var icon = this.el.select('i.fa-star', true).first();
12114 if (Roo.bootstrap.version == 3) {
12115 this.el.addClass(this.validClass);
12117 this.inputEl().addClass('is-valid');
12121 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12123 var feedback = this.el.select('.form-control-feedback', true).first();
12126 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12127 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12132 this.fireEvent('valid', this);
12136 * Mark this field as invalid
12137 * @param {String} msg The validation message
12139 markInvalid : function(msg)
12141 if(!this.el || this.preventMark){ // not rendered
12145 this.el.removeClass([this.invalidClass, this.validClass]);
12146 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12148 var feedback = this.el.select('.form-control-feedback', true).first();
12151 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12154 if(this.disabled || this.allowBlank){
12158 var label = this.el.select('label', true).first();
12159 var icon = this.el.select('i.fa-star', true).first();
12161 if(!this.getValue().length && label && !icon){
12162 this.el.createChild({
12164 cls : 'text-danger fa fa-lg fa-star',
12165 tooltip : 'This field is required',
12166 style : 'margin-right:5px;'
12170 if (Roo.bootstrap.version == 3) {
12171 this.el.addClass(this.invalidClass);
12173 this.inputEl().addClass('is-invalid');
12176 // fixme ... this may be depricated need to test..
12177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179 var feedback = this.el.select('.form-control-feedback', true).first();
12182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12184 if(this.getValue().length || this.forceFeedback){
12185 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12192 this.fireEvent('invalid', this, msg);
12200 * trigger field - base class for combo..
12205 * @class Roo.bootstrap.TriggerField
12206 * @extends Roo.bootstrap.Input
12207 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12208 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12209 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12210 * for which you can provide a custom implementation. For example:
12212 var trigger = new Roo.bootstrap.TriggerField();
12213 trigger.onTriggerClick = myTriggerFn;
12214 trigger.applyTo('my-field');
12217 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12218 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12219 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12220 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12221 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12224 * Create a new TriggerField.
12225 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12226 * to the base TextField)
12228 Roo.bootstrap.TriggerField = function(config){
12229 this.mimicing = false;
12230 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12233 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12235 * @cfg {String} triggerClass A CSS class to apply to the trigger
12238 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12243 * @cfg {Boolean} removable (true|false) special filter default false
12247 /** @cfg {Boolean} grow @hide */
12248 /** @cfg {Number} growMin @hide */
12249 /** @cfg {Number} growMax @hide */
12255 autoSize: Roo.emptyFn,
12259 deferHeight : true,
12262 actionMode : 'wrap',
12267 getAutoCreate : function(){
12269 var align = this.labelAlign || this.parentLabelAlign();
12274 cls: 'form-group' //input-group
12281 type : this.inputType,
12282 cls : 'form-control',
12283 autocomplete: 'new-password',
12284 placeholder : this.placeholder || ''
12288 input.name = this.name;
12291 input.cls += ' input-' + this.size;
12294 if (this.disabled) {
12295 input.disabled=true;
12298 var inputblock = input;
12300 if(this.hasFeedback && !this.allowBlank){
12304 cls: 'glyphicon form-control-feedback'
12307 if(this.removable && !this.editable ){
12309 cls : 'has-feedback',
12315 cls : 'roo-combo-removable-btn close'
12322 cls : 'has-feedback',
12331 if(this.removable && !this.editable ){
12333 cls : 'roo-removable',
12339 cls : 'roo-combo-removable-btn close'
12346 if (this.before || this.after) {
12349 cls : 'input-group',
12353 inputblock.cn.push({
12355 cls : 'input-group-addon input-group-prepend input-group-text',
12360 inputblock.cn.push(input);
12362 if(this.hasFeedback && !this.allowBlank){
12363 inputblock.cls += ' has-feedback';
12364 inputblock.cn.push(feedback);
12368 inputblock.cn.push({
12370 cls : 'input-group-addon input-group-append input-group-text',
12379 var ibwrap = inputblock;
12384 cls: 'roo-select2-choices',
12388 cls: 'roo-select2-search-field',
12400 cls: 'roo-select2-container input-group',
12405 cls: 'form-hidden-field'
12411 if(!this.multiple && this.showToggleBtn){
12417 if (this.caret != false) {
12420 cls: 'fa fa-' + this.caret
12427 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12429 Roo.bootstrap.version == 3 ? caret : '',
12432 cls: 'combobox-clear',
12446 combobox.cls += ' roo-select2-container-multi';
12450 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12451 tooltip : 'This field is required'
12453 if (Roo.bootstrap.version == 4) {
12456 style : 'display:none'
12461 if (align ==='left' && this.fieldLabel.length) {
12463 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12470 cls : 'control-label',
12471 html : this.fieldLabel
12483 var labelCfg = cfg.cn[1];
12484 var contentCfg = cfg.cn[2];
12486 if(this.indicatorpos == 'right'){
12491 cls : 'control-label',
12495 html : this.fieldLabel
12509 labelCfg = cfg.cn[0];
12510 contentCfg = cfg.cn[1];
12513 if(this.labelWidth > 12){
12514 labelCfg.style = "width: " + this.labelWidth + 'px';
12517 if(this.labelWidth < 13 && this.labelmd == 0){
12518 this.labelmd = this.labelWidth;
12521 if(this.labellg > 0){
12522 labelCfg.cls += ' col-lg-' + this.labellg;
12523 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12526 if(this.labelmd > 0){
12527 labelCfg.cls += ' col-md-' + this.labelmd;
12528 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12531 if(this.labelsm > 0){
12532 labelCfg.cls += ' col-sm-' + this.labelsm;
12533 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12536 if(this.labelxs > 0){
12537 labelCfg.cls += ' col-xs-' + this.labelxs;
12538 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12541 } else if ( this.fieldLabel.length) {
12542 // Roo.log(" label");
12547 //cls : 'input-group-addon',
12548 html : this.fieldLabel
12556 if(this.indicatorpos == 'right'){
12564 html : this.fieldLabel
12578 // Roo.log(" no label && no align");
12585 ['xs','sm','md','lg'].map(function(size){
12586 if (settings[size]) {
12587 cfg.cls += ' col-' + size + '-' + settings[size];
12598 onResize : function(w, h){
12599 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12600 // if(typeof w == 'number'){
12601 // var x = w - this.trigger.getWidth();
12602 // this.inputEl().setWidth(this.adjustWidth('input', x));
12603 // this.trigger.setStyle('left', x+'px');
12608 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12611 getResizeEl : function(){
12612 return this.inputEl();
12616 getPositionEl : function(){
12617 return this.inputEl();
12621 alignErrorIcon : function(){
12622 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12626 initEvents : function(){
12630 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12631 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12632 if(!this.multiple && this.showToggleBtn){
12633 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12634 if(this.hideTrigger){
12635 this.trigger.setDisplayed(false);
12637 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12641 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12644 if(this.removable && !this.editable && !this.tickable){
12645 var close = this.closeTriggerEl();
12648 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12649 close.on('click', this.removeBtnClick, this, close);
12653 //this.trigger.addClassOnOver('x-form-trigger-over');
12654 //this.trigger.addClassOnClick('x-form-trigger-click');
12657 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12661 closeTriggerEl : function()
12663 var close = this.el.select('.roo-combo-removable-btn', true).first();
12664 return close ? close : false;
12667 removeBtnClick : function(e, h, el)
12669 e.preventDefault();
12671 if(this.fireEvent("remove", this) !== false){
12673 this.fireEvent("afterremove", this)
12677 createList : function()
12679 this.list = Roo.get(document.body).createChild({
12680 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12681 cls: 'typeahead typeahead-long dropdown-menu shadow',
12682 style: 'display:none'
12685 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12690 initTrigger : function(){
12695 onDestroy : function(){
12697 this.trigger.removeAllListeners();
12698 // this.trigger.remove();
12701 // this.wrap.remove();
12703 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12707 onFocus : function(){
12708 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12710 if(!this.mimicing){
12711 this.wrap.addClass('x-trigger-wrap-focus');
12712 this.mimicing = true;
12713 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12714 if(this.monitorTab){
12715 this.el.on("keydown", this.checkTab, this);
12722 checkTab : function(e){
12723 if(e.getKey() == e.TAB){
12724 this.triggerBlur();
12729 onBlur : function(){
12734 mimicBlur : function(e, t){
12736 if(!this.wrap.contains(t) && this.validateBlur()){
12737 this.triggerBlur();
12743 triggerBlur : function(){
12744 this.mimicing = false;
12745 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12746 if(this.monitorTab){
12747 this.el.un("keydown", this.checkTab, this);
12749 //this.wrap.removeClass('x-trigger-wrap-focus');
12750 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12754 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12755 validateBlur : function(e, t){
12760 onDisable : function(){
12761 this.inputEl().dom.disabled = true;
12762 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12764 // this.wrap.addClass('x-item-disabled');
12769 onEnable : function(){
12770 this.inputEl().dom.disabled = false;
12771 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12773 // this.el.removeClass('x-item-disabled');
12778 onShow : function(){
12779 var ae = this.getActionEl();
12782 ae.dom.style.display = '';
12783 ae.dom.style.visibility = 'visible';
12789 onHide : function(){
12790 var ae = this.getActionEl();
12791 ae.dom.style.display = 'none';
12795 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12796 * by an implementing function.
12798 * @param {EventObject} e
12800 onTriggerClick : Roo.emptyFn
12808 * @class Roo.bootstrap.CardUploader
12809 * @extends Roo.bootstrap.Button
12810 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12811 * @cfg {Number} errorTimeout default 3000
12812 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12813 * @cfg {Array} html The button text.
12817 * Create a new CardUploader
12818 * @param {Object} config The config object
12821 Roo.bootstrap.CardUploader = function(config){
12825 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12828 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12836 * When a image is clicked on - and needs to display a slideshow or similar..
12837 * @param {Roo.bootstrap.Card} this
12838 * @param {Object} The image information data
12844 * When a the download link is clicked
12845 * @param {Roo.bootstrap.Card} this
12846 * @param {Object} The image information data contains
12853 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12856 errorTimeout : 3000,
12860 fileCollection : false,
12863 getAutoCreate : function()
12867 cls :'form-group' ,
12872 //cls : 'input-group-addon',
12873 html : this.fieldLabel
12881 value : this.value,
12882 cls : 'd-none form-control'
12887 multiple : 'multiple',
12889 cls : 'd-none roo-card-upload-selector'
12893 cls : 'roo-card-uploader-button-container w-100 mb-2'
12896 cls : 'card-columns roo-card-uploader-container'
12906 getChildContainer : function() /// what children are added to.
12908 return this.containerEl;
12911 getButtonContainer : function() /// what children are added to.
12913 return this.el.select(".roo-card-uploader-button-container").first();
12916 initEvents : function()
12919 Roo.bootstrap.Input.prototype.initEvents.call(this);
12923 xns: Roo.bootstrap,
12926 container_method : 'getButtonContainer' ,
12927 html : this.html, // fix changable?
12930 'click' : function(btn, e) {
12939 this.urlAPI = (window.createObjectURL && window) ||
12940 (window.URL && URL.revokeObjectURL && URL) ||
12941 (window.webkitURL && webkitURL);
12946 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12948 this.selectorEl.on('change', this.onFileSelected, this);
12951 this.images.forEach(function(img) {
12954 this.images = false;
12956 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12962 onClick : function(e)
12964 e.preventDefault();
12966 this.selectorEl.dom.click();
12970 onFileSelected : function(e)
12972 e.preventDefault();
12974 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12978 Roo.each(this.selectorEl.dom.files, function(file){
12979 this.addFile(file);
12988 addFile : function(file)
12991 if(typeof(file) === 'string'){
12992 throw "Add file by name?"; // should not happen
12996 if(!file || !this.urlAPI){
13006 var url = _this.urlAPI.createObjectURL( file);
13009 id : Roo.bootstrap.CardUploader.ID--,
13010 is_uploaded : false,
13014 mimetype : file.type,
13022 * addCard - add an Attachment to the uploader
13023 * @param data - the data about the image to upload
13027 title : "Title of file",
13028 is_uploaded : false,
13029 src : "http://.....",
13030 srcfile : { the File upload object },
13031 mimetype : file.type,
13034 .. any other data...
13040 addCard : function (data)
13042 // hidden input element?
13043 // if the file is not an image...
13044 //then we need to use something other that and header_image
13049 xns : Roo.bootstrap,
13050 xtype : 'CardFooter',
13053 xns : Roo.bootstrap,
13059 xns : Roo.bootstrap,
13061 html : String.format("<small>{0}</small>", data.title),
13062 cls : 'col-10 text-left',
13067 click : function() {
13069 t.fireEvent( "download", t, data );
13075 xns : Roo.bootstrap,
13077 style: 'max-height: 28px; ',
13083 click : function() {
13084 t.removeCard(data.id)
13096 var cn = this.addxtype(
13099 xns : Roo.bootstrap,
13102 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13103 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13104 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13109 initEvents : function() {
13110 Roo.bootstrap.Card.prototype.initEvents.call(this);
13112 this.imgEl = this.el.select('.card-img-top').first();
13114 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13115 this.imgEl.set({ 'pointer' : 'cursor' });
13118 this.getCardFooter().addClass('p-1');
13125 // dont' really need ot update items.
13126 // this.items.push(cn);
13127 this.fileCollection.add(cn);
13129 if (!data.srcfile) {
13130 this.updateInput();
13135 var reader = new FileReader();
13136 reader.addEventListener("load", function() {
13137 data.srcdata = reader.result;
13140 reader.readAsDataURL(data.srcfile);
13145 removeCard : function(id)
13148 var card = this.fileCollection.get(id);
13149 card.data.is_deleted = 1;
13150 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13151 //this.fileCollection.remove(card);
13152 //this.items = this.items.filter(function(e) { return e != card });
13153 // dont' really need ot update items.
13154 card.el.dom.parentNode.removeChild(card.el.dom);
13155 this.updateInput();
13161 this.fileCollection.each(function(card) {
13162 if (card.el.dom && card.el.dom.parentNode) {
13163 card.el.dom.parentNode.removeChild(card.el.dom);
13166 this.fileCollection.clear();
13167 this.updateInput();
13170 updateInput : function()
13173 this.fileCollection.each(function(e) {
13177 this.inputEl().dom.value = JSON.stringify(data);
13187 Roo.bootstrap.CardUploader.ID = -1;/*
13189 * Ext JS Library 1.1.1
13190 * Copyright(c) 2006-2007, Ext JS, LLC.
13192 * Originally Released Under LGPL - original licence link has changed is not relivant.
13195 * <script type="text/javascript">
13200 * @class Roo.data.SortTypes
13202 * Defines the default sorting (casting?) comparison functions used when sorting data.
13204 Roo.data.SortTypes = {
13206 * Default sort that does nothing
13207 * @param {Mixed} s The value being converted
13208 * @return {Mixed} The comparison value
13210 none : function(s){
13215 * The regular expression used to strip tags
13219 stripTagsRE : /<\/?[^>]+>/gi,
13222 * Strips all HTML tags to sort on text only
13223 * @param {Mixed} s The value being converted
13224 * @return {String} The comparison value
13226 asText : function(s){
13227 return String(s).replace(this.stripTagsRE, "");
13231 * Strips all HTML tags to sort on text only - Case insensitive
13232 * @param {Mixed} s The value being converted
13233 * @return {String} The comparison value
13235 asUCText : function(s){
13236 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13240 * Case insensitive string
13241 * @param {Mixed} s The value being converted
13242 * @return {String} The comparison value
13244 asUCString : function(s) {
13245 return String(s).toUpperCase();
13250 * @param {Mixed} s The value being converted
13251 * @return {Number} The comparison value
13253 asDate : function(s) {
13257 if(s instanceof Date){
13258 return s.getTime();
13260 return Date.parse(String(s));
13265 * @param {Mixed} s The value being converted
13266 * @return {Float} The comparison value
13268 asFloat : function(s) {
13269 var val = parseFloat(String(s).replace(/,/g, ""));
13278 * @param {Mixed} s The value being converted
13279 * @return {Number} The comparison value
13281 asInt : function(s) {
13282 var val = parseInt(String(s).replace(/,/g, ""));
13290 * Ext JS Library 1.1.1
13291 * Copyright(c) 2006-2007, Ext JS, LLC.
13293 * Originally Released Under LGPL - original licence link has changed is not relivant.
13296 * <script type="text/javascript">
13300 * @class Roo.data.Record
13301 * Instances of this class encapsulate both record <em>definition</em> information, and record
13302 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13303 * to access Records cached in an {@link Roo.data.Store} object.<br>
13305 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13306 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13309 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13311 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13312 * {@link #create}. The parameters are the same.
13313 * @param {Array} data An associative Array of data values keyed by the field name.
13314 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13315 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13316 * not specified an integer id is generated.
13318 Roo.data.Record = function(data, id){
13319 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13324 * Generate a constructor for a specific record layout.
13325 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13326 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13327 * Each field definition object may contain the following properties: <ul>
13328 * <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,
13329 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13330 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13331 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13332 * is being used, then this is a string containing the javascript expression to reference the data relative to
13333 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13334 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13335 * this may be omitted.</p></li>
13336 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13337 * <ul><li>auto (Default, implies no conversion)</li>
13342 * <li>date</li></ul></p></li>
13343 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13344 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13345 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13346 * by the Reader into an object that will be stored in the Record. It is passed the
13347 * following parameters:<ul>
13348 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13350 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13352 * <br>usage:<br><pre><code>
13353 var TopicRecord = Roo.data.Record.create(
13354 {name: 'title', mapping: 'topic_title'},
13355 {name: 'author', mapping: 'username'},
13356 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13357 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13358 {name: 'lastPoster', mapping: 'user2'},
13359 {name: 'excerpt', mapping: 'post_text'}
13362 var myNewRecord = new TopicRecord({
13363 title: 'Do my job please',
13366 lastPost: new Date(),
13367 lastPoster: 'Animal',
13368 excerpt: 'No way dude!'
13370 myStore.add(myNewRecord);
13375 Roo.data.Record.create = function(o){
13376 var f = function(){
13377 f.superclass.constructor.apply(this, arguments);
13379 Roo.extend(f, Roo.data.Record);
13380 var p = f.prototype;
13381 p.fields = new Roo.util.MixedCollection(false, function(field){
13384 for(var i = 0, len = o.length; i < len; i++){
13385 p.fields.add(new Roo.data.Field(o[i]));
13387 f.getField = function(name){
13388 return p.fields.get(name);
13393 Roo.data.Record.AUTO_ID = 1000;
13394 Roo.data.Record.EDIT = 'edit';
13395 Roo.data.Record.REJECT = 'reject';
13396 Roo.data.Record.COMMIT = 'commit';
13398 Roo.data.Record.prototype = {
13400 * Readonly flag - true if this record has been modified.
13409 join : function(store){
13410 this.store = store;
13414 * Set the named field to the specified value.
13415 * @param {String} name The name of the field to set.
13416 * @param {Object} value The value to set the field to.
13418 set : function(name, value){
13419 if(this.data[name] == value){
13423 if(!this.modified){
13424 this.modified = {};
13426 if(typeof this.modified[name] == 'undefined'){
13427 this.modified[name] = this.data[name];
13429 this.data[name] = value;
13430 if(!this.editing && this.store){
13431 this.store.afterEdit(this);
13436 * Get the value of the named field.
13437 * @param {String} name The name of the field to get the value of.
13438 * @return {Object} The value of the field.
13440 get : function(name){
13441 return this.data[name];
13445 beginEdit : function(){
13446 this.editing = true;
13447 this.modified = {};
13451 cancelEdit : function(){
13452 this.editing = false;
13453 delete this.modified;
13457 endEdit : function(){
13458 this.editing = false;
13459 if(this.dirty && this.store){
13460 this.store.afterEdit(this);
13465 * Usually called by the {@link Roo.data.Store} which owns the Record.
13466 * Rejects all changes made to the Record since either creation, or the last commit operation.
13467 * Modified fields are reverted to their original values.
13469 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13470 * of reject operations.
13472 reject : function(){
13473 var m = this.modified;
13475 if(typeof m[n] != "function"){
13476 this.data[n] = m[n];
13479 this.dirty = false;
13480 delete this.modified;
13481 this.editing = false;
13483 this.store.afterReject(this);
13488 * Usually called by the {@link Roo.data.Store} which owns the Record.
13489 * Commits all changes made to the Record since either creation, or the last commit operation.
13491 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13492 * of commit operations.
13494 commit : function(){
13495 this.dirty = false;
13496 delete this.modified;
13497 this.editing = false;
13499 this.store.afterCommit(this);
13504 hasError : function(){
13505 return this.error != null;
13509 clearError : function(){
13514 * Creates a copy of this record.
13515 * @param {String} id (optional) A new record id if you don't want to use this record's id
13518 copy : function(newId) {
13519 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13523 * Ext JS Library 1.1.1
13524 * Copyright(c) 2006-2007, Ext JS, LLC.
13526 * Originally Released Under LGPL - original licence link has changed is not relivant.
13529 * <script type="text/javascript">
13535 * @class Roo.data.Store
13536 * @extends Roo.util.Observable
13537 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13538 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13540 * 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
13541 * has no knowledge of the format of the data returned by the Proxy.<br>
13543 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13544 * instances from the data object. These records are cached and made available through accessor functions.
13546 * Creates a new Store.
13547 * @param {Object} config A config object containing the objects needed for the Store to access data,
13548 * and read the data into Records.
13550 Roo.data.Store = function(config){
13551 this.data = new Roo.util.MixedCollection(false);
13552 this.data.getKey = function(o){
13555 this.baseParams = {};
13557 this.paramNames = {
13562 "multisort" : "_multisort"
13565 if(config && config.data){
13566 this.inlineData = config.data;
13567 delete config.data;
13570 Roo.apply(this, config);
13572 if(this.reader){ // reader passed
13573 this.reader = Roo.factory(this.reader, Roo.data);
13574 this.reader.xmodule = this.xmodule || false;
13575 if(!this.recordType){
13576 this.recordType = this.reader.recordType;
13578 if(this.reader.onMetaChange){
13579 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13583 if(this.recordType){
13584 this.fields = this.recordType.prototype.fields;
13586 this.modified = [];
13590 * @event datachanged
13591 * Fires when the data cache has changed, and a widget which is using this Store
13592 * as a Record cache should refresh its view.
13593 * @param {Store} this
13595 datachanged : true,
13597 * @event metachange
13598 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13599 * @param {Store} this
13600 * @param {Object} meta The JSON metadata
13605 * Fires when Records have been added to the Store
13606 * @param {Store} this
13607 * @param {Roo.data.Record[]} records The array of Records added
13608 * @param {Number} index The index at which the record(s) were added
13613 * Fires when a Record has been removed from the Store
13614 * @param {Store} this
13615 * @param {Roo.data.Record} record The Record that was removed
13616 * @param {Number} index The index at which the record was removed
13621 * Fires when a Record has been updated
13622 * @param {Store} this
13623 * @param {Roo.data.Record} record The Record that was updated
13624 * @param {String} operation The update operation being performed. Value may be one of:
13626 Roo.data.Record.EDIT
13627 Roo.data.Record.REJECT
13628 Roo.data.Record.COMMIT
13634 * Fires when the data cache has been cleared.
13635 * @param {Store} this
13639 * @event beforeload
13640 * Fires before a request is made for a new data object. If the beforeload handler returns false
13641 * the load action will be canceled.
13642 * @param {Store} this
13643 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13647 * @event beforeloadadd
13648 * Fires after a new set of Records has been loaded.
13649 * @param {Store} this
13650 * @param {Roo.data.Record[]} records The Records that were loaded
13651 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13653 beforeloadadd : true,
13656 * Fires after a new set of Records has been loaded, before they are added to the store.
13657 * @param {Store} this
13658 * @param {Roo.data.Record[]} records The Records that were loaded
13659 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13660 * @params {Object} return from reader
13664 * @event loadexception
13665 * Fires if an exception occurs in the Proxy during loading.
13666 * Called with the signature of the Proxy's "loadexception" event.
13667 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13670 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13671 * @param {Object} load options
13672 * @param {Object} jsonData from your request (normally this contains the Exception)
13674 loadexception : true
13678 this.proxy = Roo.factory(this.proxy, Roo.data);
13679 this.proxy.xmodule = this.xmodule || false;
13680 this.relayEvents(this.proxy, ["loadexception"]);
13682 this.sortToggle = {};
13683 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13685 Roo.data.Store.superclass.constructor.call(this);
13687 if(this.inlineData){
13688 this.loadData(this.inlineData);
13689 delete this.inlineData;
13693 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13695 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13696 * without a remote query - used by combo/forms at present.
13700 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13703 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13706 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13707 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13710 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13711 * on any HTTP request
13714 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13717 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13721 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13722 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13724 remoteSort : false,
13727 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13728 * loaded or when a record is removed. (defaults to false).
13730 pruneModifiedRecords : false,
13733 lastOptions : null,
13736 * Add Records to the Store and fires the add event.
13737 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13739 add : function(records){
13740 records = [].concat(records);
13741 for(var i = 0, len = records.length; i < len; i++){
13742 records[i].join(this);
13744 var index = this.data.length;
13745 this.data.addAll(records);
13746 this.fireEvent("add", this, records, index);
13750 * Remove a Record from the Store and fires the remove event.
13751 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13753 remove : function(record){
13754 var index = this.data.indexOf(record);
13755 this.data.removeAt(index);
13757 if(this.pruneModifiedRecords){
13758 this.modified.remove(record);
13760 this.fireEvent("remove", this, record, index);
13764 * Remove all Records from the Store and fires the clear event.
13766 removeAll : function(){
13768 if(this.pruneModifiedRecords){
13769 this.modified = [];
13771 this.fireEvent("clear", this);
13775 * Inserts Records to the Store at the given index and fires the add event.
13776 * @param {Number} index The start index at which to insert the passed Records.
13777 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13779 insert : function(index, records){
13780 records = [].concat(records);
13781 for(var i = 0, len = records.length; i < len; i++){
13782 this.data.insert(index, records[i]);
13783 records[i].join(this);
13785 this.fireEvent("add", this, records, index);
13789 * Get the index within the cache of the passed Record.
13790 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13791 * @return {Number} The index of the passed Record. Returns -1 if not found.
13793 indexOf : function(record){
13794 return this.data.indexOf(record);
13798 * Get the index within the cache of the Record with the passed id.
13799 * @param {String} id The id of the Record to find.
13800 * @return {Number} The index of the Record. Returns -1 if not found.
13802 indexOfId : function(id){
13803 return this.data.indexOfKey(id);
13807 * Get the Record with the specified id.
13808 * @param {String} id The id of the Record to find.
13809 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13811 getById : function(id){
13812 return this.data.key(id);
13816 * Get the Record at the specified index.
13817 * @param {Number} index The index of the Record to find.
13818 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13820 getAt : function(index){
13821 return this.data.itemAt(index);
13825 * Returns a range of Records between specified indices.
13826 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13827 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13828 * @return {Roo.data.Record[]} An array of Records
13830 getRange : function(start, end){
13831 return this.data.getRange(start, end);
13835 storeOptions : function(o){
13836 o = Roo.apply({}, o);
13839 this.lastOptions = o;
13843 * Loads the Record cache from the configured Proxy using the configured Reader.
13845 * If using remote paging, then the first load call must specify the <em>start</em>
13846 * and <em>limit</em> properties in the options.params property to establish the initial
13847 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13849 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13850 * and this call will return before the new data has been loaded. Perform any post-processing
13851 * in a callback function, or in a "load" event handler.</strong>
13853 * @param {Object} options An object containing properties which control loading options:<ul>
13854 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13855 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13856 * passed the following arguments:<ul>
13857 * <li>r : Roo.data.Record[]</li>
13858 * <li>options: Options object from the load call</li>
13859 * <li>success: Boolean success indicator</li></ul></li>
13860 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13861 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13864 load : function(options){
13865 options = options || {};
13866 if(this.fireEvent("beforeload", this, options) !== false){
13867 this.storeOptions(options);
13868 var p = Roo.apply(options.params || {}, this.baseParams);
13869 // if meta was not loaded from remote source.. try requesting it.
13870 if (!this.reader.metaFromRemote) {
13871 p._requestMeta = 1;
13873 if(this.sortInfo && this.remoteSort){
13874 var pn = this.paramNames;
13875 p[pn["sort"]] = this.sortInfo.field;
13876 p[pn["dir"]] = this.sortInfo.direction;
13878 if (this.multiSort) {
13879 var pn = this.paramNames;
13880 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13883 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13888 * Reloads the Record cache from the configured Proxy using the configured Reader and
13889 * the options from the last load operation performed.
13890 * @param {Object} options (optional) An object containing properties which may override the options
13891 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13892 * the most recently used options are reused).
13894 reload : function(options){
13895 this.load(Roo.applyIf(options||{}, this.lastOptions));
13899 // Called as a callback by the Reader during a load operation.
13900 loadRecords : function(o, options, success){
13901 if(!o || success === false){
13902 if(success !== false){
13903 this.fireEvent("load", this, [], options, o);
13905 if(options.callback){
13906 options.callback.call(options.scope || this, [], options, false);
13910 // if data returned failure - throw an exception.
13911 if (o.success === false) {
13912 // show a message if no listener is registered.
13913 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13914 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13916 // loadmask wil be hooked into this..
13917 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13920 var r = o.records, t = o.totalRecords || r.length;
13922 this.fireEvent("beforeloadadd", this, r, options, o);
13924 if(!options || options.add !== true){
13925 if(this.pruneModifiedRecords){
13926 this.modified = [];
13928 for(var i = 0, len = r.length; i < len; i++){
13932 this.data = this.snapshot;
13933 delete this.snapshot;
13936 this.data.addAll(r);
13937 this.totalLength = t;
13939 this.fireEvent("datachanged", this);
13941 this.totalLength = Math.max(t, this.data.length+r.length);
13945 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13947 var e = new Roo.data.Record({});
13949 e.set(this.parent.displayField, this.parent.emptyTitle);
13950 e.set(this.parent.valueField, '');
13955 this.fireEvent("load", this, r, options, o);
13956 if(options.callback){
13957 options.callback.call(options.scope || this, r, options, true);
13963 * Loads data from a passed data block. A Reader which understands the format of the data
13964 * must have been configured in the constructor.
13965 * @param {Object} data The data block from which to read the Records. The format of the data expected
13966 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13967 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13969 loadData : function(o, append){
13970 var r = this.reader.readRecords(o);
13971 this.loadRecords(r, {add: append}, true);
13975 * using 'cn' the nested child reader read the child array into it's child stores.
13976 * @param {Object} rec The record with a 'children array
13978 loadDataFromChildren : function(rec)
13980 this.loadData(this.reader.toLoadData(rec));
13985 * Gets the number of cached records.
13987 * <em>If using paging, this may not be the total size of the dataset. If the data object
13988 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13989 * the data set size</em>
13991 getCount : function(){
13992 return this.data.length || 0;
13996 * Gets the total number of records in the dataset as returned by the server.
13998 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13999 * the dataset size</em>
14001 getTotalCount : function(){
14002 return this.totalLength || 0;
14006 * Returns the sort state of the Store as an object with two properties:
14008 field {String} The name of the field by which the Records are sorted
14009 direction {String} The sort order, "ASC" or "DESC"
14012 getSortState : function(){
14013 return this.sortInfo;
14017 applySort : function(){
14018 if(this.sortInfo && !this.remoteSort){
14019 var s = this.sortInfo, f = s.field;
14020 var st = this.fields.get(f).sortType;
14021 var fn = function(r1, r2){
14022 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14023 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14025 this.data.sort(s.direction, fn);
14026 if(this.snapshot && this.snapshot != this.data){
14027 this.snapshot.sort(s.direction, fn);
14033 * Sets the default sort column and order to be used by the next load operation.
14034 * @param {String} fieldName The name of the field to sort by.
14035 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14037 setDefaultSort : function(field, dir){
14038 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14042 * Sort the Records.
14043 * If remote sorting is used, the sort is performed on the server, and the cache is
14044 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
14049 var f = this.fields.get(fieldName);
14051 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14053 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14054 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14059 this.sortToggle[f.name] = dir;
14060 this.sortInfo = {field: f.name, direction: dir};
14061 if(!this.remoteSort){
14063 this.fireEvent("datachanged", this);
14065 this.load(this.lastOptions);
14070 * Calls the specified function for each of the Records in the cache.
14071 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14072 * Returning <em>false</em> aborts and exits the iteration.
14073 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14075 each : function(fn, scope){
14076 this.data.each(fn, scope);
14080 * Gets all records modified since the last commit. Modified records are persisted across load operations
14081 * (e.g., during paging).
14082 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14084 getModifiedRecords : function(){
14085 return this.modified;
14089 createFilterFn : function(property, value, anyMatch){
14090 if(!value.exec){ // not a regex
14091 value = String(value);
14092 if(value.length == 0){
14095 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14097 return function(r){
14098 return value.test(r.data[property]);
14103 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14104 * @param {String} property A field on your records
14105 * @param {Number} start The record index to start at (defaults to 0)
14106 * @param {Number} end The last record index to include (defaults to length - 1)
14107 * @return {Number} The sum
14109 sum : function(property, start, end){
14110 var rs = this.data.items, v = 0;
14111 start = start || 0;
14112 end = (end || end === 0) ? end : rs.length-1;
14114 for(var i = start; i <= end; i++){
14115 v += (rs[i].data[property] || 0);
14121 * Filter the records by a specified property.
14122 * @param {String} field A field on your records
14123 * @param {String/RegExp} value Either a string that the field
14124 * should start with or a RegExp to test against the field
14125 * @param {Boolean} anyMatch True to match any part not just the beginning
14127 filter : function(property, value, anyMatch){
14128 var fn = this.createFilterFn(property, value, anyMatch);
14129 return fn ? this.filterBy(fn) : this.clearFilter();
14133 * Filter by a function. The specified function will be called with each
14134 * record in this data source. If the function returns true the record is included,
14135 * otherwise it is filtered.
14136 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14137 * @param {Object} scope (optional) The scope of the function (defaults to this)
14139 filterBy : function(fn, scope){
14140 this.snapshot = this.snapshot || this.data;
14141 this.data = this.queryBy(fn, scope||this);
14142 this.fireEvent("datachanged", this);
14146 * Query the records by a specified property.
14147 * @param {String} field A field on your records
14148 * @param {String/RegExp} value Either a string that the field
14149 * should start with or a RegExp to test against the field
14150 * @param {Boolean} anyMatch True to match any part not just the beginning
14151 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14153 query : function(property, value, anyMatch){
14154 var fn = this.createFilterFn(property, value, anyMatch);
14155 return fn ? this.queryBy(fn) : this.data.clone();
14159 * Query by a function. The specified function will be called with each
14160 * record in this data source. If the function returns true the record is included
14162 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14163 * @param {Object} scope (optional) The scope of the function (defaults to this)
14164 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14166 queryBy : function(fn, scope){
14167 var data = this.snapshot || this.data;
14168 return data.filterBy(fn, scope||this);
14172 * Collects unique values for a particular dataIndex from this store.
14173 * @param {String} dataIndex The property to collect
14174 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14175 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14176 * @return {Array} An array of the unique values
14178 collect : function(dataIndex, allowNull, bypassFilter){
14179 var d = (bypassFilter === true && this.snapshot) ?
14180 this.snapshot.items : this.data.items;
14181 var v, sv, r = [], l = {};
14182 for(var i = 0, len = d.length; i < len; i++){
14183 v = d[i].data[dataIndex];
14185 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14194 * Revert to a view of the Record cache with no filtering applied.
14195 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14197 clearFilter : function(suppressEvent){
14198 if(this.snapshot && this.snapshot != this.data){
14199 this.data = this.snapshot;
14200 delete this.snapshot;
14201 if(suppressEvent !== true){
14202 this.fireEvent("datachanged", this);
14208 afterEdit : function(record){
14209 if(this.modified.indexOf(record) == -1){
14210 this.modified.push(record);
14212 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14216 afterReject : function(record){
14217 this.modified.remove(record);
14218 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14222 afterCommit : function(record){
14223 this.modified.remove(record);
14224 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14228 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14229 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14231 commitChanges : function(){
14232 var m = this.modified.slice(0);
14233 this.modified = [];
14234 for(var i = 0, len = m.length; i < len; i++){
14240 * Cancel outstanding changes on all changed records.
14242 rejectChanges : function(){
14243 var m = this.modified.slice(0);
14244 this.modified = [];
14245 for(var i = 0, len = m.length; i < len; i++){
14250 onMetaChange : function(meta, rtype, o){
14251 this.recordType = rtype;
14252 this.fields = rtype.prototype.fields;
14253 delete this.snapshot;
14254 this.sortInfo = meta.sortInfo || this.sortInfo;
14255 this.modified = [];
14256 this.fireEvent('metachange', this, this.reader.meta);
14259 moveIndex : function(data, type)
14261 var index = this.indexOf(data);
14263 var newIndex = index + type;
14267 this.insert(newIndex, data);
14272 * Ext JS Library 1.1.1
14273 * Copyright(c) 2006-2007, Ext JS, LLC.
14275 * Originally Released Under LGPL - original licence link has changed is not relivant.
14278 * <script type="text/javascript">
14282 * @class Roo.data.SimpleStore
14283 * @extends Roo.data.Store
14284 * Small helper class to make creating Stores from Array data easier.
14285 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14286 * @cfg {Array} fields An array of field definition objects, or field name strings.
14287 * @cfg {Object} an existing reader (eg. copied from another store)
14288 * @cfg {Array} data The multi-dimensional array of data
14290 * @param {Object} config
14292 Roo.data.SimpleStore = function(config)
14294 Roo.data.SimpleStore.superclass.constructor.call(this, {
14296 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14299 Roo.data.Record.create(config.fields)
14301 proxy : new Roo.data.MemoryProxy(config.data)
14305 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14307 * Ext JS Library 1.1.1
14308 * Copyright(c) 2006-2007, Ext JS, LLC.
14310 * Originally Released Under LGPL - original licence link has changed is not relivant.
14313 * <script type="text/javascript">
14318 * @extends Roo.data.Store
14319 * @class Roo.data.JsonStore
14320 * Small helper class to make creating Stores for JSON data easier. <br/>
14322 var store = new Roo.data.JsonStore({
14323 url: 'get-images.php',
14325 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14328 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14329 * JsonReader and HttpProxy (unless inline data is provided).</b>
14330 * @cfg {Array} fields An array of field definition objects, or field name strings.
14332 * @param {Object} config
14334 Roo.data.JsonStore = function(c){
14335 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14336 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14337 reader: new Roo.data.JsonReader(c, c.fields)
14340 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14342 * Ext JS Library 1.1.1
14343 * Copyright(c) 2006-2007, Ext JS, LLC.
14345 * Originally Released Under LGPL - original licence link has changed is not relivant.
14348 * <script type="text/javascript">
14352 Roo.data.Field = function(config){
14353 if(typeof config == "string"){
14354 config = {name: config};
14356 Roo.apply(this, config);
14359 this.type = "auto";
14362 var st = Roo.data.SortTypes;
14363 // named sortTypes are supported, here we look them up
14364 if(typeof this.sortType == "string"){
14365 this.sortType = st[this.sortType];
14368 // set default sortType for strings and dates
14369 if(!this.sortType){
14372 this.sortType = st.asUCString;
14375 this.sortType = st.asDate;
14378 this.sortType = st.none;
14383 var stripRe = /[\$,%]/g;
14385 // prebuilt conversion function for this field, instead of
14386 // switching every time we're reading a value
14388 var cv, dateFormat = this.dateFormat;
14393 cv = function(v){ return v; };
14396 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14400 return v !== undefined && v !== null && v !== '' ?
14401 parseInt(String(v).replace(stripRe, ""), 10) : '';
14406 return v !== undefined && v !== null && v !== '' ?
14407 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14412 cv = function(v){ return v === true || v === "true" || v == 1; };
14419 if(v instanceof Date){
14423 if(dateFormat == "timestamp"){
14424 return new Date(v*1000);
14426 return Date.parseDate(v, dateFormat);
14428 var parsed = Date.parse(v);
14429 return parsed ? new Date(parsed) : null;
14438 Roo.data.Field.prototype = {
14446 * Ext JS Library 1.1.1
14447 * Copyright(c) 2006-2007, Ext JS, LLC.
14449 * Originally Released Under LGPL - original licence link has changed is not relivant.
14452 * <script type="text/javascript">
14455 // Base class for reading structured data from a data source. This class is intended to be
14456 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14459 * @class Roo.data.DataReader
14460 * Base class for reading structured data from a data source. This class is intended to be
14461 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14464 Roo.data.DataReader = function(meta, recordType){
14468 this.recordType = recordType instanceof Array ?
14469 Roo.data.Record.create(recordType) : recordType;
14472 Roo.data.DataReader.prototype = {
14475 readerType : 'Data',
14477 * Create an empty record
14478 * @param {Object} data (optional) - overlay some values
14479 * @return {Roo.data.Record} record created.
14481 newRow : function(d) {
14483 this.recordType.prototype.fields.each(function(c) {
14485 case 'int' : da[c.name] = 0; break;
14486 case 'date' : da[c.name] = new Date(); break;
14487 case 'float' : da[c.name] = 0.0; break;
14488 case 'boolean' : da[c.name] = false; break;
14489 default : da[c.name] = ""; break;
14493 return new this.recordType(Roo.apply(da, d));
14499 * Ext JS Library 1.1.1
14500 * Copyright(c) 2006-2007, Ext JS, LLC.
14502 * Originally Released Under LGPL - original licence link has changed is not relivant.
14505 * <script type="text/javascript">
14509 * @class Roo.data.DataProxy
14510 * @extends Roo.data.Observable
14511 * This class is an abstract base class for implementations which provide retrieval of
14512 * unformatted data objects.<br>
14514 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14515 * (of the appropriate type which knows how to parse the data object) to provide a block of
14516 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14518 * Custom implementations must implement the load method as described in
14519 * {@link Roo.data.HttpProxy#load}.
14521 Roo.data.DataProxy = function(){
14524 * @event beforeload
14525 * Fires before a network request is made to retrieve a data object.
14526 * @param {Object} This DataProxy object.
14527 * @param {Object} params The params parameter to the load function.
14532 * Fires before the load method's callback is called.
14533 * @param {Object} This DataProxy object.
14534 * @param {Object} o The data object.
14535 * @param {Object} arg The callback argument object passed to the load function.
14539 * @event loadexception
14540 * Fires if an Exception occurs during data retrieval.
14541 * @param {Object} This DataProxy object.
14542 * @param {Object} o The data object.
14543 * @param {Object} arg The callback argument object passed to the load function.
14544 * @param {Object} e The Exception.
14546 loadexception : true
14548 Roo.data.DataProxy.superclass.constructor.call(this);
14551 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14554 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14558 * Ext JS Library 1.1.1
14559 * Copyright(c) 2006-2007, Ext JS, LLC.
14561 * Originally Released Under LGPL - original licence link has changed is not relivant.
14564 * <script type="text/javascript">
14567 * @class Roo.data.MemoryProxy
14568 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14569 * to the Reader when its load method is called.
14571 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14573 Roo.data.MemoryProxy = function(data){
14577 Roo.data.MemoryProxy.superclass.constructor.call(this);
14581 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14584 * Load data from the requested source (in this case an in-memory
14585 * data object passed to the constructor), read the data object into
14586 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14587 * process that block using the passed callback.
14588 * @param {Object} params This parameter is not used by the MemoryProxy class.
14589 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14590 * object into a block of Roo.data.Records.
14591 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14592 * The function must be passed <ul>
14593 * <li>The Record block object</li>
14594 * <li>The "arg" argument from the load function</li>
14595 * <li>A boolean success indicator</li>
14597 * @param {Object} scope The scope in which to call the callback
14598 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14600 load : function(params, reader, callback, scope, arg){
14601 params = params || {};
14604 result = reader.readRecords(params.data ? params.data :this.data);
14606 this.fireEvent("loadexception", this, arg, null, e);
14607 callback.call(scope, null, arg, false);
14610 callback.call(scope, result, arg, true);
14614 update : function(params, records){
14619 * Ext JS Library 1.1.1
14620 * Copyright(c) 2006-2007, Ext JS, LLC.
14622 * Originally Released Under LGPL - original licence link has changed is not relivant.
14625 * <script type="text/javascript">
14628 * @class Roo.data.HttpProxy
14629 * @extends Roo.data.DataProxy
14630 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14631 * configured to reference a certain URL.<br><br>
14633 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14634 * from which the running page was served.<br><br>
14636 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14638 * Be aware that to enable the browser to parse an XML document, the server must set
14639 * the Content-Type header in the HTTP response to "text/xml".
14641 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14642 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14643 * will be used to make the request.
14645 Roo.data.HttpProxy = function(conn){
14646 Roo.data.HttpProxy.superclass.constructor.call(this);
14647 // is conn a conn config or a real conn?
14649 this.useAjax = !conn || !conn.events;
14653 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14654 // thse are take from connection...
14657 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14660 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14661 * extra parameters to each request made by this object. (defaults to undefined)
14664 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14665 * to each request made by this object. (defaults to undefined)
14668 * @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)
14671 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14674 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14680 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14684 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14685 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14686 * a finer-grained basis than the DataProxy events.
14688 getConnection : function(){
14689 return this.useAjax ? Roo.Ajax : this.conn;
14693 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14694 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14695 * process that block using the passed callback.
14696 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14697 * for the request to the remote server.
14698 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14699 * object into a block of Roo.data.Records.
14700 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14701 * The function must be passed <ul>
14702 * <li>The Record block object</li>
14703 * <li>The "arg" argument from the load function</li>
14704 * <li>A boolean success indicator</li>
14706 * @param {Object} scope The scope in which to call the callback
14707 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14709 load : function(params, reader, callback, scope, arg){
14710 if(this.fireEvent("beforeload", this, params) !== false){
14712 params : params || {},
14714 callback : callback,
14719 callback : this.loadResponse,
14723 Roo.applyIf(o, this.conn);
14724 if(this.activeRequest){
14725 Roo.Ajax.abort(this.activeRequest);
14727 this.activeRequest = Roo.Ajax.request(o);
14729 this.conn.request(o);
14732 callback.call(scope||this, null, arg, false);
14737 loadResponse : function(o, success, response){
14738 delete this.activeRequest;
14740 this.fireEvent("loadexception", this, o, response);
14741 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14746 result = o.reader.read(response);
14748 this.fireEvent("loadexception", this, o, response, e);
14749 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14753 this.fireEvent("load", this, o, o.request.arg);
14754 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14758 update : function(dataSet){
14763 updateResponse : function(dataSet){
14768 * Ext JS Library 1.1.1
14769 * Copyright(c) 2006-2007, Ext JS, LLC.
14771 * Originally Released Under LGPL - original licence link has changed is not relivant.
14774 * <script type="text/javascript">
14778 * @class Roo.data.ScriptTagProxy
14779 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14780 * other than the originating domain of the running page.<br><br>
14782 * <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
14783 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14785 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14786 * source code that is used as the source inside a <script> tag.<br><br>
14788 * In order for the browser to process the returned data, the server must wrap the data object
14789 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14790 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14791 * depending on whether the callback name was passed:
14794 boolean scriptTag = false;
14795 String cb = request.getParameter("callback");
14798 response.setContentType("text/javascript");
14800 response.setContentType("application/x-json");
14802 Writer out = response.getWriter();
14804 out.write(cb + "(");
14806 out.print(dataBlock.toJsonString());
14813 * @param {Object} config A configuration object.
14815 Roo.data.ScriptTagProxy = function(config){
14816 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14817 Roo.apply(this, config);
14818 this.head = document.getElementsByTagName("head")[0];
14821 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14823 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14825 * @cfg {String} url The URL from which to request the data object.
14828 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14832 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14833 * the server the name of the callback function set up by the load call to process the returned data object.
14834 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14835 * javascript output which calls this named function passing the data object as its only parameter.
14837 callbackParam : "callback",
14839 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14840 * name to the request.
14845 * Load data from the configured URL, read the data object into
14846 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14847 * process that block using the passed callback.
14848 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14849 * for the request to the remote server.
14850 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14851 * object into a block of Roo.data.Records.
14852 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14853 * The function must be passed <ul>
14854 * <li>The Record block object</li>
14855 * <li>The "arg" argument from the load function</li>
14856 * <li>A boolean success indicator</li>
14858 * @param {Object} scope The scope in which to call the callback
14859 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14861 load : function(params, reader, callback, scope, arg){
14862 if(this.fireEvent("beforeload", this, params) !== false){
14864 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14866 var url = this.url;
14867 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14869 url += "&_dc=" + (new Date().getTime());
14871 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14874 cb : "stcCallback"+transId,
14875 scriptId : "stcScript"+transId,
14879 callback : callback,
14885 window[trans.cb] = function(o){
14886 conn.handleResponse(o, trans);
14889 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14891 if(this.autoAbort !== false){
14895 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14897 var script = document.createElement("script");
14898 script.setAttribute("src", url);
14899 script.setAttribute("type", "text/javascript");
14900 script.setAttribute("id", trans.scriptId);
14901 this.head.appendChild(script);
14903 this.trans = trans;
14905 callback.call(scope||this, null, arg, false);
14910 isLoading : function(){
14911 return this.trans ? true : false;
14915 * Abort the current server request.
14917 abort : function(){
14918 if(this.isLoading()){
14919 this.destroyTrans(this.trans);
14924 destroyTrans : function(trans, isLoaded){
14925 this.head.removeChild(document.getElementById(trans.scriptId));
14926 clearTimeout(trans.timeoutId);
14928 window[trans.cb] = undefined;
14930 delete window[trans.cb];
14933 // if hasn't been loaded, wait for load to remove it to prevent script error
14934 window[trans.cb] = function(){
14935 window[trans.cb] = undefined;
14937 delete window[trans.cb];
14944 handleResponse : function(o, trans){
14945 this.trans = false;
14946 this.destroyTrans(trans, true);
14949 result = trans.reader.readRecords(o);
14951 this.fireEvent("loadexception", this, o, trans.arg, e);
14952 trans.callback.call(trans.scope||window, null, trans.arg, false);
14955 this.fireEvent("load", this, o, trans.arg);
14956 trans.callback.call(trans.scope||window, result, trans.arg, true);
14960 handleFailure : function(trans){
14961 this.trans = false;
14962 this.destroyTrans(trans, false);
14963 this.fireEvent("loadexception", this, null, trans.arg);
14964 trans.callback.call(trans.scope||window, null, trans.arg, false);
14968 * Ext JS Library 1.1.1
14969 * Copyright(c) 2006-2007, Ext JS, LLC.
14971 * Originally Released Under LGPL - original licence link has changed is not relivant.
14974 * <script type="text/javascript">
14978 * @class Roo.data.JsonReader
14979 * @extends Roo.data.DataReader
14980 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14981 * based on mappings in a provided Roo.data.Record constructor.
14983 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14984 * in the reply previously.
14989 var RecordDef = Roo.data.Record.create([
14990 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14991 {name: 'occupation'} // This field will use "occupation" as the mapping.
14993 var myReader = new Roo.data.JsonReader({
14994 totalProperty: "results", // The property which contains the total dataset size (optional)
14995 root: "rows", // The property which contains an Array of row objects
14996 id: "id" // The property within each row object that provides an ID for the record (optional)
15000 * This would consume a JSON file like this:
15002 { 'results': 2, 'rows': [
15003 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15004 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15007 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15008 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15009 * paged from the remote server.
15010 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15011 * @cfg {String} root name of the property which contains the Array of row objects.
15012 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15013 * @cfg {Array} fields Array of field definition objects
15015 * Create a new JsonReader
15016 * @param {Object} meta Metadata configuration options
15017 * @param {Object} recordType Either an Array of field definition objects,
15018 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15020 Roo.data.JsonReader = function(meta, recordType){
15023 // set some defaults:
15024 Roo.applyIf(meta, {
15025 totalProperty: 'total',
15026 successProperty : 'success',
15031 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15033 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15035 readerType : 'Json',
15038 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15039 * Used by Store query builder to append _requestMeta to params.
15042 metaFromRemote : false,
15044 * This method is only used by a DataProxy which has retrieved data from a remote server.
15045 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15046 * @return {Object} data A data block which is used by an Roo.data.Store object as
15047 * a cache of Roo.data.Records.
15049 read : function(response){
15050 var json = response.responseText;
15052 var o = /* eval:var:o */ eval("("+json+")");
15054 throw {message: "JsonReader.read: Json object not found"};
15060 this.metaFromRemote = true;
15061 this.meta = o.metaData;
15062 this.recordType = Roo.data.Record.create(o.metaData.fields);
15063 this.onMetaChange(this.meta, this.recordType, o);
15065 return this.readRecords(o);
15068 // private function a store will implement
15069 onMetaChange : function(meta, recordType, o){
15076 simpleAccess: function(obj, subsc) {
15083 getJsonAccessor: function(){
15085 return function(expr) {
15087 return(re.test(expr))
15088 ? new Function("obj", "return obj." + expr)
15093 return Roo.emptyFn;
15098 * Create a data block containing Roo.data.Records from an XML document.
15099 * @param {Object} o An object which contains an Array of row objects in the property specified
15100 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15101 * which contains the total size of the dataset.
15102 * @return {Object} data A data block which is used by an Roo.data.Store object as
15103 * a cache of Roo.data.Records.
15105 readRecords : function(o){
15107 * After any data loads, the raw JSON data is available for further custom processing.
15111 var s = this.meta, Record = this.recordType,
15112 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15114 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15116 if(s.totalProperty) {
15117 this.getTotal = this.getJsonAccessor(s.totalProperty);
15119 if(s.successProperty) {
15120 this.getSuccess = this.getJsonAccessor(s.successProperty);
15122 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15124 var g = this.getJsonAccessor(s.id);
15125 this.getId = function(rec) {
15127 return (r === undefined || r === "") ? null : r;
15130 this.getId = function(){return null;};
15133 for(var jj = 0; jj < fl; jj++){
15135 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15136 this.ef[jj] = this.getJsonAccessor(map);
15140 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15141 if(s.totalProperty){
15142 var vt = parseInt(this.getTotal(o), 10);
15147 if(s.successProperty){
15148 var vs = this.getSuccess(o);
15149 if(vs === false || vs === 'false'){
15154 for(var i = 0; i < c; i++){
15157 var id = this.getId(n);
15158 for(var j = 0; j < fl; j++){
15160 var v = this.ef[j](n);
15162 Roo.log('missing convert for ' + f.name);
15166 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15168 var record = new Record(values, id);
15170 records[i] = record;
15176 totalRecords : totalRecords
15179 // used when loading children.. @see loadDataFromChildren
15180 toLoadData: function(rec)
15182 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15183 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15184 return { data : data, total : data.length };
15189 * Ext JS Library 1.1.1
15190 * Copyright(c) 2006-2007, Ext JS, LLC.
15192 * Originally Released Under LGPL - original licence link has changed is not relivant.
15195 * <script type="text/javascript">
15199 * @class Roo.data.ArrayReader
15200 * @extends Roo.data.DataReader
15201 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15202 * Each element of that Array represents a row of data fields. The
15203 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15204 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15208 var RecordDef = Roo.data.Record.create([
15209 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15210 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15212 var myReader = new Roo.data.ArrayReader({
15213 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15217 * This would consume an Array like this:
15219 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15223 * Create a new JsonReader
15224 * @param {Object} meta Metadata configuration options.
15225 * @param {Object|Array} recordType Either an Array of field definition objects
15227 * @cfg {Array} fields Array of field definition objects
15228 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15229 * as specified to {@link Roo.data.Record#create},
15230 * or an {@link Roo.data.Record} object
15233 * created using {@link Roo.data.Record#create}.
15235 Roo.data.ArrayReader = function(meta, recordType)
15237 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15240 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15243 * Create a data block containing Roo.data.Records from an XML document.
15244 * @param {Object} o An Array of row objects which represents the dataset.
15245 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15246 * a cache of Roo.data.Records.
15248 readRecords : function(o)
15250 var sid = this.meta ? this.meta.id : null;
15251 var recordType = this.recordType, fields = recordType.prototype.fields;
15254 for(var i = 0; i < root.length; i++){
15257 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15258 for(var j = 0, jlen = fields.length; j < jlen; j++){
15259 var f = fields.items[j];
15260 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15261 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15263 values[f.name] = v;
15265 var record = new recordType(values, id);
15267 records[records.length] = record;
15271 totalRecords : records.length
15274 // used when loading children.. @see loadDataFromChildren
15275 toLoadData: function(rec)
15277 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15278 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15289 * @class Roo.bootstrap.ComboBox
15290 * @extends Roo.bootstrap.TriggerField
15291 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15292 * @cfg {Boolean} append (true|false) default false
15293 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15294 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15295 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15296 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15297 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15298 * @cfg {Boolean} animate default true
15299 * @cfg {Boolean} emptyResultText only for touch device
15300 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15301 * @cfg {String} emptyTitle default ''
15302 * @cfg {Number} width fixed with? experimental
15304 * Create a new ComboBox.
15305 * @param {Object} config Configuration options
15307 Roo.bootstrap.ComboBox = function(config){
15308 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15312 * Fires when the dropdown list is expanded
15313 * @param {Roo.bootstrap.ComboBox} combo This combo box
15318 * Fires when the dropdown list is collapsed
15319 * @param {Roo.bootstrap.ComboBox} combo This combo box
15323 * @event beforeselect
15324 * Fires before a list item is selected. Return false to cancel the selection.
15325 * @param {Roo.bootstrap.ComboBox} combo This combo box
15326 * @param {Roo.data.Record} record The data record returned from the underlying store
15327 * @param {Number} index The index of the selected item in the dropdown list
15329 'beforeselect' : true,
15332 * Fires when a list item is selected
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15334 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15335 * @param {Number} index The index of the selected item in the dropdown list
15339 * @event beforequery
15340 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15341 * The event object passed has these properties:
15342 * @param {Roo.bootstrap.ComboBox} combo This combo box
15343 * @param {String} query The query
15344 * @param {Boolean} forceAll true to force "all" query
15345 * @param {Boolean} cancel true to cancel the query
15346 * @param {Object} e The query event object
15348 'beforequery': true,
15351 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15357 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15358 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15364 * Fires when the remove value from the combobox array
15365 * @param {Roo.bootstrap.ComboBox} combo This combo box
15369 * @event afterremove
15370 * Fires when the remove value from the combobox array
15371 * @param {Roo.bootstrap.ComboBox} combo This combo box
15373 'afterremove' : true,
15375 * @event specialfilter
15376 * Fires when specialfilter
15377 * @param {Roo.bootstrap.ComboBox} combo This combo box
15379 'specialfilter' : true,
15382 * Fires when tick the element
15383 * @param {Roo.bootstrap.ComboBox} combo This combo box
15387 * @event touchviewdisplay
15388 * Fires when touch view require special display (default is using displayField)
15389 * @param {Roo.bootstrap.ComboBox} combo This combo box
15390 * @param {Object} cfg set html .
15392 'touchviewdisplay' : true
15397 this.tickItems = [];
15399 this.selectedIndex = -1;
15400 if(this.mode == 'local'){
15401 if(config.queryDelay === undefined){
15402 this.queryDelay = 10;
15404 if(config.minChars === undefined){
15410 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15413 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15414 * rendering into an Roo.Editor, defaults to false)
15417 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15418 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15421 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15424 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15425 * the dropdown list (defaults to undefined, with no header element)
15429 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15433 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15435 listWidth: undefined,
15437 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15438 * mode = 'remote' or 'text' if mode = 'local')
15440 displayField: undefined,
15443 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15444 * mode = 'remote' or 'value' if mode = 'local').
15445 * Note: use of a valueField requires the user make a selection
15446 * in order for a value to be mapped.
15448 valueField: undefined,
15450 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15455 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15456 * field's data value (defaults to the underlying DOM element's name)
15458 hiddenName: undefined,
15460 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15464 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15466 selectedClass: 'active',
15469 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15473 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15474 * anchor positions (defaults to 'tl-bl')
15476 listAlign: 'tl-bl?',
15478 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15482 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15483 * query specified by the allQuery config option (defaults to 'query')
15485 triggerAction: 'query',
15487 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15488 * (defaults to 4, does not apply if editable = false)
15492 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15493 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15497 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15498 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15502 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15503 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15507 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15508 * when editable = true (defaults to false)
15510 selectOnFocus:false,
15512 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15514 queryParam: 'query',
15516 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15517 * when mode = 'remote' (defaults to 'Loading...')
15519 loadingText: 'Loading...',
15521 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15525 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15529 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15530 * traditional select (defaults to true)
15534 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15538 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15542 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15543 * listWidth has a higher value)
15547 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15548 * allow the user to set arbitrary text into the field (defaults to false)
15550 forceSelection:false,
15552 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15553 * if typeAhead = true (defaults to 250)
15555 typeAheadDelay : 250,
15557 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15558 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15560 valueNotFoundText : undefined,
15562 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15564 blockFocus : false,
15567 * @cfg {Boolean} disableClear Disable showing of clear button.
15569 disableClear : false,
15571 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15573 alwaysQuery : false,
15576 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15581 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15583 invalidClass : "has-warning",
15586 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15588 validClass : "has-success",
15591 * @cfg {Boolean} specialFilter (true|false) special filter default false
15593 specialFilter : false,
15596 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15598 mobileTouchView : true,
15601 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15603 useNativeIOS : false,
15606 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15608 mobile_restrict_height : false,
15610 ios_options : false,
15622 btnPosition : 'right',
15623 triggerList : true,
15624 showToggleBtn : true,
15626 emptyResultText: 'Empty',
15627 triggerText : 'Select',
15631 // element that contains real text value.. (when hidden is used..)
15633 getAutoCreate : function()
15638 * Render classic select for iso
15641 if(Roo.isIOS && this.useNativeIOS){
15642 cfg = this.getAutoCreateNativeIOS();
15650 if(Roo.isTouch && this.mobileTouchView){
15651 cfg = this.getAutoCreateTouchView();
15658 if(!this.tickable){
15659 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15664 * ComboBox with tickable selections
15667 var align = this.labelAlign || this.parentLabelAlign();
15670 cls : 'form-group roo-combobox-tickable' //input-group
15673 var btn_text_select = '';
15674 var btn_text_done = '';
15675 var btn_text_cancel = '';
15677 if (this.btn_text_show) {
15678 btn_text_select = 'Select';
15679 btn_text_done = 'Done';
15680 btn_text_cancel = 'Cancel';
15685 cls : 'tickable-buttons',
15690 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15691 //html : this.triggerText
15692 html: btn_text_select
15698 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15700 html: btn_text_done
15706 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15708 html: btn_text_cancel
15714 buttons.cn.unshift({
15716 cls: 'roo-select2-search-field-input'
15722 Roo.each(buttons.cn, function(c){
15724 c.cls += ' btn-' + _this.size;
15727 if (_this.disabled) {
15734 style : 'display: contents',
15739 cls: 'form-hidden-field'
15743 cls: 'roo-select2-choices',
15747 cls: 'roo-select2-search-field',
15758 cls: 'roo-select2-container input-group roo-select2-container-multi',
15764 // cls: 'typeahead typeahead-long dropdown-menu',
15765 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15770 if(this.hasFeedback && !this.allowBlank){
15774 cls: 'glyphicon form-control-feedback'
15777 combobox.cn.push(feedback);
15784 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15785 tooltip : 'This field is required'
15787 if (Roo.bootstrap.version == 4) {
15790 style : 'display:none'
15793 if (align ==='left' && this.fieldLabel.length) {
15795 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15802 cls : 'control-label col-form-label',
15803 html : this.fieldLabel
15815 var labelCfg = cfg.cn[1];
15816 var contentCfg = cfg.cn[2];
15819 if(this.indicatorpos == 'right'){
15825 cls : 'control-label col-form-label',
15829 html : this.fieldLabel
15845 labelCfg = cfg.cn[0];
15846 contentCfg = cfg.cn[1];
15850 if(this.labelWidth > 12){
15851 labelCfg.style = "width: " + this.labelWidth + 'px';
15853 if(this.width * 1 > 0){
15854 contentCfg.style = "width: " + this.width + 'px';
15856 if(this.labelWidth < 13 && this.labelmd == 0){
15857 this.labelmd = this.labelWidth;
15860 if(this.labellg > 0){
15861 labelCfg.cls += ' col-lg-' + this.labellg;
15862 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15865 if(this.labelmd > 0){
15866 labelCfg.cls += ' col-md-' + this.labelmd;
15867 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15870 if(this.labelsm > 0){
15871 labelCfg.cls += ' col-sm-' + this.labelsm;
15872 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15875 if(this.labelxs > 0){
15876 labelCfg.cls += ' col-xs-' + this.labelxs;
15877 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15881 } else if ( this.fieldLabel.length) {
15882 // Roo.log(" label");
15887 //cls : 'input-group-addon',
15888 html : this.fieldLabel
15893 if(this.indicatorpos == 'right'){
15897 //cls : 'input-group-addon',
15898 html : this.fieldLabel
15908 // Roo.log(" no label && no align");
15915 ['xs','sm','md','lg'].map(function(size){
15916 if (settings[size]) {
15917 cfg.cls += ' col-' + size + '-' + settings[size];
15925 _initEventsCalled : false,
15928 initEvents: function()
15930 if (this._initEventsCalled) { // as we call render... prevent looping...
15933 this._initEventsCalled = true;
15936 throw "can not find store for combo";
15939 this.indicator = this.indicatorEl();
15941 this.store = Roo.factory(this.store, Roo.data);
15942 this.store.parent = this;
15944 // if we are building from html. then this element is so complex, that we can not really
15945 // use the rendered HTML.
15946 // so we have to trash and replace the previous code.
15947 if (Roo.XComponent.build_from_html) {
15948 // remove this element....
15949 var e = this.el.dom, k=0;
15950 while (e ) { e = e.previousSibling; ++k;}
15955 this.rendered = false;
15957 this.render(this.parent().getChildContainer(true), k);
15960 if(Roo.isIOS && this.useNativeIOS){
15961 this.initIOSView();
15969 if(Roo.isTouch && this.mobileTouchView){
15970 this.initTouchView();
15975 this.initTickableEvents();
15979 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15981 if(this.hiddenName){
15983 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15985 this.hiddenField.dom.value =
15986 this.hiddenValue !== undefined ? this.hiddenValue :
15987 this.value !== undefined ? this.value : '';
15989 // prevent input submission
15990 this.el.dom.removeAttribute('name');
15991 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15996 // this.el.dom.setAttribute('autocomplete', 'off');
15999 var cls = 'x-combo-list';
16001 //this.list = new Roo.Layer({
16002 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16008 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16009 _this.list.setWidth(lw);
16012 this.list.on('mouseover', this.onViewOver, this);
16013 this.list.on('mousemove', this.onViewMove, this);
16014 this.list.on('scroll', this.onViewScroll, this);
16017 this.list.swallowEvent('mousewheel');
16018 this.assetHeight = 0;
16021 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16022 this.assetHeight += this.header.getHeight();
16025 this.innerList = this.list.createChild({cls:cls+'-inner'});
16026 this.innerList.on('mouseover', this.onViewOver, this);
16027 this.innerList.on('mousemove', this.onViewMove, this);
16028 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16030 if(this.allowBlank && !this.pageSize && !this.disableClear){
16031 this.footer = this.list.createChild({cls:cls+'-ft'});
16032 this.pageTb = new Roo.Toolbar(this.footer);
16036 this.footer = this.list.createChild({cls:cls+'-ft'});
16037 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16038 {pageSize: this.pageSize});
16042 if (this.pageTb && this.allowBlank && !this.disableClear) {
16044 this.pageTb.add(new Roo.Toolbar.Fill(), {
16045 cls: 'x-btn-icon x-btn-clear',
16047 handler: function()
16050 _this.clearValue();
16051 _this.onSelect(false, -1);
16056 this.assetHeight += this.footer.getHeight();
16061 this.tpl = Roo.bootstrap.version == 4 ?
16062 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16063 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16066 this.view = new Roo.View(this.list, this.tpl, {
16067 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16069 //this.view.wrapEl.setDisplayed(false);
16070 this.view.on('click', this.onViewClick, this);
16073 this.store.on('beforeload', this.onBeforeLoad, this);
16074 this.store.on('load', this.onLoad, this);
16075 this.store.on('loadexception', this.onLoadException, this);
16077 if(this.resizable){
16078 this.resizer = new Roo.Resizable(this.list, {
16079 pinned:true, handles:'se'
16081 this.resizer.on('resize', function(r, w, h){
16082 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16083 this.listWidth = w;
16084 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16085 this.restrictHeight();
16087 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16090 if(!this.editable){
16091 this.editable = true;
16092 this.setEditable(false);
16097 if (typeof(this.events.add.listeners) != 'undefined') {
16099 this.addicon = this.wrap.createChild(
16100 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16102 this.addicon.on('click', function(e) {
16103 this.fireEvent('add', this);
16106 if (typeof(this.events.edit.listeners) != 'undefined') {
16108 this.editicon = this.wrap.createChild(
16109 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16110 if (this.addicon) {
16111 this.editicon.setStyle('margin-left', '40px');
16113 this.editicon.on('click', function(e) {
16115 // we fire even if inothing is selected..
16116 this.fireEvent('edit', this, this.lastData );
16122 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16123 "up" : function(e){
16124 this.inKeyMode = true;
16128 "down" : function(e){
16129 if(!this.isExpanded()){
16130 this.onTriggerClick();
16132 this.inKeyMode = true;
16137 "enter" : function(e){
16138 // this.onViewClick();
16142 if(this.fireEvent("specialkey", this, e)){
16143 this.onViewClick(false);
16149 "esc" : function(e){
16153 "tab" : function(e){
16156 if(this.fireEvent("specialkey", this, e)){
16157 this.onViewClick(false);
16165 doRelay : function(foo, bar, hname){
16166 if(hname == 'down' || this.scope.isExpanded()){
16167 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16176 this.queryDelay = Math.max(this.queryDelay || 10,
16177 this.mode == 'local' ? 10 : 250);
16180 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16182 if(this.typeAhead){
16183 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16185 if(this.editable !== false){
16186 this.inputEl().on("keyup", this.onKeyUp, this);
16188 if(this.forceSelection){
16189 this.inputEl().on('blur', this.doForce, this);
16193 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16194 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16198 initTickableEvents: function()
16202 if(this.hiddenName){
16204 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16206 this.hiddenField.dom.value =
16207 this.hiddenValue !== undefined ? this.hiddenValue :
16208 this.value !== undefined ? this.value : '';
16210 // prevent input submission
16211 this.el.dom.removeAttribute('name');
16212 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16217 // this.list = this.el.select('ul.dropdown-menu',true).first();
16219 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16220 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16221 if(this.triggerList){
16222 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16225 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16226 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16228 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16229 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16231 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16232 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16234 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16235 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16236 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16239 this.cancelBtn.hide();
16244 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16245 _this.list.setWidth(lw);
16248 this.list.on('mouseover', this.onViewOver, this);
16249 this.list.on('mousemove', this.onViewMove, this);
16251 this.list.on('scroll', this.onViewScroll, this);
16254 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16255 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16258 this.view = new Roo.View(this.list, this.tpl, {
16263 selectedClass: this.selectedClass
16266 //this.view.wrapEl.setDisplayed(false);
16267 this.view.on('click', this.onViewClick, this);
16271 this.store.on('beforeload', this.onBeforeLoad, this);
16272 this.store.on('load', this.onLoad, this);
16273 this.store.on('loadexception', this.onLoadException, this);
16276 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16277 "up" : function(e){
16278 this.inKeyMode = true;
16282 "down" : function(e){
16283 this.inKeyMode = true;
16287 "enter" : function(e){
16288 if(this.fireEvent("specialkey", this, e)){
16289 this.onViewClick(false);
16295 "esc" : function(e){
16296 this.onTickableFooterButtonClick(e, false, false);
16299 "tab" : function(e){
16300 this.fireEvent("specialkey", this, e);
16302 this.onTickableFooterButtonClick(e, false, false);
16309 doRelay : function(e, fn, key){
16310 if(this.scope.isExpanded()){
16311 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16320 this.queryDelay = Math.max(this.queryDelay || 10,
16321 this.mode == 'local' ? 10 : 250);
16324 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16326 if(this.typeAhead){
16327 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16330 if(this.editable !== false){
16331 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16334 this.indicator = this.indicatorEl();
16336 if(this.indicator){
16337 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16338 this.indicator.hide();
16343 onDestroy : function(){
16345 this.view.setStore(null);
16346 this.view.el.removeAllListeners();
16347 this.view.el.remove();
16348 this.view.purgeListeners();
16351 this.list.dom.innerHTML = '';
16355 this.store.un('beforeload', this.onBeforeLoad, this);
16356 this.store.un('load', this.onLoad, this);
16357 this.store.un('loadexception', this.onLoadException, this);
16359 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16363 fireKey : function(e){
16364 if(e.isNavKeyPress() && !this.list.isVisible()){
16365 this.fireEvent("specialkey", this, e);
16370 onResize: function(w, h)
16374 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16376 // if(typeof w != 'number'){
16377 // // we do not handle it!?!?
16380 // var tw = this.trigger.getWidth();
16381 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16382 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16384 // this.inputEl().setWidth( this.adjustWidth('input', x));
16386 // //this.trigger.setStyle('left', x+'px');
16388 // if(this.list && this.listWidth === undefined){
16389 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16390 // this.list.setWidth(lw);
16391 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16399 * Allow or prevent the user from directly editing the field text. If false is passed,
16400 * the user will only be able to select from the items defined in the dropdown list. This method
16401 * is the runtime equivalent of setting the 'editable' config option at config time.
16402 * @param {Boolean} value True to allow the user to directly edit the field text
16404 setEditable : function(value){
16405 if(value == this.editable){
16408 this.editable = value;
16410 this.inputEl().dom.setAttribute('readOnly', true);
16411 this.inputEl().on('mousedown', this.onTriggerClick, this);
16412 this.inputEl().addClass('x-combo-noedit');
16414 this.inputEl().dom.removeAttribute('readOnly');
16415 this.inputEl().un('mousedown', this.onTriggerClick, this);
16416 this.inputEl().removeClass('x-combo-noedit');
16422 onBeforeLoad : function(combo,opts){
16423 if(!this.hasFocus){
16427 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16429 this.restrictHeight();
16430 this.selectedIndex = -1;
16434 onLoad : function(){
16436 this.hasQuery = false;
16438 if(!this.hasFocus){
16442 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16443 this.loading.hide();
16446 if(this.store.getCount() > 0){
16449 this.restrictHeight();
16450 if(this.lastQuery == this.allQuery){
16451 if(this.editable && !this.tickable){
16452 this.inputEl().dom.select();
16456 !this.selectByValue(this.value, true) &&
16459 !this.store.lastOptions ||
16460 typeof(this.store.lastOptions.add) == 'undefined' ||
16461 this.store.lastOptions.add != true
16464 this.select(0, true);
16467 if(this.autoFocus){
16470 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16471 this.taTask.delay(this.typeAheadDelay);
16475 this.onEmptyResults();
16481 onLoadException : function()
16483 this.hasQuery = false;
16485 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16486 this.loading.hide();
16489 if(this.tickable && this.editable){
16494 // only causes errors at present
16495 //Roo.log(this.store.reader.jsonData);
16496 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16498 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16504 onTypeAhead : function(){
16505 if(this.store.getCount() > 0){
16506 var r = this.store.getAt(0);
16507 var newValue = r.data[this.displayField];
16508 var len = newValue.length;
16509 var selStart = this.getRawValue().length;
16511 if(selStart != len){
16512 this.setRawValue(newValue);
16513 this.selectText(selStart, newValue.length);
16519 onSelect : function(record, index){
16521 if(this.fireEvent('beforeselect', this, record, index) !== false){
16523 this.setFromData(index > -1 ? record.data : false);
16526 this.fireEvent('select', this, record, index);
16531 * Returns the currently selected field value or empty string if no value is set.
16532 * @return {String} value The selected value
16534 getValue : function()
16536 if(Roo.isIOS && this.useNativeIOS){
16537 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16541 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16544 if(this.valueField){
16545 return typeof this.value != 'undefined' ? this.value : '';
16547 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16551 getRawValue : function()
16553 if(Roo.isIOS && this.useNativeIOS){
16554 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16557 var v = this.inputEl().getValue();
16563 * Clears any text/value currently set in the field
16565 clearValue : function(){
16567 if(this.hiddenField){
16568 this.hiddenField.dom.value = '';
16571 this.setRawValue('');
16572 this.lastSelectionText = '';
16573 this.lastData = false;
16575 var close = this.closeTriggerEl();
16586 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16587 * will be displayed in the field. If the value does not match the data value of an existing item,
16588 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16589 * Otherwise the field will be blank (although the value will still be set).
16590 * @param {String} value The value to match
16592 setValue : function(v)
16594 if(Roo.isIOS && this.useNativeIOS){
16595 this.setIOSValue(v);
16605 if(this.valueField){
16606 var r = this.findRecord(this.valueField, v);
16608 text = r.data[this.displayField];
16609 }else if(this.valueNotFoundText !== undefined){
16610 text = this.valueNotFoundText;
16613 this.lastSelectionText = text;
16614 if(this.hiddenField){
16615 this.hiddenField.dom.value = v;
16617 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16620 var close = this.closeTriggerEl();
16623 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16629 * @property {Object} the last set data for the element
16634 * Sets the value of the field based on a object which is related to the record format for the store.
16635 * @param {Object} value the value to set as. or false on reset?
16637 setFromData : function(o){
16644 var dv = ''; // display value
16645 var vv = ''; // value value..
16647 if (this.displayField) {
16648 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16650 // this is an error condition!!!
16651 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16654 if(this.valueField){
16655 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16658 var close = this.closeTriggerEl();
16661 if(dv.length || vv * 1 > 0){
16663 this.blockFocus=true;
16669 if(this.hiddenField){
16670 this.hiddenField.dom.value = vv;
16672 this.lastSelectionText = dv;
16673 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16677 // no hidden field.. - we store the value in 'value', but still display
16678 // display field!!!!
16679 this.lastSelectionText = dv;
16680 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16687 reset : function(){
16688 // overridden so that last data is reset..
16695 this.setValue(this.originalValue);
16696 //this.clearInvalid();
16697 this.lastData = false;
16699 this.view.clearSelections();
16705 findRecord : function(prop, value){
16707 if(this.store.getCount() > 0){
16708 this.store.each(function(r){
16709 if(r.data[prop] == value){
16719 getName: function()
16721 // returns hidden if it's set..
16722 if (!this.rendered) {return ''};
16723 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16727 onViewMove : function(e, t){
16728 this.inKeyMode = false;
16732 onViewOver : function(e, t){
16733 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16736 var item = this.view.findItemFromChild(t);
16739 var index = this.view.indexOf(item);
16740 this.select(index, false);
16745 onViewClick : function(view, doFocus, el, e)
16747 var index = this.view.getSelectedIndexes()[0];
16749 var r = this.store.getAt(index);
16753 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16760 Roo.each(this.tickItems, function(v,k){
16762 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16764 _this.tickItems.splice(k, 1);
16766 if(typeof(e) == 'undefined' && view == false){
16767 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16779 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16780 this.tickItems.push(r.data);
16783 if(typeof(e) == 'undefined' && view == false){
16784 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16791 this.onSelect(r, index);
16793 if(doFocus !== false && !this.blockFocus){
16794 this.inputEl().focus();
16799 restrictHeight : function(){
16800 //this.innerList.dom.style.height = '';
16801 //var inner = this.innerList.dom;
16802 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16803 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16804 //this.list.beginUpdate();
16805 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16806 this.list.alignTo(this.inputEl(), this.listAlign);
16807 this.list.alignTo(this.inputEl(), this.listAlign);
16808 //this.list.endUpdate();
16812 onEmptyResults : function(){
16814 if(this.tickable && this.editable){
16815 this.hasFocus = false;
16816 this.restrictHeight();
16824 * Returns true if the dropdown list is expanded, else false.
16826 isExpanded : function(){
16827 return this.list.isVisible();
16831 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16832 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16833 * @param {String} value The data value of the item to select
16834 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16835 * selected item if it is not currently in view (defaults to true)
16836 * @return {Boolean} True if the value matched an item in the list, else false
16838 selectByValue : function(v, scrollIntoView){
16839 if(v !== undefined && v !== null){
16840 var r = this.findRecord(this.valueField || this.displayField, v);
16842 this.select(this.store.indexOf(r), scrollIntoView);
16850 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16851 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16852 * @param {Number} index The zero-based index of the list item to select
16853 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16854 * selected item if it is not currently in view (defaults to true)
16856 select : function(index, scrollIntoView){
16857 this.selectedIndex = index;
16858 this.view.select(index);
16859 if(scrollIntoView !== false){
16860 var el = this.view.getNode(index);
16862 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16865 this.list.scrollChildIntoView(el, false);
16871 selectNext : function(){
16872 var ct = this.store.getCount();
16874 if(this.selectedIndex == -1){
16876 }else if(this.selectedIndex < ct-1){
16877 this.select(this.selectedIndex+1);
16883 selectPrev : function(){
16884 var ct = this.store.getCount();
16886 if(this.selectedIndex == -1){
16888 }else if(this.selectedIndex != 0){
16889 this.select(this.selectedIndex-1);
16895 onKeyUp : function(e){
16896 if(this.editable !== false && !e.isSpecialKey()){
16897 this.lastKey = e.getKey();
16898 this.dqTask.delay(this.queryDelay);
16903 validateBlur : function(){
16904 return !this.list || !this.list.isVisible();
16908 initQuery : function(){
16910 var v = this.getRawValue();
16912 if(this.tickable && this.editable){
16913 v = this.tickableInputEl().getValue();
16920 doForce : function(){
16921 if(this.inputEl().dom.value.length > 0){
16922 this.inputEl().dom.value =
16923 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16929 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16930 * query allowing the query action to be canceled if needed.
16931 * @param {String} query The SQL query to execute
16932 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16933 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16934 * saved in the current store (defaults to false)
16936 doQuery : function(q, forceAll){
16938 if(q === undefined || q === null){
16943 forceAll: forceAll,
16947 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16952 forceAll = qe.forceAll;
16953 if(forceAll === true || (q.length >= this.minChars)){
16955 this.hasQuery = true;
16957 if(this.lastQuery != q || this.alwaysQuery){
16958 this.lastQuery = q;
16959 if(this.mode == 'local'){
16960 this.selectedIndex = -1;
16962 this.store.clearFilter();
16965 if(this.specialFilter){
16966 this.fireEvent('specialfilter', this);
16971 this.store.filter(this.displayField, q);
16974 this.store.fireEvent("datachanged", this.store);
16981 this.store.baseParams[this.queryParam] = q;
16983 var options = {params : this.getParams(q)};
16986 options.add = true;
16987 options.params.start = this.page * this.pageSize;
16990 this.store.load(options);
16993 * this code will make the page width larger, at the beginning, the list not align correctly,
16994 * we should expand the list on onLoad
16995 * so command out it
17000 this.selectedIndex = -1;
17005 this.loadNext = false;
17009 getParams : function(q){
17011 //p[this.queryParam] = q;
17015 p.limit = this.pageSize;
17021 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17023 collapse : function(){
17024 if(!this.isExpanded()){
17030 this.hasFocus = false;
17034 this.cancelBtn.hide();
17035 this.trigger.show();
17038 this.tickableInputEl().dom.value = '';
17039 this.tickableInputEl().blur();
17044 Roo.get(document).un('mousedown', this.collapseIf, this);
17045 Roo.get(document).un('mousewheel', this.collapseIf, this);
17046 if (!this.editable) {
17047 Roo.get(document).un('keydown', this.listKeyPress, this);
17049 this.fireEvent('collapse', this);
17055 collapseIf : function(e){
17056 var in_combo = e.within(this.el);
17057 var in_list = e.within(this.list);
17058 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17060 if (in_combo || in_list || is_list) {
17061 //e.stopPropagation();
17066 this.onTickableFooterButtonClick(e, false, false);
17074 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17076 expand : function(){
17078 if(this.isExpanded() || !this.hasFocus){
17082 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17083 this.list.setWidth(lw);
17089 this.restrictHeight();
17093 this.tickItems = Roo.apply([], this.item);
17096 this.cancelBtn.show();
17097 this.trigger.hide();
17100 this.tickableInputEl().focus();
17105 Roo.get(document).on('mousedown', this.collapseIf, this);
17106 Roo.get(document).on('mousewheel', this.collapseIf, this);
17107 if (!this.editable) {
17108 Roo.get(document).on('keydown', this.listKeyPress, this);
17111 this.fireEvent('expand', this);
17115 // Implements the default empty TriggerField.onTriggerClick function
17116 onTriggerClick : function(e)
17118 Roo.log('trigger click');
17120 if(this.disabled || !this.triggerList){
17125 this.loadNext = false;
17127 if(this.isExpanded()){
17129 if (!this.blockFocus) {
17130 this.inputEl().focus();
17134 this.hasFocus = true;
17135 if(this.triggerAction == 'all') {
17136 this.doQuery(this.allQuery, true);
17138 this.doQuery(this.getRawValue());
17140 if (!this.blockFocus) {
17141 this.inputEl().focus();
17146 onTickableTriggerClick : function(e)
17153 this.loadNext = false;
17154 this.hasFocus = true;
17156 if(this.triggerAction == 'all') {
17157 this.doQuery(this.allQuery, true);
17159 this.doQuery(this.getRawValue());
17163 onSearchFieldClick : function(e)
17165 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17166 this.onTickableFooterButtonClick(e, false, false);
17170 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17175 this.loadNext = false;
17176 this.hasFocus = true;
17178 if(this.triggerAction == 'all') {
17179 this.doQuery(this.allQuery, true);
17181 this.doQuery(this.getRawValue());
17185 listKeyPress : function(e)
17187 //Roo.log('listkeypress');
17188 // scroll to first matching element based on key pres..
17189 if (e.isSpecialKey()) {
17192 var k = String.fromCharCode(e.getKey()).toUpperCase();
17195 var csel = this.view.getSelectedNodes();
17196 var cselitem = false;
17198 var ix = this.view.indexOf(csel[0]);
17199 cselitem = this.store.getAt(ix);
17200 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17206 this.store.each(function(v) {
17208 // start at existing selection.
17209 if (cselitem.id == v.id) {
17215 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17216 match = this.store.indexOf(v);
17222 if (match === false) {
17223 return true; // no more action?
17226 this.view.select(match);
17227 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17228 sn.scrollIntoView(sn.dom.parentNode, false);
17231 onViewScroll : function(e, t){
17233 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){
17237 this.hasQuery = true;
17239 this.loading = this.list.select('.loading', true).first();
17241 if(this.loading === null){
17242 this.list.createChild({
17244 cls: 'loading roo-select2-more-results roo-select2-active',
17245 html: 'Loading more results...'
17248 this.loading = this.list.select('.loading', true).first();
17250 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17252 this.loading.hide();
17255 this.loading.show();
17260 this.loadNext = true;
17262 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17267 addItem : function(o)
17269 var dv = ''; // display value
17271 if (this.displayField) {
17272 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17274 // this is an error condition!!!
17275 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17282 var choice = this.choices.createChild({
17284 cls: 'roo-select2-search-choice',
17293 cls: 'roo-select2-search-choice-close fa fa-times',
17298 }, this.searchField);
17300 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17302 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17310 this.inputEl().dom.value = '';
17315 onRemoveItem : function(e, _self, o)
17317 e.preventDefault();
17319 this.lastItem = Roo.apply([], this.item);
17321 var index = this.item.indexOf(o.data) * 1;
17324 Roo.log('not this item?!');
17328 this.item.splice(index, 1);
17333 this.fireEvent('remove', this, e);
17339 syncValue : function()
17341 if(!this.item.length){
17348 Roo.each(this.item, function(i){
17349 if(_this.valueField){
17350 value.push(i[_this.valueField]);
17357 this.value = value.join(',');
17359 if(this.hiddenField){
17360 this.hiddenField.dom.value = this.value;
17363 this.store.fireEvent("datachanged", this.store);
17368 clearItem : function()
17370 if(!this.multiple){
17376 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17384 if(this.tickable && !Roo.isTouch){
17385 this.view.refresh();
17389 inputEl: function ()
17391 if(Roo.isIOS && this.useNativeIOS){
17392 return this.el.select('select.roo-ios-select', true).first();
17395 if(Roo.isTouch && this.mobileTouchView){
17396 return this.el.select('input.form-control',true).first();
17400 return this.searchField;
17403 return this.el.select('input.form-control',true).first();
17406 onTickableFooterButtonClick : function(e, btn, el)
17408 e.preventDefault();
17410 this.lastItem = Roo.apply([], this.item);
17412 if(btn && btn.name == 'cancel'){
17413 this.tickItems = Roo.apply([], this.item);
17422 Roo.each(this.tickItems, function(o){
17430 validate : function()
17432 if(this.getVisibilityEl().hasClass('hidden')){
17436 var v = this.getRawValue();
17439 v = this.getValue();
17442 if(this.disabled || this.allowBlank || v.length){
17447 this.markInvalid();
17451 tickableInputEl : function()
17453 if(!this.tickable || !this.editable){
17454 return this.inputEl();
17457 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17461 getAutoCreateTouchView : function()
17466 cls: 'form-group' //input-group
17472 type : this.inputType,
17473 cls : 'form-control x-combo-noedit',
17474 autocomplete: 'new-password',
17475 placeholder : this.placeholder || '',
17480 input.name = this.name;
17484 input.cls += ' input-' + this.size;
17487 if (this.disabled) {
17488 input.disabled = true;
17492 cls : 'roo-combobox-wrap',
17499 inputblock.cls += ' input-group';
17501 inputblock.cn.unshift({
17503 cls : 'input-group-addon input-group-prepend input-group-text',
17508 if(this.removable && !this.multiple){
17509 inputblock.cls += ' roo-removable';
17511 inputblock.cn.push({
17514 cls : 'roo-combo-removable-btn close'
17518 if(this.hasFeedback && !this.allowBlank){
17520 inputblock.cls += ' has-feedback';
17522 inputblock.cn.push({
17524 cls: 'glyphicon form-control-feedback'
17531 inputblock.cls += (this.before) ? '' : ' input-group';
17533 inputblock.cn.push({
17535 cls : 'input-group-addon input-group-append input-group-text',
17541 var ibwrap = inputblock;
17546 cls: 'roo-select2-choices',
17550 cls: 'roo-select2-search-field',
17563 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17568 cls: 'form-hidden-field'
17574 if(!this.multiple && this.showToggleBtn){
17580 if (this.caret != false) {
17583 cls: 'fa fa-' + this.caret
17590 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17592 Roo.bootstrap.version == 3 ? caret : '',
17595 cls: 'combobox-clear',
17609 combobox.cls += ' roo-select2-container-multi';
17612 var required = this.allowBlank ? {
17614 style: 'display: none'
17617 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17618 tooltip : 'This field is required'
17621 var align = this.labelAlign || this.parentLabelAlign();
17623 if (align ==='left' && this.fieldLabel.length) {
17629 cls : 'control-label col-form-label',
17630 html : this.fieldLabel
17634 cls : 'roo-combobox-wrap ',
17641 var labelCfg = cfg.cn[1];
17642 var contentCfg = cfg.cn[2];
17645 if(this.indicatorpos == 'right'){
17650 cls : 'control-label col-form-label',
17654 html : this.fieldLabel
17660 cls : "roo-combobox-wrap ",
17668 labelCfg = cfg.cn[0];
17669 contentCfg = cfg.cn[1];
17674 if(this.labelWidth > 12){
17675 labelCfg.style = "width: " + this.labelWidth + 'px';
17678 if(this.labelWidth < 13 && this.labelmd == 0){
17679 this.labelmd = this.labelWidth;
17682 if(this.labellg > 0){
17683 labelCfg.cls += ' col-lg-' + this.labellg;
17684 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17687 if(this.labelmd > 0){
17688 labelCfg.cls += ' col-md-' + this.labelmd;
17689 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17692 if(this.labelsm > 0){
17693 labelCfg.cls += ' col-sm-' + this.labelsm;
17694 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17697 if(this.labelxs > 0){
17698 labelCfg.cls += ' col-xs-' + this.labelxs;
17699 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17703 } else if ( this.fieldLabel.length) {
17708 cls : 'control-label',
17709 html : this.fieldLabel
17720 if(this.indicatorpos == 'right'){
17724 cls : 'control-label',
17725 html : this.fieldLabel,
17743 var settings = this;
17745 ['xs','sm','md','lg'].map(function(size){
17746 if (settings[size]) {
17747 cfg.cls += ' col-' + size + '-' + settings[size];
17754 initTouchView : function()
17756 this.renderTouchView();
17758 this.touchViewEl.on('scroll', function(){
17759 this.el.dom.scrollTop = 0;
17762 this.originalValue = this.getValue();
17764 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17766 this.inputEl().on("click", this.showTouchView, this);
17767 if (this.triggerEl) {
17768 this.triggerEl.on("click", this.showTouchView, this);
17772 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17773 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17775 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17777 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17778 this.store.on('load', this.onTouchViewLoad, this);
17779 this.store.on('loadexception', this.onTouchViewLoadException, this);
17781 if(this.hiddenName){
17783 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17785 this.hiddenField.dom.value =
17786 this.hiddenValue !== undefined ? this.hiddenValue :
17787 this.value !== undefined ? this.value : '';
17789 this.el.dom.removeAttribute('name');
17790 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17794 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17795 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17798 if(this.removable && !this.multiple){
17799 var close = this.closeTriggerEl();
17801 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17802 close.on('click', this.removeBtnClick, this, close);
17806 * fix the bug in Safari iOS8
17808 this.inputEl().on("focus", function(e){
17809 document.activeElement.blur();
17812 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17819 renderTouchView : function()
17821 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17822 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17824 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17825 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17827 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17828 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829 this.touchViewBodyEl.setStyle('overflow', 'auto');
17831 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17832 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17834 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17835 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17839 showTouchView : function()
17845 this.touchViewHeaderEl.hide();
17847 if(this.modalTitle.length){
17848 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17849 this.touchViewHeaderEl.show();
17852 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17853 this.touchViewEl.show();
17855 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17857 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17858 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17860 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17862 if(this.modalTitle.length){
17863 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17866 this.touchViewBodyEl.setHeight(bodyHeight);
17870 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17872 this.touchViewEl.addClass(['in','show']);
17875 if(this._touchViewMask){
17876 Roo.get(document.body).addClass("x-body-masked");
17877 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17878 this._touchViewMask.setStyle('z-index', 10000);
17879 this._touchViewMask.addClass('show');
17882 this.doTouchViewQuery();
17886 hideTouchView : function()
17888 this.touchViewEl.removeClass(['in','show']);
17892 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17894 this.touchViewEl.setStyle('display', 'none');
17897 if(this._touchViewMask){
17898 this._touchViewMask.removeClass('show');
17899 Roo.get(document.body).removeClass("x-body-masked");
17903 setTouchViewValue : function()
17910 Roo.each(this.tickItems, function(o){
17915 this.hideTouchView();
17918 doTouchViewQuery : function()
17927 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17931 if(!this.alwaysQuery || this.mode == 'local'){
17932 this.onTouchViewLoad();
17939 onTouchViewBeforeLoad : function(combo,opts)
17945 onTouchViewLoad : function()
17947 if(this.store.getCount() < 1){
17948 this.onTouchViewEmptyResults();
17952 this.clearTouchView();
17954 var rawValue = this.getRawValue();
17956 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17958 this.tickItems = [];
17960 this.store.data.each(function(d, rowIndex){
17961 var row = this.touchViewListGroup.createChild(template);
17963 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17964 row.addClass(d.data.cls);
17967 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17970 html : d.data[this.displayField]
17973 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17974 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17977 row.removeClass('selected');
17978 if(!this.multiple && this.valueField &&
17979 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17982 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983 row.addClass('selected');
17986 if(this.multiple && this.valueField &&
17987 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17991 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17992 this.tickItems.push(d.data);
17995 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17999 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18001 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18003 if(this.modalTitle.length){
18004 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18007 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18009 if(this.mobile_restrict_height && listHeight < bodyHeight){
18010 this.touchViewBodyEl.setHeight(listHeight);
18015 if(firstChecked && listHeight > bodyHeight){
18016 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18021 onTouchViewLoadException : function()
18023 this.hideTouchView();
18026 onTouchViewEmptyResults : function()
18028 this.clearTouchView();
18030 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18032 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18036 clearTouchView : function()
18038 this.touchViewListGroup.dom.innerHTML = '';
18041 onTouchViewClick : function(e, el, o)
18043 e.preventDefault();
18046 var rowIndex = o.rowIndex;
18048 var r = this.store.getAt(rowIndex);
18050 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18052 if(!this.multiple){
18053 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18054 c.dom.removeAttribute('checked');
18057 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18059 this.setFromData(r.data);
18061 var close = this.closeTriggerEl();
18067 this.hideTouchView();
18069 this.fireEvent('select', this, r, rowIndex);
18074 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18075 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18076 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18080 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18081 this.addItem(r.data);
18082 this.tickItems.push(r.data);
18086 getAutoCreateNativeIOS : function()
18089 cls: 'form-group' //input-group,
18094 cls : 'roo-ios-select'
18098 combobox.name = this.name;
18101 if (this.disabled) {
18102 combobox.disabled = true;
18105 var settings = this;
18107 ['xs','sm','md','lg'].map(function(size){
18108 if (settings[size]) {
18109 cfg.cls += ' col-' + size + '-' + settings[size];
18119 initIOSView : function()
18121 this.store.on('load', this.onIOSViewLoad, this);
18126 onIOSViewLoad : function()
18128 if(this.store.getCount() < 1){
18132 this.clearIOSView();
18134 if(this.allowBlank) {
18136 var default_text = '-- SELECT --';
18138 if(this.placeholder.length){
18139 default_text = this.placeholder;
18142 if(this.emptyTitle.length){
18143 default_text += ' - ' + this.emptyTitle + ' -';
18146 var opt = this.inputEl().createChild({
18149 html : default_text
18153 o[this.valueField] = 0;
18154 o[this.displayField] = default_text;
18156 this.ios_options.push({
18163 this.store.data.each(function(d, rowIndex){
18167 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18168 html = d.data[this.displayField];
18173 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18174 value = d.data[this.valueField];
18183 if(this.value == d.data[this.valueField]){
18184 option['selected'] = true;
18187 var opt = this.inputEl().createChild(option);
18189 this.ios_options.push({
18196 this.inputEl().on('change', function(){
18197 this.fireEvent('select', this);
18202 clearIOSView: function()
18204 this.inputEl().dom.innerHTML = '';
18206 this.ios_options = [];
18209 setIOSValue: function(v)
18213 if(!this.ios_options){
18217 Roo.each(this.ios_options, function(opts){
18219 opts.el.dom.removeAttribute('selected');
18221 if(opts.data[this.valueField] != v){
18225 opts.el.dom.setAttribute('selected', true);
18231 * @cfg {Boolean} grow
18235 * @cfg {Number} growMin
18239 * @cfg {Number} growMax
18248 Roo.apply(Roo.bootstrap.ComboBox, {
18252 cls: 'modal-header',
18274 cls: 'list-group-item',
18278 cls: 'roo-combobox-list-group-item-value'
18282 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18296 listItemCheckbox : {
18298 cls: 'list-group-item',
18302 cls: 'roo-combobox-list-group-item-value'
18306 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18322 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18327 cls: 'modal-footer',
18335 cls: 'col-xs-6 text-left',
18338 cls: 'btn btn-danger roo-touch-view-cancel',
18344 cls: 'col-xs-6 text-right',
18347 cls: 'btn btn-success roo-touch-view-ok',
18358 Roo.apply(Roo.bootstrap.ComboBox, {
18360 touchViewTemplate : {
18362 cls: 'modal fade roo-combobox-touch-view',
18366 cls: 'modal-dialog',
18367 style : 'position:fixed', // we have to fix position....
18371 cls: 'modal-content',
18373 Roo.bootstrap.ComboBox.header,
18374 Roo.bootstrap.ComboBox.body,
18375 Roo.bootstrap.ComboBox.footer
18384 * Ext JS Library 1.1.1
18385 * Copyright(c) 2006-2007, Ext JS, LLC.
18387 * Originally Released Under LGPL - original licence link has changed is not relivant.
18390 * <script type="text/javascript">
18395 * @extends Roo.util.Observable
18396 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18397 * This class also supports single and multi selection modes. <br>
18398 * Create a data model bound view:
18400 var store = new Roo.data.Store(...);
18402 var view = new Roo.View({
18404 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18406 singleSelect: true,
18407 selectedClass: "ydataview-selected",
18411 // listen for node click?
18412 view.on("click", function(vw, index, node, e){
18413 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18417 dataModel.load("foobar.xml");
18419 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18421 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18422 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18424 * Note: old style constructor is still suported (container, template, config)
18427 * Create a new View
18428 * @param {Object} config The config object
18431 Roo.View = function(config, depreciated_tpl, depreciated_config){
18433 this.parent = false;
18435 if (typeof(depreciated_tpl) == 'undefined') {
18436 // new way.. - universal constructor.
18437 Roo.apply(this, config);
18438 this.el = Roo.get(this.el);
18441 this.el = Roo.get(config);
18442 this.tpl = depreciated_tpl;
18443 Roo.apply(this, depreciated_config);
18445 this.wrapEl = this.el.wrap().wrap();
18446 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18449 if(typeof(this.tpl) == "string"){
18450 this.tpl = new Roo.Template(this.tpl);
18452 // support xtype ctors..
18453 this.tpl = new Roo.factory(this.tpl, Roo);
18457 this.tpl.compile();
18462 * @event beforeclick
18463 * Fires before a click is processed. Returns false to cancel the default action.
18464 * @param {Roo.View} this
18465 * @param {Number} index The index of the target node
18466 * @param {HTMLElement} node The target node
18467 * @param {Roo.EventObject} e The raw event object
18469 "beforeclick" : true,
18472 * Fires when a template node is clicked.
18473 * @param {Roo.View} this
18474 * @param {Number} index The index of the target node
18475 * @param {HTMLElement} node The target node
18476 * @param {Roo.EventObject} e The raw event object
18481 * Fires when a template node is double clicked.
18482 * @param {Roo.View} this
18483 * @param {Number} index The index of the target node
18484 * @param {HTMLElement} node The target node
18485 * @param {Roo.EventObject} e The raw event object
18489 * @event contextmenu
18490 * Fires when a template node is right clicked.
18491 * @param {Roo.View} this
18492 * @param {Number} index The index of the target node
18493 * @param {HTMLElement} node The target node
18494 * @param {Roo.EventObject} e The raw event object
18496 "contextmenu" : true,
18498 * @event selectionchange
18499 * Fires when the selected nodes change.
18500 * @param {Roo.View} this
18501 * @param {Array} selections Array of the selected nodes
18503 "selectionchange" : true,
18506 * @event beforeselect
18507 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18508 * @param {Roo.View} this
18509 * @param {HTMLElement} node The node to be selected
18510 * @param {Array} selections Array of currently selected nodes
18512 "beforeselect" : true,
18514 * @event preparedata
18515 * Fires on every row to render, to allow you to change the data.
18516 * @param {Roo.View} this
18517 * @param {Object} data to be rendered (change this)
18519 "preparedata" : true
18527 "click": this.onClick,
18528 "dblclick": this.onDblClick,
18529 "contextmenu": this.onContextMenu,
18533 this.selections = [];
18535 this.cmp = new Roo.CompositeElementLite([]);
18537 this.store = Roo.factory(this.store, Roo.data);
18538 this.setStore(this.store, true);
18541 if ( this.footer && this.footer.xtype) {
18543 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18545 this.footer.dataSource = this.store;
18546 this.footer.container = fctr;
18547 this.footer = Roo.factory(this.footer, Roo);
18548 fctr.insertFirst(this.el);
18550 // this is a bit insane - as the paging toolbar seems to detach the el..
18551 // dom.parentNode.parentNode.parentNode
18552 // they get detached?
18556 Roo.View.superclass.constructor.call(this);
18561 Roo.extend(Roo.View, Roo.util.Observable, {
18564 * @cfg {Roo.data.Store} store Data store to load data from.
18569 * @cfg {String|Roo.Element} el The container element.
18574 * @cfg {String|Roo.Template} tpl The template used by this View
18578 * @cfg {String} dataName the named area of the template to use as the data area
18579 * Works with domtemplates roo-name="name"
18583 * @cfg {String} selectedClass The css class to add to selected nodes
18585 selectedClass : "x-view-selected",
18587 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18592 * @cfg {String} text to display on mask (default Loading)
18596 * @cfg {Boolean} multiSelect Allow multiple selection
18598 multiSelect : false,
18600 * @cfg {Boolean} singleSelect Allow single selection
18602 singleSelect: false,
18605 * @cfg {Boolean} toggleSelect - selecting
18607 toggleSelect : false,
18610 * @cfg {Boolean} tickable - selecting
18615 * Returns the element this view is bound to.
18616 * @return {Roo.Element}
18618 getEl : function(){
18619 return this.wrapEl;
18625 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18627 refresh : function(){
18628 //Roo.log('refresh');
18631 // if we are using something like 'domtemplate', then
18632 // the what gets used is:
18633 // t.applySubtemplate(NAME, data, wrapping data..)
18634 // the outer template then get' applied with
18635 // the store 'extra data'
18636 // and the body get's added to the
18637 // roo-name="data" node?
18638 // <span class='roo-tpl-{name}'></span> ?????
18642 this.clearSelections();
18643 this.el.update("");
18645 var records = this.store.getRange();
18646 if(records.length < 1) {
18648 // is this valid?? = should it render a template??
18650 this.el.update(this.emptyText);
18654 if (this.dataName) {
18655 this.el.update(t.apply(this.store.meta)); //????
18656 el = this.el.child('.roo-tpl-' + this.dataName);
18659 for(var i = 0, len = records.length; i < len; i++){
18660 var data = this.prepareData(records[i].data, i, records[i]);
18661 this.fireEvent("preparedata", this, data, i, records[i]);
18663 var d = Roo.apply({}, data);
18666 Roo.apply(d, {'roo-id' : Roo.id()});
18670 Roo.each(this.parent.item, function(item){
18671 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18674 Roo.apply(d, {'roo-data-checked' : 'checked'});
18678 html[html.length] = Roo.util.Format.trim(
18680 t.applySubtemplate(this.dataName, d, this.store.meta) :
18687 el.update(html.join(""));
18688 this.nodes = el.dom.childNodes;
18689 this.updateIndexes(0);
18694 * Function to override to reformat the data that is sent to
18695 * the template for each node.
18696 * DEPRICATED - use the preparedata event handler.
18697 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18698 * a JSON object for an UpdateManager bound view).
18700 prepareData : function(data, index, record)
18702 this.fireEvent("preparedata", this, data, index, record);
18706 onUpdate : function(ds, record){
18707 // Roo.log('on update');
18708 this.clearSelections();
18709 var index = this.store.indexOf(record);
18710 var n = this.nodes[index];
18711 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18712 n.parentNode.removeChild(n);
18713 this.updateIndexes(index, index);
18719 onAdd : function(ds, records, index)
18721 //Roo.log(['on Add', ds, records, index] );
18722 this.clearSelections();
18723 if(this.nodes.length == 0){
18727 var n = this.nodes[index];
18728 for(var i = 0, len = records.length; i < len; i++){
18729 var d = this.prepareData(records[i].data, i, records[i]);
18731 this.tpl.insertBefore(n, d);
18734 this.tpl.append(this.el, d);
18737 this.updateIndexes(index);
18740 onRemove : function(ds, record, index){
18741 // Roo.log('onRemove');
18742 this.clearSelections();
18743 var el = this.dataName ?
18744 this.el.child('.roo-tpl-' + this.dataName) :
18747 el.dom.removeChild(this.nodes[index]);
18748 this.updateIndexes(index);
18752 * Refresh an individual node.
18753 * @param {Number} index
18755 refreshNode : function(index){
18756 this.onUpdate(this.store, this.store.getAt(index));
18759 updateIndexes : function(startIndex, endIndex){
18760 var ns = this.nodes;
18761 startIndex = startIndex || 0;
18762 endIndex = endIndex || ns.length - 1;
18763 for(var i = startIndex; i <= endIndex; i++){
18764 ns[i].nodeIndex = i;
18769 * Changes the data store this view uses and refresh the view.
18770 * @param {Store} store
18772 setStore : function(store, initial){
18773 if(!initial && this.store){
18774 this.store.un("datachanged", this.refresh);
18775 this.store.un("add", this.onAdd);
18776 this.store.un("remove", this.onRemove);
18777 this.store.un("update", this.onUpdate);
18778 this.store.un("clear", this.refresh);
18779 this.store.un("beforeload", this.onBeforeLoad);
18780 this.store.un("load", this.onLoad);
18781 this.store.un("loadexception", this.onLoad);
18785 store.on("datachanged", this.refresh, this);
18786 store.on("add", this.onAdd, this);
18787 store.on("remove", this.onRemove, this);
18788 store.on("update", this.onUpdate, this);
18789 store.on("clear", this.refresh, this);
18790 store.on("beforeload", this.onBeforeLoad, this);
18791 store.on("load", this.onLoad, this);
18792 store.on("loadexception", this.onLoad, this);
18800 * onbeforeLoad - masks the loading area.
18803 onBeforeLoad : function(store,opts)
18805 //Roo.log('onBeforeLoad');
18807 this.el.update("");
18809 this.el.mask(this.mask ? this.mask : "Loading" );
18811 onLoad : function ()
18818 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18819 * @param {HTMLElement} node
18820 * @return {HTMLElement} The template node
18822 findItemFromChild : function(node){
18823 var el = this.dataName ?
18824 this.el.child('.roo-tpl-' + this.dataName,true) :
18827 if(!node || node.parentNode == el){
18830 var p = node.parentNode;
18831 while(p && p != el){
18832 if(p.parentNode == el){
18841 onClick : function(e){
18842 var item = this.findItemFromChild(e.getTarget());
18844 var index = this.indexOf(item);
18845 if(this.onItemClick(item, index, e) !== false){
18846 this.fireEvent("click", this, index, item, e);
18849 this.clearSelections();
18854 onContextMenu : function(e){
18855 var item = this.findItemFromChild(e.getTarget());
18857 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18862 onDblClick : function(e){
18863 var item = this.findItemFromChild(e.getTarget());
18865 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18869 onItemClick : function(item, index, e)
18871 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18874 if (this.toggleSelect) {
18875 var m = this.isSelected(item) ? 'unselect' : 'select';
18878 _t[m](item, true, false);
18881 if(this.multiSelect || this.singleSelect){
18882 if(this.multiSelect && e.shiftKey && this.lastSelection){
18883 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18885 this.select(item, this.multiSelect && e.ctrlKey);
18886 this.lastSelection = item;
18889 if(!this.tickable){
18890 e.preventDefault();
18898 * Get the number of selected nodes.
18901 getSelectionCount : function(){
18902 return this.selections.length;
18906 * Get the currently selected nodes.
18907 * @return {Array} An array of HTMLElements
18909 getSelectedNodes : function(){
18910 return this.selections;
18914 * Get the indexes of the selected nodes.
18917 getSelectedIndexes : function(){
18918 var indexes = [], s = this.selections;
18919 for(var i = 0, len = s.length; i < len; i++){
18920 indexes.push(s[i].nodeIndex);
18926 * Clear all selections
18927 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18929 clearSelections : function(suppressEvent){
18930 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18931 this.cmp.elements = this.selections;
18932 this.cmp.removeClass(this.selectedClass);
18933 this.selections = [];
18934 if(!suppressEvent){
18935 this.fireEvent("selectionchange", this, this.selections);
18941 * Returns true if the passed node is selected
18942 * @param {HTMLElement/Number} node The node or node index
18943 * @return {Boolean}
18945 isSelected : function(node){
18946 var s = this.selections;
18950 node = this.getNode(node);
18951 return s.indexOf(node) !== -1;
18956 * @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
18957 * @param {Boolean} keepExisting (optional) true to keep existing selections
18958 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18960 select : function(nodeInfo, keepExisting, suppressEvent){
18961 if(nodeInfo instanceof Array){
18963 this.clearSelections(true);
18965 for(var i = 0, len = nodeInfo.length; i < len; i++){
18966 this.select(nodeInfo[i], true, true);
18970 var node = this.getNode(nodeInfo);
18971 if(!node || this.isSelected(node)){
18972 return; // already selected.
18975 this.clearSelections(true);
18978 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18979 Roo.fly(node).addClass(this.selectedClass);
18980 this.selections.push(node);
18981 if(!suppressEvent){
18982 this.fireEvent("selectionchange", this, this.selections);
18990 * @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
18991 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18992 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18994 unselect : function(nodeInfo, keepExisting, suppressEvent)
18996 if(nodeInfo instanceof Array){
18997 Roo.each(this.selections, function(s) {
18998 this.unselect(s, nodeInfo);
19002 var node = this.getNode(nodeInfo);
19003 if(!node || !this.isSelected(node)){
19004 //Roo.log("not selected");
19005 return; // not selected.
19009 Roo.each(this.selections, function(s) {
19011 Roo.fly(node).removeClass(this.selectedClass);
19018 this.selections= ns;
19019 this.fireEvent("selectionchange", this, this.selections);
19023 * Gets a template node.
19024 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025 * @return {HTMLElement} The node or null if it wasn't found
19027 getNode : function(nodeInfo){
19028 if(typeof nodeInfo == "string"){
19029 return document.getElementById(nodeInfo);
19030 }else if(typeof nodeInfo == "number"){
19031 return this.nodes[nodeInfo];
19037 * Gets a range template nodes.
19038 * @param {Number} startIndex
19039 * @param {Number} endIndex
19040 * @return {Array} An array of nodes
19042 getNodes : function(start, end){
19043 var ns = this.nodes;
19044 start = start || 0;
19045 end = typeof end == "undefined" ? ns.length - 1 : end;
19048 for(var i = start; i <= end; i++){
19052 for(var i = start; i >= end; i--){
19060 * Finds the index of the passed node
19061 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19062 * @return {Number} The index of the node or -1
19064 indexOf : function(node){
19065 node = this.getNode(node);
19066 if(typeof node.nodeIndex == "number"){
19067 return node.nodeIndex;
19069 var ns = this.nodes;
19070 for(var i = 0, len = ns.length; i < len; i++){
19081 * based on jquery fullcalendar
19085 Roo.bootstrap = Roo.bootstrap || {};
19087 * @class Roo.bootstrap.Calendar
19088 * @extends Roo.bootstrap.Component
19089 * Bootstrap Calendar class
19090 * @cfg {Boolean} loadMask (true|false) default false
19091 * @cfg {Object} header generate the user specific header of the calendar, default false
19094 * Create a new Container
19095 * @param {Object} config The config object
19100 Roo.bootstrap.Calendar = function(config){
19101 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19105 * Fires when a date is selected
19106 * @param {DatePicker} this
19107 * @param {Date} date The selected date
19111 * @event monthchange
19112 * Fires when the displayed month changes
19113 * @param {DatePicker} this
19114 * @param {Date} date The selected month
19116 'monthchange': true,
19118 * @event evententer
19119 * Fires when mouse over an event
19120 * @param {Calendar} this
19121 * @param {event} Event
19123 'evententer': true,
19125 * @event eventleave
19126 * Fires when the mouse leaves an
19127 * @param {Calendar} this
19130 'eventleave': true,
19132 * @event eventclick
19133 * Fires when the mouse click an
19134 * @param {Calendar} this
19143 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19146 * @cfg {Number} startDay
19147 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19155 getAutoCreate : function(){
19158 var fc_button = function(name, corner, style, content ) {
19159 return Roo.apply({},{
19161 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19163 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19166 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19177 style : 'width:100%',
19184 cls : 'fc-header-left',
19186 fc_button('prev', 'left', 'arrow', '‹' ),
19187 fc_button('next', 'right', 'arrow', '›' ),
19188 { tag: 'span', cls: 'fc-header-space' },
19189 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19197 cls : 'fc-header-center',
19201 cls: 'fc-header-title',
19204 html : 'month / year'
19212 cls : 'fc-header-right',
19214 /* fc_button('month', 'left', '', 'month' ),
19215 fc_button('week', '', '', 'week' ),
19216 fc_button('day', 'right', '', 'day' )
19228 header = this.header;
19231 var cal_heads = function() {
19233 // fixme - handle this.
19235 for (var i =0; i < Date.dayNames.length; i++) {
19236 var d = Date.dayNames[i];
19239 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19240 html : d.substring(0,3)
19244 ret[0].cls += ' fc-first';
19245 ret[6].cls += ' fc-last';
19248 var cal_cell = function(n) {
19251 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19256 cls: 'fc-day-number',
19260 cls: 'fc-day-content',
19264 style: 'position: relative;' // height: 17px;
19276 var cal_rows = function() {
19279 for (var r = 0; r < 6; r++) {
19286 for (var i =0; i < Date.dayNames.length; i++) {
19287 var d = Date.dayNames[i];
19288 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19291 row.cn[0].cls+=' fc-first';
19292 row.cn[0].cn[0].style = 'min-height:90px';
19293 row.cn[6].cls+=' fc-last';
19297 ret[0].cls += ' fc-first';
19298 ret[4].cls += ' fc-prev-last';
19299 ret[5].cls += ' fc-last';
19306 cls: 'fc-border-separate',
19307 style : 'width:100%',
19315 cls : 'fc-first fc-last',
19333 cls : 'fc-content',
19334 style : "position: relative;",
19337 cls : 'fc-view fc-view-month fc-grid',
19338 style : 'position: relative',
19339 unselectable : 'on',
19342 cls : 'fc-event-container',
19343 style : 'position:absolute;z-index:8;top:0;left:0;'
19361 initEvents : function()
19364 throw "can not find store for calendar";
19370 style: "text-align:center",
19374 style: "background-color:white;width:50%;margin:250 auto",
19378 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19389 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19391 var size = this.el.select('.fc-content', true).first().getSize();
19392 this.maskEl.setSize(size.width, size.height);
19393 this.maskEl.enableDisplayMode("block");
19394 if(!this.loadMask){
19395 this.maskEl.hide();
19398 this.store = Roo.factory(this.store, Roo.data);
19399 this.store.on('load', this.onLoad, this);
19400 this.store.on('beforeload', this.onBeforeLoad, this);
19404 this.cells = this.el.select('.fc-day',true);
19405 //Roo.log(this.cells);
19406 this.textNodes = this.el.query('.fc-day-number');
19407 this.cells.addClassOnOver('fc-state-hover');
19409 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19410 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19411 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19412 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19414 this.on('monthchange', this.onMonthChange, this);
19416 this.update(new Date().clearTime());
19419 resize : function() {
19420 var sz = this.el.getSize();
19422 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19423 this.el.select('.fc-day-content div',true).setHeight(34);
19428 showPrevMonth : function(e){
19429 this.update(this.activeDate.add("mo", -1));
19431 showToday : function(e){
19432 this.update(new Date().clearTime());
19435 showNextMonth : function(e){
19436 this.update(this.activeDate.add("mo", 1));
19440 showPrevYear : function(){
19441 this.update(this.activeDate.add("y", -1));
19445 showNextYear : function(){
19446 this.update(this.activeDate.add("y", 1));
19451 update : function(date)
19453 var vd = this.activeDate;
19454 this.activeDate = date;
19455 // if(vd && this.el){
19456 // var t = date.getTime();
19457 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19458 // Roo.log('using add remove');
19460 // this.fireEvent('monthchange', this, date);
19462 // this.cells.removeClass("fc-state-highlight");
19463 // this.cells.each(function(c){
19464 // if(c.dateValue == t){
19465 // c.addClass("fc-state-highlight");
19466 // setTimeout(function(){
19467 // try{c.dom.firstChild.focus();}catch(e){}
19477 var days = date.getDaysInMonth();
19479 var firstOfMonth = date.getFirstDateOfMonth();
19480 var startingPos = firstOfMonth.getDay()-this.startDay;
19482 if(startingPos < this.startDay){
19486 var pm = date.add(Date.MONTH, -1);
19487 var prevStart = pm.getDaysInMonth()-startingPos;
19489 this.cells = this.el.select('.fc-day',true);
19490 this.textNodes = this.el.query('.fc-day-number');
19491 this.cells.addClassOnOver('fc-state-hover');
19493 var cells = this.cells.elements;
19494 var textEls = this.textNodes;
19496 Roo.each(cells, function(cell){
19497 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19500 days += startingPos;
19502 // convert everything to numbers so it's fast
19503 var day = 86400000;
19504 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19507 //Roo.log(prevStart);
19509 var today = new Date().clearTime().getTime();
19510 var sel = date.clearTime().getTime();
19511 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19512 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19513 var ddMatch = this.disabledDatesRE;
19514 var ddText = this.disabledDatesText;
19515 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19516 var ddaysText = this.disabledDaysText;
19517 var format = this.format;
19519 var setCellClass = function(cal, cell){
19523 //Roo.log('set Cell Class');
19525 var t = d.getTime();
19529 cell.dateValue = t;
19531 cell.className += " fc-today";
19532 cell.className += " fc-state-highlight";
19533 cell.title = cal.todayText;
19536 // disable highlight in other month..
19537 //cell.className += " fc-state-highlight";
19542 cell.className = " fc-state-disabled";
19543 cell.title = cal.minText;
19547 cell.className = " fc-state-disabled";
19548 cell.title = cal.maxText;
19552 if(ddays.indexOf(d.getDay()) != -1){
19553 cell.title = ddaysText;
19554 cell.className = " fc-state-disabled";
19557 if(ddMatch && format){
19558 var fvalue = d.dateFormat(format);
19559 if(ddMatch.test(fvalue)){
19560 cell.title = ddText.replace("%0", fvalue);
19561 cell.className = " fc-state-disabled";
19565 if (!cell.initialClassName) {
19566 cell.initialClassName = cell.dom.className;
19569 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19574 for(; i < startingPos; i++) {
19575 textEls[i].innerHTML = (++prevStart);
19576 d.setDate(d.getDate()+1);
19578 cells[i].className = "fc-past fc-other-month";
19579 setCellClass(this, cells[i]);
19584 for(; i < days; i++){
19585 intDay = i - startingPos + 1;
19586 textEls[i].innerHTML = (intDay);
19587 d.setDate(d.getDate()+1);
19589 cells[i].className = ''; // "x-date-active";
19590 setCellClass(this, cells[i]);
19594 for(; i < 42; i++) {
19595 textEls[i].innerHTML = (++extraDays);
19596 d.setDate(d.getDate()+1);
19598 cells[i].className = "fc-future fc-other-month";
19599 setCellClass(this, cells[i]);
19602 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19604 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19606 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19607 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19609 if(totalRows != 6){
19610 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19611 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19614 this.fireEvent('monthchange', this, date);
19618 if(!this.internalRender){
19619 var main = this.el.dom.firstChild;
19620 var w = main.offsetWidth;
19621 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19622 Roo.fly(main).setWidth(w);
19623 this.internalRender = true;
19624 // opera does not respect the auto grow header center column
19625 // then, after it gets a width opera refuses to recalculate
19626 // without a second pass
19627 if(Roo.isOpera && !this.secondPass){
19628 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19629 this.secondPass = true;
19630 this.update.defer(10, this, [date]);
19637 findCell : function(dt) {
19638 dt = dt.clearTime().getTime();
19640 this.cells.each(function(c){
19641 //Roo.log("check " +c.dateValue + '?=' + dt);
19642 if(c.dateValue == dt){
19652 findCells : function(ev) {
19653 var s = ev.start.clone().clearTime().getTime();
19655 var e= ev.end.clone().clearTime().getTime();
19658 this.cells.each(function(c){
19659 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19661 if(c.dateValue > e){
19664 if(c.dateValue < s){
19673 // findBestRow: function(cells)
19677 // for (var i =0 ; i < cells.length;i++) {
19678 // ret = Math.max(cells[i].rows || 0,ret);
19685 addItem : function(ev)
19687 // look for vertical location slot in
19688 var cells = this.findCells(ev);
19690 // ev.row = this.findBestRow(cells);
19692 // work out the location.
19696 for(var i =0; i < cells.length; i++) {
19698 cells[i].row = cells[0].row;
19701 cells[i].row = cells[i].row + 1;
19711 if (crow.start.getY() == cells[i].getY()) {
19713 crow.end = cells[i];
19730 cells[0].events.push(ev);
19732 this.calevents.push(ev);
19735 clearEvents: function() {
19737 if(!this.calevents){
19741 Roo.each(this.cells.elements, function(c){
19747 Roo.each(this.calevents, function(e) {
19748 Roo.each(e.els, function(el) {
19749 el.un('mouseenter' ,this.onEventEnter, this);
19750 el.un('mouseleave' ,this.onEventLeave, this);
19755 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19761 renderEvents: function()
19765 this.cells.each(function(c) {
19774 if(c.row != c.events.length){
19775 r = 4 - (4 - (c.row - c.events.length));
19778 c.events = ev.slice(0, r);
19779 c.more = ev.slice(r);
19781 if(c.more.length && c.more.length == 1){
19782 c.events.push(c.more.pop());
19785 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19789 this.cells.each(function(c) {
19791 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19794 for (var e = 0; e < c.events.length; e++){
19795 var ev = c.events[e];
19796 var rows = ev.rows;
19798 for(var i = 0; i < rows.length; i++) {
19800 // how many rows should it span..
19803 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19804 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19806 unselectable : "on",
19809 cls: 'fc-event-inner',
19813 // cls: 'fc-event-time',
19814 // html : cells.length > 1 ? '' : ev.time
19818 cls: 'fc-event-title',
19819 html : String.format('{0}', ev.title)
19826 cls: 'ui-resizable-handle ui-resizable-e',
19827 html : '  '
19834 cfg.cls += ' fc-event-start';
19836 if ((i+1) == rows.length) {
19837 cfg.cls += ' fc-event-end';
19840 var ctr = _this.el.select('.fc-event-container',true).first();
19841 var cg = ctr.createChild(cfg);
19843 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19844 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19846 var r = (c.more.length) ? 1 : 0;
19847 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19848 cg.setWidth(ebox.right - sbox.x -2);
19850 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19851 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19852 cg.on('click', _this.onEventClick, _this, ev);
19863 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19864 style : 'position: absolute',
19865 unselectable : "on",
19868 cls: 'fc-event-inner',
19872 cls: 'fc-event-title',
19880 cls: 'ui-resizable-handle ui-resizable-e',
19881 html : '  '
19887 var ctr = _this.el.select('.fc-event-container',true).first();
19888 var cg = ctr.createChild(cfg);
19890 var sbox = c.select('.fc-day-content',true).first().getBox();
19891 var ebox = c.select('.fc-day-content',true).first().getBox();
19893 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19894 cg.setWidth(ebox.right - sbox.x -2);
19896 cg.on('click', _this.onMoreEventClick, _this, c.more);
19906 onEventEnter: function (e, el,event,d) {
19907 this.fireEvent('evententer', this, el, event);
19910 onEventLeave: function (e, el,event,d) {
19911 this.fireEvent('eventleave', this, el, event);
19914 onEventClick: function (e, el,event,d) {
19915 this.fireEvent('eventclick', this, el, event);
19918 onMonthChange: function () {
19922 onMoreEventClick: function(e, el, more)
19926 this.calpopover.placement = 'right';
19927 this.calpopover.setTitle('More');
19929 this.calpopover.setContent('');
19931 var ctr = this.calpopover.el.select('.popover-content', true).first();
19933 Roo.each(more, function(m){
19935 cls : 'fc-event-hori fc-event-draggable',
19938 var cg = ctr.createChild(cfg);
19940 cg.on('click', _this.onEventClick, _this, m);
19943 this.calpopover.show(el);
19948 onLoad: function ()
19950 this.calevents = [];
19953 if(this.store.getCount() > 0){
19954 this.store.data.each(function(d){
19957 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19958 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19959 time : d.data.start_time,
19960 title : d.data.title,
19961 description : d.data.description,
19962 venue : d.data.venue
19967 this.renderEvents();
19969 if(this.calevents.length && this.loadMask){
19970 this.maskEl.hide();
19974 onBeforeLoad: function()
19976 this.clearEvents();
19978 this.maskEl.show();
19992 * @class Roo.bootstrap.Popover
19993 * @extends Roo.bootstrap.Component
19994 * Bootstrap Popover class
19995 * @cfg {String} html contents of the popover (or false to use children..)
19996 * @cfg {String} title of popover (or false to hide)
19997 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19998 * @cfg {String} trigger click || hover (or false to trigger manually)
19999 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20000 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20001 * - if false and it has a 'parent' then it will be automatically added to that element
20002 * - if string - Roo.get will be called
20003 * @cfg {Number} delay - delay before showing
20006 * Create a new Popover
20007 * @param {Object} config The config object
20010 Roo.bootstrap.Popover = function(config){
20011 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20017 * After the popover show
20019 * @param {Roo.bootstrap.Popover} this
20024 * After the popover hide
20026 * @param {Roo.bootstrap.Popover} this
20032 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20037 placement : 'right',
20038 trigger : 'hover', // hover
20044 can_build_overlaid : false,
20046 maskEl : false, // the mask element
20049 alignEl : false, // when show is called with an element - this get's stored.
20051 getChildContainer : function()
20053 return this.contentEl;
20056 getPopoverHeader : function()
20058 this.title = true; // flag not to hide it..
20059 this.headerEl.addClass('p-0');
20060 return this.headerEl
20064 getAutoCreate : function(){
20067 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20068 style: 'display:block',
20074 cls : 'popover-inner ',
20078 cls: 'popover-title popover-header',
20079 html : this.title === false ? '' : this.title
20082 cls : 'popover-content popover-body ' + (this.cls || ''),
20083 html : this.html || ''
20094 * @param {string} the title
20096 setTitle: function(str)
20100 this.headerEl.dom.innerHTML = str;
20105 * @param {string} the body content
20107 setContent: function(str)
20110 if (this.contentEl) {
20111 this.contentEl.dom.innerHTML = str;
20115 // as it get's added to the bottom of the page.
20116 onRender : function(ct, position)
20118 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20123 var cfg = Roo.apply({}, this.getAutoCreate());
20127 cfg.cls += ' ' + this.cls;
20130 cfg.style = this.style;
20132 //Roo.log("adding to ");
20133 this.el = Roo.get(document.body).createChild(cfg, position);
20134 // Roo.log(this.el);
20137 this.contentEl = this.el.select('.popover-content',true).first();
20138 this.headerEl = this.el.select('.popover-title',true).first();
20141 if(typeof(this.items) != 'undefined'){
20142 var items = this.items;
20145 for(var i =0;i < items.length;i++) {
20146 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20150 this.items = nitems;
20152 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20153 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20160 resizeMask : function()
20162 this.maskEl.setSize(
20163 Roo.lib.Dom.getViewWidth(true),
20164 Roo.lib.Dom.getViewHeight(true)
20168 initEvents : function()
20172 Roo.bootstrap.Popover.register(this);
20175 this.arrowEl = this.el.select('.arrow',true).first();
20176 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20177 this.el.enableDisplayMode('block');
20181 if (this.over === false && !this.parent()) {
20184 if (this.triggers === false) {
20189 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20190 var triggers = this.trigger ? this.trigger.split(' ') : [];
20191 Roo.each(triggers, function(trigger) {
20193 if (trigger == 'click') {
20194 on_el.on('click', this.toggle, this);
20195 } else if (trigger != 'manual') {
20196 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20197 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20199 on_el.on(eventIn ,this.enter, this);
20200 on_el.on(eventOut, this.leave, this);
20210 toggle : function () {
20211 this.hoverState == 'in' ? this.leave() : this.enter();
20214 enter : function () {
20216 clearTimeout(this.timeout);
20218 this.hoverState = 'in';
20220 if (!this.delay || !this.delay.show) {
20225 this.timeout = setTimeout(function () {
20226 if (_t.hoverState == 'in') {
20229 }, this.delay.show)
20232 leave : function() {
20233 clearTimeout(this.timeout);
20235 this.hoverState = 'out';
20237 if (!this.delay || !this.delay.hide) {
20242 this.timeout = setTimeout(function () {
20243 if (_t.hoverState == 'out') {
20246 }, this.delay.hide)
20250 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20251 * @param {string} (left|right|top|bottom) position
20253 show : function (on_el, placement)
20255 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20256 on_el = on_el || false; // default to false
20259 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20260 on_el = this.parent().el;
20261 } else if (this.over) {
20262 on_el = Roo.get(this.over);
20267 this.alignEl = Roo.get( on_el );
20270 this.render(document.body);
20276 if (this.title === false) {
20277 this.headerEl.hide();
20282 this.el.dom.style.display = 'block';
20285 if (this.alignEl) {
20286 this.updatePosition(this.placement, true);
20289 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20290 var es = this.el.getSize();
20291 var x = Roo.lib.Dom.getViewWidth()/2;
20292 var y = Roo.lib.Dom.getViewHeight()/2;
20293 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20298 //var arrow = this.el.select('.arrow',true).first();
20299 //arrow.set(align[2],
20301 this.el.addClass('in');
20305 this.hoverState = 'in';
20308 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20309 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20310 this.maskEl.dom.style.display = 'block';
20311 this.maskEl.addClass('show');
20313 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20315 this.fireEvent('show', this);
20319 * fire this manually after loading a grid in the table for example
20320 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20321 * @param {Boolean} try and move it if we cant get right position.
20323 updatePosition : function(placement, try_move)
20325 // allow for calling with no parameters
20326 placement = placement ? placement : this.placement;
20327 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20329 this.el.removeClass([
20330 'fade','top','bottom', 'left', 'right','in',
20331 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20333 this.el.addClass(placement + ' bs-popover-' + placement);
20335 if (!this.alignEl ) {
20339 switch (placement) {
20341 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20342 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20343 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344 //normal display... or moved up/down.
20345 this.el.setXY(offset);
20346 var xy = this.alignEl.getAnchorXY('tr', false);
20348 this.arrowEl.setXY(xy);
20351 // continue through...
20352 return this.updatePosition('left', false);
20356 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20357 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20358 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20359 //normal display... or moved up/down.
20360 this.el.setXY(offset);
20361 var xy = this.alignEl.getAnchorXY('tl', false);
20362 xy[0]-=10;xy[1]+=5; // << fix me
20363 this.arrowEl.setXY(xy);
20367 return this.updatePosition('right', false);
20370 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20371 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20372 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20373 //normal display... or moved up/down.
20374 this.el.setXY(offset);
20375 var xy = this.alignEl.getAnchorXY('t', false);
20376 xy[1]-=10; // << fix me
20377 this.arrowEl.setXY(xy);
20381 return this.updatePosition('bottom', false);
20384 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20385 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20386 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20387 //normal display... or moved up/down.
20388 this.el.setXY(offset);
20389 var xy = this.alignEl.getAnchorXY('b', false);
20390 xy[1]+=2; // << fix me
20391 this.arrowEl.setXY(xy);
20395 return this.updatePosition('top', false);
20406 this.el.setXY([0,0]);
20407 this.el.removeClass('in');
20409 this.hoverState = null;
20410 this.maskEl.hide(); // always..
20411 this.fireEvent('hide', this);
20417 Roo.apply(Roo.bootstrap.Popover, {
20420 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20421 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20422 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20423 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20428 clickHander : false,
20432 onMouseDown : function(e)
20434 if (this.popups.length && !e.getTarget(".roo-popover")) {
20435 /// what is nothing is showing..
20444 register : function(popup)
20446 if (!Roo.bootstrap.Popover.clickHandler) {
20447 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20449 // hide other popups.
20450 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20451 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20452 this.hideAll(); //<< why?
20453 //this.popups.push(popup);
20455 hideAll : function()
20457 this.popups.forEach(function(p) {
20461 onShow : function() {
20462 Roo.bootstrap.Popover.popups.push(this);
20464 onHide : function() {
20465 Roo.bootstrap.Popover.popups.remove(this);
20471 * Card header - holder for the card header elements.
20476 * @class Roo.bootstrap.PopoverNav
20477 * @extends Roo.bootstrap.NavGroup
20478 * Bootstrap Popover header navigation class
20480 * Create a new Popover Header Navigation
20481 * @param {Object} config The config object
20484 Roo.bootstrap.PopoverNav = function(config){
20485 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20488 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20491 container_method : 'getPopoverHeader'
20509 * @class Roo.bootstrap.Progress
20510 * @extends Roo.bootstrap.Component
20511 * Bootstrap Progress class
20512 * @cfg {Boolean} striped striped of the progress bar
20513 * @cfg {Boolean} active animated of the progress bar
20517 * Create a new Progress
20518 * @param {Object} config The config object
20521 Roo.bootstrap.Progress = function(config){
20522 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20525 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20530 getAutoCreate : function(){
20538 cfg.cls += ' progress-striped';
20542 cfg.cls += ' active';
20561 * @class Roo.bootstrap.ProgressBar
20562 * @extends Roo.bootstrap.Component
20563 * Bootstrap ProgressBar class
20564 * @cfg {Number} aria_valuenow aria-value now
20565 * @cfg {Number} aria_valuemin aria-value min
20566 * @cfg {Number} aria_valuemax aria-value max
20567 * @cfg {String} label label for the progress bar
20568 * @cfg {String} panel (success | info | warning | danger )
20569 * @cfg {String} role role of the progress bar
20570 * @cfg {String} sr_only text
20574 * Create a new ProgressBar
20575 * @param {Object} config The config object
20578 Roo.bootstrap.ProgressBar = function(config){
20579 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20582 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20586 aria_valuemax : 100,
20592 getAutoCreate : function()
20597 cls: 'progress-bar',
20598 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20610 cfg.role = this.role;
20613 if(this.aria_valuenow){
20614 cfg['aria-valuenow'] = this.aria_valuenow;
20617 if(this.aria_valuemin){
20618 cfg['aria-valuemin'] = this.aria_valuemin;
20621 if(this.aria_valuemax){
20622 cfg['aria-valuemax'] = this.aria_valuemax;
20625 if(this.label && !this.sr_only){
20626 cfg.html = this.label;
20630 cfg.cls += ' progress-bar-' + this.panel;
20636 update : function(aria_valuenow)
20638 this.aria_valuenow = aria_valuenow;
20640 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20655 * @class Roo.bootstrap.TabGroup
20656 * @extends Roo.bootstrap.Column
20657 * Bootstrap Column class
20658 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20659 * @cfg {Boolean} carousel true to make the group behave like a carousel
20660 * @cfg {Boolean} bullets show bullets for the panels
20661 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20662 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20663 * @cfg {Boolean} showarrow (true|false) show arrow default true
20666 * Create a new TabGroup
20667 * @param {Object} config The config object
20670 Roo.bootstrap.TabGroup = function(config){
20671 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20673 this.navId = Roo.id();
20676 Roo.bootstrap.TabGroup.register(this);
20680 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20683 transition : false,
20688 slideOnTouch : false,
20691 getAutoCreate : function()
20693 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20695 cfg.cls += ' tab-content';
20697 if (this.carousel) {
20698 cfg.cls += ' carousel slide';
20701 cls : 'carousel-inner',
20705 if(this.bullets && !Roo.isTouch){
20708 cls : 'carousel-bullets',
20712 if(this.bullets_cls){
20713 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20720 cfg.cn[0].cn.push(bullets);
20723 if(this.showarrow){
20724 cfg.cn[0].cn.push({
20726 class : 'carousel-arrow',
20730 class : 'carousel-prev',
20734 class : 'fa fa-chevron-left'
20740 class : 'carousel-next',
20744 class : 'fa fa-chevron-right'
20757 initEvents: function()
20759 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20760 // this.el.on("touchstart", this.onTouchStart, this);
20763 if(this.autoslide){
20766 this.slideFn = window.setInterval(function() {
20767 _this.showPanelNext();
20771 if(this.showarrow){
20772 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20773 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20779 // onTouchStart : function(e, el, o)
20781 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20785 // this.showPanelNext();
20789 getChildContainer : function()
20791 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20795 * register a Navigation item
20796 * @param {Roo.bootstrap.NavItem} the navitem to add
20798 register : function(item)
20800 this.tabs.push( item);
20801 item.navId = this.navId; // not really needed..
20806 getActivePanel : function()
20809 Roo.each(this.tabs, function(t) {
20819 getPanelByName : function(n)
20822 Roo.each(this.tabs, function(t) {
20823 if (t.tabId == n) {
20831 indexOfPanel : function(p)
20834 Roo.each(this.tabs, function(t,i) {
20835 if (t.tabId == p.tabId) {
20844 * show a specific panel
20845 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20846 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20848 showPanel : function (pan)
20850 if(this.transition || typeof(pan) == 'undefined'){
20851 Roo.log("waiting for the transitionend");
20855 if (typeof(pan) == 'number') {
20856 pan = this.tabs[pan];
20859 if (typeof(pan) == 'string') {
20860 pan = this.getPanelByName(pan);
20863 var cur = this.getActivePanel();
20866 Roo.log('pan or acitve pan is undefined');
20870 if (pan.tabId == this.getActivePanel().tabId) {
20874 if (false === cur.fireEvent('beforedeactivate')) {
20878 if(this.bullets > 0 && !Roo.isTouch){
20879 this.setActiveBullet(this.indexOfPanel(pan));
20882 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20884 //class="carousel-item carousel-item-next carousel-item-left"
20886 this.transition = true;
20887 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20888 var lr = dir == 'next' ? 'left' : 'right';
20889 pan.el.addClass(dir); // or prev
20890 pan.el.addClass('carousel-item-' + dir); // or prev
20891 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20892 cur.el.addClass(lr); // or right
20893 pan.el.addClass(lr);
20894 cur.el.addClass('carousel-item-' +lr); // or right
20895 pan.el.addClass('carousel-item-' +lr);
20899 cur.el.on('transitionend', function() {
20900 Roo.log("trans end?");
20902 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20903 pan.setActive(true);
20905 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20906 cur.setActive(false);
20908 _this.transition = false;
20910 }, this, { single: true } );
20915 cur.setActive(false);
20916 pan.setActive(true);
20921 showPanelNext : function()
20923 var i = this.indexOfPanel(this.getActivePanel());
20925 if (i >= this.tabs.length - 1 && !this.autoslide) {
20929 if (i >= this.tabs.length - 1 && this.autoslide) {
20933 this.showPanel(this.tabs[i+1]);
20936 showPanelPrev : function()
20938 var i = this.indexOfPanel(this.getActivePanel());
20940 if (i < 1 && !this.autoslide) {
20944 if (i < 1 && this.autoslide) {
20945 i = this.tabs.length;
20948 this.showPanel(this.tabs[i-1]);
20952 addBullet: function()
20954 if(!this.bullets || Roo.isTouch){
20957 var ctr = this.el.select('.carousel-bullets',true).first();
20958 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20959 var bullet = ctr.createChild({
20960 cls : 'bullet bullet-' + i
20961 },ctr.dom.lastChild);
20966 bullet.on('click', (function(e, el, o, ii, t){
20968 e.preventDefault();
20970 this.showPanel(ii);
20972 if(this.autoslide && this.slideFn){
20973 clearInterval(this.slideFn);
20974 this.slideFn = window.setInterval(function() {
20975 _this.showPanelNext();
20979 }).createDelegate(this, [i, bullet], true));
20984 setActiveBullet : function(i)
20990 Roo.each(this.el.select('.bullet', true).elements, function(el){
20991 el.removeClass('selected');
20994 var bullet = this.el.select('.bullet-' + i, true).first();
21000 bullet.addClass('selected');
21011 Roo.apply(Roo.bootstrap.TabGroup, {
21015 * register a Navigation Group
21016 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21018 register : function(navgrp)
21020 this.groups[navgrp.navId] = navgrp;
21024 * fetch a Navigation Group based on the navigation ID
21025 * if one does not exist , it will get created.
21026 * @param {string} the navgroup to add
21027 * @returns {Roo.bootstrap.NavGroup} the navgroup
21029 get: function(navId) {
21030 if (typeof(this.groups[navId]) == 'undefined') {
21031 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21033 return this.groups[navId] ;
21048 * @class Roo.bootstrap.TabPanel
21049 * @extends Roo.bootstrap.Component
21050 * Bootstrap TabPanel class
21051 * @cfg {Boolean} active panel active
21052 * @cfg {String} html panel content
21053 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21054 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21055 * @cfg {String} href click to link..
21056 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21060 * Create a new TabPanel
21061 * @param {Object} config The config object
21064 Roo.bootstrap.TabPanel = function(config){
21065 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21069 * Fires when the active status changes
21070 * @param {Roo.bootstrap.TabPanel} this
21071 * @param {Boolean} state the new state
21076 * @event beforedeactivate
21077 * Fires before a tab is de-activated - can be used to do validation on a form.
21078 * @param {Roo.bootstrap.TabPanel} this
21079 * @return {Boolean} false if there is an error
21082 'beforedeactivate': true
21085 this.tabId = this.tabId || Roo.id();
21089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21096 touchSlide : false,
21097 getAutoCreate : function(){
21102 // item is needed for carousel - not sure if it has any effect otherwise
21103 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21104 html: this.html || ''
21108 cfg.cls += ' active';
21112 cfg.tabId = this.tabId;
21120 initEvents: function()
21122 var p = this.parent();
21124 this.navId = this.navId || p.navId;
21126 if (typeof(this.navId) != 'undefined') {
21127 // not really needed.. but just in case.. parent should be a NavGroup.
21128 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21132 var i = tg.tabs.length - 1;
21134 if(this.active && tg.bullets > 0 && i < tg.bullets){
21135 tg.setActiveBullet(i);
21139 this.el.on('click', this.onClick, this);
21141 if(Roo.isTouch && this.touchSlide){
21142 this.el.on("touchstart", this.onTouchStart, this);
21143 this.el.on("touchmove", this.onTouchMove, this);
21144 this.el.on("touchend", this.onTouchEnd, this);
21149 onRender : function(ct, position)
21151 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21154 setActive : function(state)
21156 Roo.log("panel - set active " + this.tabId + "=" + state);
21158 this.active = state;
21160 this.el.removeClass('active');
21162 } else if (!this.el.hasClass('active')) {
21163 this.el.addClass('active');
21166 this.fireEvent('changed', this, state);
21169 onClick : function(e)
21171 e.preventDefault();
21173 if(!this.href.length){
21177 window.location.href = this.href;
21186 onTouchStart : function(e)
21188 this.swiping = false;
21190 this.startX = e.browserEvent.touches[0].clientX;
21191 this.startY = e.browserEvent.touches[0].clientY;
21194 onTouchMove : function(e)
21196 this.swiping = true;
21198 this.endX = e.browserEvent.touches[0].clientX;
21199 this.endY = e.browserEvent.touches[0].clientY;
21202 onTouchEnd : function(e)
21209 var tabGroup = this.parent();
21211 if(this.endX > this.startX){ // swiping right
21212 tabGroup.showPanelPrev();
21216 if(this.startX > this.endX){ // swiping left
21217 tabGroup.showPanelNext();
21236 * @class Roo.bootstrap.DateField
21237 * @extends Roo.bootstrap.Input
21238 * Bootstrap DateField class
21239 * @cfg {Number} weekStart default 0
21240 * @cfg {String} viewMode default empty, (months|years)
21241 * @cfg {String} minViewMode default empty, (months|years)
21242 * @cfg {Number} startDate default -Infinity
21243 * @cfg {Number} endDate default Infinity
21244 * @cfg {Boolean} todayHighlight default false
21245 * @cfg {Boolean} todayBtn default false
21246 * @cfg {Boolean} calendarWeeks default false
21247 * @cfg {Object} daysOfWeekDisabled default empty
21248 * @cfg {Boolean} singleMode default false (true | false)
21250 * @cfg {Boolean} keyboardNavigation default true
21251 * @cfg {String} language default en
21254 * Create a new DateField
21255 * @param {Object} config The config object
21258 Roo.bootstrap.DateField = function(config){
21259 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21263 * Fires when this field show.
21264 * @param {Roo.bootstrap.DateField} this
21265 * @param {Mixed} date The date value
21270 * Fires when this field hide.
21271 * @param {Roo.bootstrap.DateField} this
21272 * @param {Mixed} date The date value
21277 * Fires when select a date.
21278 * @param {Roo.bootstrap.DateField} this
21279 * @param {Mixed} date The date value
21283 * @event beforeselect
21284 * Fires when before select a date.
21285 * @param {Roo.bootstrap.DateField} this
21286 * @param {Mixed} date The date value
21288 beforeselect : true
21292 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21295 * @cfg {String} format
21296 * The default date format string which can be overriden for localization support. The format must be
21297 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21301 * @cfg {String} altFormats
21302 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21303 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21305 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21313 todayHighlight : false,
21319 keyboardNavigation: true,
21321 calendarWeeks: false,
21323 startDate: -Infinity,
21327 daysOfWeekDisabled: [],
21331 singleMode : false,
21333 UTCDate: function()
21335 return new Date(Date.UTC.apply(Date, arguments));
21338 UTCToday: function()
21340 var today = new Date();
21341 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21344 getDate: function() {
21345 var d = this.getUTCDate();
21346 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21349 getUTCDate: function() {
21353 setDate: function(d) {
21354 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21357 setUTCDate: function(d) {
21359 this.setValue(this.formatDate(this.date));
21362 onRender: function(ct, position)
21365 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21367 this.language = this.language || 'en';
21368 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21369 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21371 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21372 this.format = this.format || 'm/d/y';
21373 this.isInline = false;
21374 this.isInput = true;
21375 this.component = this.el.select('.add-on', true).first() || false;
21376 this.component = (this.component && this.component.length === 0) ? false : this.component;
21377 this.hasInput = this.component && this.inputEl().length;
21379 if (typeof(this.minViewMode === 'string')) {
21380 switch (this.minViewMode) {
21382 this.minViewMode = 1;
21385 this.minViewMode = 2;
21388 this.minViewMode = 0;
21393 if (typeof(this.viewMode === 'string')) {
21394 switch (this.viewMode) {
21407 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21409 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21411 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21413 this.picker().on('mousedown', this.onMousedown, this);
21414 this.picker().on('click', this.onClick, this);
21416 this.picker().addClass('datepicker-dropdown');
21418 this.startViewMode = this.viewMode;
21420 if(this.singleMode){
21421 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21422 v.setVisibilityMode(Roo.Element.DISPLAY);
21426 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21427 v.setStyle('width', '189px');
21431 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21432 if(!this.calendarWeeks){
21437 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21438 v.attr('colspan', function(i, val){
21439 return parseInt(val) + 1;
21444 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21446 this.setStartDate(this.startDate);
21447 this.setEndDate(this.endDate);
21449 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21456 if(this.isInline) {
21461 picker : function()
21463 return this.pickerEl;
21464 // return this.el.select('.datepicker', true).first();
21467 fillDow: function()
21469 var dowCnt = this.weekStart;
21478 if(this.calendarWeeks){
21486 while (dowCnt < this.weekStart + 7) {
21490 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21494 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21497 fillMonths: function()
21500 var months = this.picker().select('>.datepicker-months td', true).first();
21502 months.dom.innerHTML = '';
21508 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21511 months.createChild(month);
21518 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;
21520 if (this.date < this.startDate) {
21521 this.viewDate = new Date(this.startDate);
21522 } else if (this.date > this.endDate) {
21523 this.viewDate = new Date(this.endDate);
21525 this.viewDate = new Date(this.date);
21533 var d = new Date(this.viewDate),
21534 year = d.getUTCFullYear(),
21535 month = d.getUTCMonth(),
21536 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21537 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21538 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21539 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21540 currentDate = this.date && this.date.valueOf(),
21541 today = this.UTCToday();
21543 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21545 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21547 // this.picker.select('>tfoot th.today').
21548 // .text(dates[this.language].today)
21549 // .toggle(this.todayBtn !== false);
21551 this.updateNavArrows();
21554 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21556 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21558 prevMonth.setUTCDate(day);
21560 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21562 var nextMonth = new Date(prevMonth);
21564 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21566 nextMonth = nextMonth.valueOf();
21568 var fillMonths = false;
21570 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21572 while(prevMonth.valueOf() <= nextMonth) {
21575 if (prevMonth.getUTCDay() === this.weekStart) {
21577 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21585 if(this.calendarWeeks){
21586 // ISO 8601: First week contains first thursday.
21587 // ISO also states week starts on Monday, but we can be more abstract here.
21589 // Start of current week: based on weekstart/current date
21590 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21591 // Thursday of this week
21592 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21593 // First Thursday of year, year from thursday
21594 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21595 // Calendar week: ms between thursdays, div ms per day, div 7 days
21596 calWeek = (th - yth) / 864e5 / 7 + 1;
21598 fillMonths.cn.push({
21606 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21608 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21611 if (this.todayHighlight &&
21612 prevMonth.getUTCFullYear() == today.getFullYear() &&
21613 prevMonth.getUTCMonth() == today.getMonth() &&
21614 prevMonth.getUTCDate() == today.getDate()) {
21615 clsName += ' today';
21618 if (currentDate && prevMonth.valueOf() === currentDate) {
21619 clsName += ' active';
21622 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21623 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21624 clsName += ' disabled';
21627 fillMonths.cn.push({
21629 cls: 'day ' + clsName,
21630 html: prevMonth.getDate()
21633 prevMonth.setDate(prevMonth.getDate()+1);
21636 var currentYear = this.date && this.date.getUTCFullYear();
21637 var currentMonth = this.date && this.date.getUTCMonth();
21639 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21641 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21642 v.removeClass('active');
21644 if(currentYear === year && k === currentMonth){
21645 v.addClass('active');
21648 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21649 v.addClass('disabled');
21655 year = parseInt(year/10, 10) * 10;
21657 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21659 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21662 for (var i = -1; i < 11; i++) {
21663 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21665 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21673 showMode: function(dir)
21676 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21679 Roo.each(this.picker().select('>div',true).elements, function(v){
21680 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21683 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21688 if(this.isInline) {
21692 this.picker().removeClass(['bottom', 'top']);
21694 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21696 * place to the top of element!
21700 this.picker().addClass('top');
21701 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21706 this.picker().addClass('bottom');
21708 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21711 parseDate : function(value)
21713 if(!value || value instanceof Date){
21716 var v = Date.parseDate(value, this.format);
21717 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21718 v = Date.parseDate(value, 'Y-m-d');
21720 if(!v && this.altFormats){
21721 if(!this.altFormatsArray){
21722 this.altFormatsArray = this.altFormats.split("|");
21724 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21725 v = Date.parseDate(value, this.altFormatsArray[i]);
21731 formatDate : function(date, fmt)
21733 return (!date || !(date instanceof Date)) ?
21734 date : date.dateFormat(fmt || this.format);
21737 onFocus : function()
21739 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21743 onBlur : function()
21745 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21747 var d = this.inputEl().getValue();
21754 showPopup : function()
21756 this.picker().show();
21760 this.fireEvent('showpopup', this, this.date);
21763 hidePopup : function()
21765 if(this.isInline) {
21768 this.picker().hide();
21769 this.viewMode = this.startViewMode;
21772 this.fireEvent('hidepopup', this, this.date);
21776 onMousedown: function(e)
21778 e.stopPropagation();
21779 e.preventDefault();
21784 Roo.bootstrap.DateField.superclass.keyup.call(this);
21788 setValue: function(v)
21790 if(this.fireEvent('beforeselect', this, v) !== false){
21791 var d = new Date(this.parseDate(v) ).clearTime();
21793 if(isNaN(d.getTime())){
21794 this.date = this.viewDate = '';
21795 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21799 v = this.formatDate(d);
21801 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21803 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21807 this.fireEvent('select', this, this.date);
21811 getValue: function()
21813 return this.formatDate(this.date);
21816 fireKey: function(e)
21818 if (!this.picker().isVisible()){
21819 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21825 var dateChanged = false,
21827 newDate, newViewDate;
21832 e.preventDefault();
21836 if (!this.keyboardNavigation) {
21839 dir = e.keyCode == 37 ? -1 : 1;
21842 newDate = this.moveYear(this.date, dir);
21843 newViewDate = this.moveYear(this.viewDate, dir);
21844 } else if (e.shiftKey){
21845 newDate = this.moveMonth(this.date, dir);
21846 newViewDate = this.moveMonth(this.viewDate, dir);
21848 newDate = new Date(this.date);
21849 newDate.setUTCDate(this.date.getUTCDate() + dir);
21850 newViewDate = new Date(this.viewDate);
21851 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21853 if (this.dateWithinRange(newDate)){
21854 this.date = newDate;
21855 this.viewDate = newViewDate;
21856 this.setValue(this.formatDate(this.date));
21858 e.preventDefault();
21859 dateChanged = true;
21864 if (!this.keyboardNavigation) {
21867 dir = e.keyCode == 38 ? -1 : 1;
21869 newDate = this.moveYear(this.date, dir);
21870 newViewDate = this.moveYear(this.viewDate, dir);
21871 } else if (e.shiftKey){
21872 newDate = this.moveMonth(this.date, dir);
21873 newViewDate = this.moveMonth(this.viewDate, dir);
21875 newDate = new Date(this.date);
21876 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21877 newViewDate = new Date(this.viewDate);
21878 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21880 if (this.dateWithinRange(newDate)){
21881 this.date = newDate;
21882 this.viewDate = newViewDate;
21883 this.setValue(this.formatDate(this.date));
21885 e.preventDefault();
21886 dateChanged = true;
21890 this.setValue(this.formatDate(this.date));
21892 e.preventDefault();
21895 this.setValue(this.formatDate(this.date));
21909 onClick: function(e)
21911 e.stopPropagation();
21912 e.preventDefault();
21914 var target = e.getTarget();
21916 if(target.nodeName.toLowerCase() === 'i'){
21917 target = Roo.get(target).dom.parentNode;
21920 var nodeName = target.nodeName;
21921 var className = target.className;
21922 var html = target.innerHTML;
21923 //Roo.log(nodeName);
21925 switch(nodeName.toLowerCase()) {
21927 switch(className) {
21933 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21934 switch(this.viewMode){
21936 this.viewDate = this.moveMonth(this.viewDate, dir);
21940 this.viewDate = this.moveYear(this.viewDate, dir);
21946 var date = new Date();
21947 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21949 this.setValue(this.formatDate(this.date));
21956 if (className.indexOf('disabled') < 0) {
21957 this.viewDate.setUTCDate(1);
21958 if (className.indexOf('month') > -1) {
21959 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21961 var year = parseInt(html, 10) || 0;
21962 this.viewDate.setUTCFullYear(year);
21966 if(this.singleMode){
21967 this.setValue(this.formatDate(this.viewDate));
21978 //Roo.log(className);
21979 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21980 var day = parseInt(html, 10) || 1;
21981 var year = (this.viewDate || new Date()).getUTCFullYear(),
21982 month = (this.viewDate || new Date()).getUTCMonth();
21984 if (className.indexOf('old') > -1) {
21991 } else if (className.indexOf('new') > -1) {
21999 //Roo.log([year,month,day]);
22000 this.date = this.UTCDate(year, month, day,0,0,0,0);
22001 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22003 //Roo.log(this.formatDate(this.date));
22004 this.setValue(this.formatDate(this.date));
22011 setStartDate: function(startDate)
22013 this.startDate = startDate || -Infinity;
22014 if (this.startDate !== -Infinity) {
22015 this.startDate = this.parseDate(this.startDate);
22018 this.updateNavArrows();
22021 setEndDate: function(endDate)
22023 this.endDate = endDate || Infinity;
22024 if (this.endDate !== Infinity) {
22025 this.endDate = this.parseDate(this.endDate);
22028 this.updateNavArrows();
22031 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22033 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22034 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22035 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22037 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22038 return parseInt(d, 10);
22041 this.updateNavArrows();
22044 updateNavArrows: function()
22046 if(this.singleMode){
22050 var d = new Date(this.viewDate),
22051 year = d.getUTCFullYear(),
22052 month = d.getUTCMonth();
22054 Roo.each(this.picker().select('.prev', true).elements, function(v){
22056 switch (this.viewMode) {
22059 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22065 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22072 Roo.each(this.picker().select('.next', true).elements, function(v){
22074 switch (this.viewMode) {
22077 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22083 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22091 moveMonth: function(date, dir)
22096 var new_date = new Date(date.valueOf()),
22097 day = new_date.getUTCDate(),
22098 month = new_date.getUTCMonth(),
22099 mag = Math.abs(dir),
22101 dir = dir > 0 ? 1 : -1;
22104 // If going back one month, make sure month is not current month
22105 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22107 return new_date.getUTCMonth() == month;
22109 // If going forward one month, make sure month is as expected
22110 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22112 return new_date.getUTCMonth() != new_month;
22114 new_month = month + dir;
22115 new_date.setUTCMonth(new_month);
22116 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22117 if (new_month < 0 || new_month > 11) {
22118 new_month = (new_month + 12) % 12;
22121 // For magnitudes >1, move one month at a time...
22122 for (var i=0; i<mag; i++) {
22123 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22124 new_date = this.moveMonth(new_date, dir);
22126 // ...then reset the day, keeping it in the new month
22127 new_month = new_date.getUTCMonth();
22128 new_date.setUTCDate(day);
22130 return new_month != new_date.getUTCMonth();
22133 // Common date-resetting loop -- if date is beyond end of month, make it
22136 new_date.setUTCDate(--day);
22137 new_date.setUTCMonth(new_month);
22142 moveYear: function(date, dir)
22144 return this.moveMonth(date, dir*12);
22147 dateWithinRange: function(date)
22149 return date >= this.startDate && date <= this.endDate;
22155 this.picker().remove();
22158 validateValue : function(value)
22160 if(this.getVisibilityEl().hasClass('hidden')){
22164 if(value.length < 1) {
22165 if(this.allowBlank){
22171 if(value.length < this.minLength){
22174 if(value.length > this.maxLength){
22178 var vt = Roo.form.VTypes;
22179 if(!vt[this.vtype](value, this)){
22183 if(typeof this.validator == "function"){
22184 var msg = this.validator(value);
22190 if(this.regex && !this.regex.test(value)){
22194 if(typeof(this.parseDate(value)) == 'undefined'){
22198 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22202 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22212 this.date = this.viewDate = '';
22214 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22219 Roo.apply(Roo.bootstrap.DateField, {
22230 html: '<i class="fa fa-arrow-left"/>'
22240 html: '<i class="fa fa-arrow-right"/>'
22282 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22283 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22284 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22285 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22286 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22299 navFnc: 'FullYear',
22304 navFnc: 'FullYear',
22309 Roo.apply(Roo.bootstrap.DateField, {
22313 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22317 cls: 'datepicker-days',
22321 cls: 'table-condensed',
22323 Roo.bootstrap.DateField.head,
22327 Roo.bootstrap.DateField.footer
22334 cls: 'datepicker-months',
22338 cls: 'table-condensed',
22340 Roo.bootstrap.DateField.head,
22341 Roo.bootstrap.DateField.content,
22342 Roo.bootstrap.DateField.footer
22349 cls: 'datepicker-years',
22353 cls: 'table-condensed',
22355 Roo.bootstrap.DateField.head,
22356 Roo.bootstrap.DateField.content,
22357 Roo.bootstrap.DateField.footer
22376 * @class Roo.bootstrap.TimeField
22377 * @extends Roo.bootstrap.Input
22378 * Bootstrap DateField class
22382 * Create a new TimeField
22383 * @param {Object} config The config object
22386 Roo.bootstrap.TimeField = function(config){
22387 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22391 * Fires when this field show.
22392 * @param {Roo.bootstrap.DateField} thisthis
22393 * @param {Mixed} date The date value
22398 * Fires when this field hide.
22399 * @param {Roo.bootstrap.DateField} this
22400 * @param {Mixed} date The date value
22405 * Fires when select a date.
22406 * @param {Roo.bootstrap.DateField} this
22407 * @param {Mixed} date The date value
22413 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22416 * @cfg {String} format
22417 * The default time format string which can be overriden for localization support. The format must be
22418 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22422 getAutoCreate : function()
22424 this.after = '<i class="fa far fa-clock"></i>';
22425 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22429 onRender: function(ct, position)
22432 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22434 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22436 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22438 this.pop = this.picker().select('>.datepicker-time',true).first();
22439 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22441 this.picker().on('mousedown', this.onMousedown, this);
22442 this.picker().on('click', this.onClick, this);
22444 this.picker().addClass('datepicker-dropdown');
22449 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22450 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22451 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22452 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22453 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22454 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22458 fireKey: function(e){
22459 if (!this.picker().isVisible()){
22460 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22466 e.preventDefault();
22474 this.onTogglePeriod();
22477 this.onIncrementMinutes();
22480 this.onDecrementMinutes();
22489 onClick: function(e) {
22490 e.stopPropagation();
22491 e.preventDefault();
22494 picker : function()
22496 return this.pickerEl;
22499 fillTime: function()
22501 var time = this.pop.select('tbody', true).first();
22503 time.dom.innerHTML = '';
22518 cls: 'hours-up fa fas fa-chevron-up'
22538 cls: 'minutes-up fa fas fa-chevron-up'
22559 cls: 'timepicker-hour',
22574 cls: 'timepicker-minute',
22589 cls: 'btn btn-primary period',
22611 cls: 'hours-down fa fas fa-chevron-down'
22631 cls: 'minutes-down fa fas fa-chevron-down'
22649 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22656 var hours = this.time.getHours();
22657 var minutes = this.time.getMinutes();
22670 hours = hours - 12;
22674 hours = '0' + hours;
22678 minutes = '0' + minutes;
22681 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22682 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22683 this.pop.select('button', true).first().dom.innerHTML = period;
22689 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22691 var cls = ['bottom'];
22693 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22700 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22704 //this.picker().setXY(20000,20000);
22705 this.picker().addClass(cls.join('-'));
22709 Roo.each(cls, function(c){
22714 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22715 //_this.picker().setTop(_this.inputEl().getHeight());
22719 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22721 //_this.picker().setTop(0 - _this.picker().getHeight());
22726 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22730 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22738 onFocus : function()
22740 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22744 onBlur : function()
22746 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22752 this.picker().show();
22757 this.fireEvent('show', this, this.date);
22762 this.picker().hide();
22765 this.fireEvent('hide', this, this.date);
22768 setTime : function()
22771 this.setValue(this.time.format(this.format));
22773 this.fireEvent('select', this, this.date);
22778 onMousedown: function(e){
22779 e.stopPropagation();
22780 e.preventDefault();
22783 onIncrementHours: function()
22785 Roo.log('onIncrementHours');
22786 this.time = this.time.add(Date.HOUR, 1);
22791 onDecrementHours: function()
22793 Roo.log('onDecrementHours');
22794 this.time = this.time.add(Date.HOUR, -1);
22798 onIncrementMinutes: function()
22800 Roo.log('onIncrementMinutes');
22801 this.time = this.time.add(Date.MINUTE, 1);
22805 onDecrementMinutes: function()
22807 Roo.log('onDecrementMinutes');
22808 this.time = this.time.add(Date.MINUTE, -1);
22812 onTogglePeriod: function()
22814 Roo.log('onTogglePeriod');
22815 this.time = this.time.add(Date.HOUR, 12);
22823 Roo.apply(Roo.bootstrap.TimeField, {
22827 cls: 'datepicker dropdown-menu',
22831 cls: 'datepicker-time',
22835 cls: 'table-condensed',
22864 cls: 'btn btn-info ok',
22892 * @class Roo.bootstrap.MonthField
22893 * @extends Roo.bootstrap.Input
22894 * Bootstrap MonthField class
22896 * @cfg {String} language default en
22899 * Create a new MonthField
22900 * @param {Object} config The config object
22903 Roo.bootstrap.MonthField = function(config){
22904 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22909 * Fires when this field show.
22910 * @param {Roo.bootstrap.MonthField} this
22911 * @param {Mixed} date The date value
22916 * Fires when this field hide.
22917 * @param {Roo.bootstrap.MonthField} this
22918 * @param {Mixed} date The date value
22923 * Fires when select a date.
22924 * @param {Roo.bootstrap.MonthField} this
22925 * @param {String} oldvalue The old value
22926 * @param {String} newvalue The new value
22932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22934 onRender: function(ct, position)
22937 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22939 this.language = this.language || 'en';
22940 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22941 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22943 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22944 this.isInline = false;
22945 this.isInput = true;
22946 this.component = this.el.select('.add-on', true).first() || false;
22947 this.component = (this.component && this.component.length === 0) ? false : this.component;
22948 this.hasInput = this.component && this.inputEL().length;
22950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22954 this.picker().on('mousedown', this.onMousedown, this);
22955 this.picker().on('click', this.onClick, this);
22957 this.picker().addClass('datepicker-dropdown');
22959 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22960 v.setStyle('width', '189px');
22967 if(this.isInline) {
22973 setValue: function(v, suppressEvent)
22975 var o = this.getValue();
22977 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22981 if(suppressEvent !== true){
22982 this.fireEvent('select', this, o, v);
22987 getValue: function()
22992 onClick: function(e)
22994 e.stopPropagation();
22995 e.preventDefault();
22997 var target = e.getTarget();
22999 if(target.nodeName.toLowerCase() === 'i'){
23000 target = Roo.get(target).dom.parentNode;
23003 var nodeName = target.nodeName;
23004 var className = target.className;
23005 var html = target.innerHTML;
23007 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23011 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23013 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23019 picker : function()
23021 return this.pickerEl;
23024 fillMonths: function()
23027 var months = this.picker().select('>.datepicker-months td', true).first();
23029 months.dom.innerHTML = '';
23035 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23038 months.createChild(month);
23047 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23048 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23051 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23052 e.removeClass('active');
23054 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23055 e.addClass('active');
23062 if(this.isInline) {
23066 this.picker().removeClass(['bottom', 'top']);
23068 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23070 * place to the top of element!
23074 this.picker().addClass('top');
23075 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23080 this.picker().addClass('bottom');
23082 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23085 onFocus : function()
23087 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23091 onBlur : function()
23093 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23095 var d = this.inputEl().getValue();
23104 this.picker().show();
23105 this.picker().select('>.datepicker-months', true).first().show();
23109 this.fireEvent('show', this, this.date);
23114 if(this.isInline) {
23117 this.picker().hide();
23118 this.fireEvent('hide', this, this.date);
23122 onMousedown: function(e)
23124 e.stopPropagation();
23125 e.preventDefault();
23130 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23134 fireKey: function(e)
23136 if (!this.picker().isVisible()){
23137 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23148 e.preventDefault();
23152 dir = e.keyCode == 37 ? -1 : 1;
23154 this.vIndex = this.vIndex + dir;
23156 if(this.vIndex < 0){
23160 if(this.vIndex > 11){
23164 if(isNaN(this.vIndex)){
23168 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23174 dir = e.keyCode == 38 ? -1 : 1;
23176 this.vIndex = this.vIndex + dir * 4;
23178 if(this.vIndex < 0){
23182 if(this.vIndex > 11){
23186 if(isNaN(this.vIndex)){
23190 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23195 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23196 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23200 e.preventDefault();
23203 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23204 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23220 this.picker().remove();
23225 Roo.apply(Roo.bootstrap.MonthField, {
23244 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23245 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23250 Roo.apply(Roo.bootstrap.MonthField, {
23254 cls: 'datepicker dropdown-menu roo-dynamic',
23258 cls: 'datepicker-months',
23262 cls: 'table-condensed',
23264 Roo.bootstrap.DateField.content
23284 * @class Roo.bootstrap.CheckBox
23285 * @extends Roo.bootstrap.Input
23286 * Bootstrap CheckBox class
23288 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23289 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23290 * @cfg {String} boxLabel The text that appears beside the checkbox
23291 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23292 * @cfg {Boolean} checked initnal the element
23293 * @cfg {Boolean} inline inline the element (default false)
23294 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23295 * @cfg {String} tooltip label tooltip
23298 * Create a new CheckBox
23299 * @param {Object} config The config object
23302 Roo.bootstrap.CheckBox = function(config){
23303 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23308 * Fires when the element is checked or unchecked.
23309 * @param {Roo.bootstrap.CheckBox} this This input
23310 * @param {Boolean} checked The new checked value
23315 * Fires when the element is click.
23316 * @param {Roo.bootstrap.CheckBox} this This input
23323 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23325 inputType: 'checkbox',
23334 // checkbox success does not make any sense really..
23339 getAutoCreate : function()
23341 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23347 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23350 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23356 type : this.inputType,
23357 value : this.inputValue,
23358 cls : 'roo-' + this.inputType, //'form-box',
23359 placeholder : this.placeholder || ''
23363 if(this.inputType != 'radio'){
23367 cls : 'roo-hidden-value',
23368 value : this.checked ? this.inputValue : this.valueOff
23373 if (this.weight) { // Validity check?
23374 cfg.cls += " " + this.inputType + "-" + this.weight;
23377 if (this.disabled) {
23378 input.disabled=true;
23382 input.checked = this.checked;
23387 input.name = this.name;
23389 if(this.inputType != 'radio'){
23390 hidden.name = this.name;
23391 input.name = '_hidden_' + this.name;
23396 input.cls += ' input-' + this.size;
23401 ['xs','sm','md','lg'].map(function(size){
23402 if (settings[size]) {
23403 cfg.cls += ' col-' + size + '-' + settings[size];
23407 var inputblock = input;
23409 if (this.before || this.after) {
23412 cls : 'input-group',
23417 inputblock.cn.push({
23419 cls : 'input-group-addon',
23424 inputblock.cn.push(input);
23426 if(this.inputType != 'radio'){
23427 inputblock.cn.push(hidden);
23431 inputblock.cn.push({
23433 cls : 'input-group-addon',
23439 var boxLabelCfg = false;
23445 //'for': id, // box label is handled by onclick - so no for...
23447 html: this.boxLabel
23450 boxLabelCfg.tooltip = this.tooltip;
23456 if (align ==='left' && this.fieldLabel.length) {
23457 // Roo.log("left and has label");
23462 cls : 'control-label',
23463 html : this.fieldLabel
23474 cfg.cn[1].cn.push(boxLabelCfg);
23477 if(this.labelWidth > 12){
23478 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23481 if(this.labelWidth < 13 && this.labelmd == 0){
23482 this.labelmd = this.labelWidth;
23485 if(this.labellg > 0){
23486 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23487 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23490 if(this.labelmd > 0){
23491 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23492 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23495 if(this.labelsm > 0){
23496 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23497 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23500 if(this.labelxs > 0){
23501 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23502 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23505 } else if ( this.fieldLabel.length) {
23506 // Roo.log(" label");
23510 tag: this.boxLabel ? 'span' : 'label',
23512 cls: 'control-label box-input-label',
23513 //cls : 'input-group-addon',
23514 html : this.fieldLabel
23521 cfg.cn.push(boxLabelCfg);
23526 // Roo.log(" no label && no align");
23527 cfg.cn = [ inputblock ] ;
23529 cfg.cn.push(boxLabelCfg);
23537 if(this.inputType != 'radio'){
23538 cfg.cn.push(hidden);
23546 * return the real input element.
23548 inputEl: function ()
23550 return this.el.select('input.roo-' + this.inputType,true).first();
23552 hiddenEl: function ()
23554 return this.el.select('input.roo-hidden-value',true).first();
23557 labelEl: function()
23559 return this.el.select('label.control-label',true).first();
23561 /* depricated... */
23565 return this.labelEl();
23568 boxLabelEl: function()
23570 return this.el.select('label.box-label',true).first();
23573 initEvents : function()
23575 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23577 this.inputEl().on('click', this.onClick, this);
23579 if (this.boxLabel) {
23580 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23583 this.startValue = this.getValue();
23586 Roo.bootstrap.CheckBox.register(this);
23590 onClick : function(e)
23592 if(this.fireEvent('click', this, e) !== false){
23593 this.setChecked(!this.checked);
23598 setChecked : function(state,suppressEvent)
23600 this.startValue = this.getValue();
23602 if(this.inputType == 'radio'){
23604 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23605 e.dom.checked = false;
23608 this.inputEl().dom.checked = true;
23610 this.inputEl().dom.value = this.inputValue;
23612 if(suppressEvent !== true){
23613 this.fireEvent('check', this, true);
23621 this.checked = state;
23623 this.inputEl().dom.checked = state;
23626 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23628 if(suppressEvent !== true){
23629 this.fireEvent('check', this, state);
23635 getValue : function()
23637 if(this.inputType == 'radio'){
23638 return this.getGroupValue();
23641 return this.hiddenEl().dom.value;
23645 getGroupValue : function()
23647 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23651 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23654 setValue : function(v,suppressEvent)
23656 if(this.inputType == 'radio'){
23657 this.setGroupValue(v, suppressEvent);
23661 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23666 setGroupValue : function(v, suppressEvent)
23668 this.startValue = this.getValue();
23670 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671 e.dom.checked = false;
23673 if(e.dom.value == v){
23674 e.dom.checked = true;
23678 if(suppressEvent !== true){
23679 this.fireEvent('check', this, true);
23687 validate : function()
23689 if(this.getVisibilityEl().hasClass('hidden')){
23695 (this.inputType == 'radio' && this.validateRadio()) ||
23696 (this.inputType == 'checkbox' && this.validateCheckbox())
23702 this.markInvalid();
23706 validateRadio : function()
23708 if(this.getVisibilityEl().hasClass('hidden')){
23712 if(this.allowBlank){
23718 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23719 if(!e.dom.checked){
23731 validateCheckbox : function()
23734 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23735 //return (this.getValue() == this.inputValue) ? true : false;
23738 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23746 for(var i in group){
23747 if(group[i].el.isVisible(true)){
23755 for(var i in group){
23760 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23767 * Mark this field as valid
23769 markValid : function()
23773 this.fireEvent('valid', this);
23775 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23778 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23785 if(this.inputType == 'radio'){
23786 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23787 var fg = e.findParent('.form-group', false, true);
23788 if (Roo.bootstrap.version == 3) {
23789 fg.removeClass([_this.invalidClass, _this.validClass]);
23790 fg.addClass(_this.validClass);
23792 fg.removeClass(['is-valid', 'is-invalid']);
23793 fg.addClass('is-valid');
23801 var fg = this.el.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');
23812 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23818 for(var i in group){
23819 var fg = group[i].el.findParent('.form-group', false, true);
23820 if (Roo.bootstrap.version == 3) {
23821 fg.removeClass([this.invalidClass, this.validClass]);
23822 fg.addClass(this.validClass);
23824 fg.removeClass(['is-valid', 'is-invalid']);
23825 fg.addClass('is-valid');
23831 * Mark this field as invalid
23832 * @param {String} msg The validation message
23834 markInvalid : function(msg)
23836 if(this.allowBlank){
23842 this.fireEvent('invalid', this, msg);
23844 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23847 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23851 label.markInvalid();
23854 if(this.inputType == 'radio'){
23856 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23857 var fg = e.findParent('.form-group', false, true);
23858 if (Roo.bootstrap.version == 3) {
23859 fg.removeClass([_this.invalidClass, _this.validClass]);
23860 fg.addClass(_this.invalidClass);
23862 fg.removeClass(['is-invalid', 'is-valid']);
23863 fg.addClass('is-invalid');
23871 var fg = this.el.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');
23882 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23888 for(var i in group){
23889 var fg = group[i].el.findParent('.form-group', false, true);
23890 if (Roo.bootstrap.version == 3) {
23891 fg.removeClass([_this.invalidClass, _this.validClass]);
23892 fg.addClass(_this.invalidClass);
23894 fg.removeClass(['is-invalid', 'is-valid']);
23895 fg.addClass('is-invalid');
23901 clearInvalid : function()
23903 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23905 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23907 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23909 if (label && label.iconEl) {
23910 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23911 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23915 disable : function()
23917 if(this.inputType != 'radio'){
23918 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23925 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23926 _this.getActionEl().addClass(this.disabledClass);
23927 e.dom.disabled = true;
23931 this.disabled = true;
23932 this.fireEvent("disable", this);
23936 enable : function()
23938 if(this.inputType != 'radio'){
23939 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23946 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23947 _this.getActionEl().removeClass(this.disabledClass);
23948 e.dom.disabled = false;
23952 this.disabled = false;
23953 this.fireEvent("enable", this);
23957 setBoxLabel : function(v)
23962 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23968 Roo.apply(Roo.bootstrap.CheckBox, {
23973 * register a CheckBox Group
23974 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23976 register : function(checkbox)
23978 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23979 this.groups[checkbox.groupId] = {};
23982 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23986 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23990 * fetch a CheckBox Group based on the group ID
23991 * @param {string} the group ID
23992 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23994 get: function(groupId) {
23995 if (typeof(this.groups[groupId]) == 'undefined') {
23999 return this.groups[groupId] ;
24012 * @class Roo.bootstrap.Radio
24013 * @extends Roo.bootstrap.Component
24014 * Bootstrap Radio class
24015 * @cfg {String} boxLabel - the label associated
24016 * @cfg {String} value - the value of radio
24019 * Create a new Radio
24020 * @param {Object} config The config object
24022 Roo.bootstrap.Radio = function(config){
24023 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24027 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24033 getAutoCreate : function()
24037 cls : 'form-group radio',
24042 html : this.boxLabel
24050 initEvents : function()
24052 this.parent().register(this);
24054 this.el.on('click', this.onClick, this);
24058 onClick : function(e)
24060 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24061 this.setChecked(true);
24065 setChecked : function(state, suppressEvent)
24067 this.parent().setValue(this.value, suppressEvent);
24071 setBoxLabel : function(v)
24076 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24091 * @class Roo.bootstrap.SecurePass
24092 * @extends Roo.bootstrap.Input
24093 * Bootstrap SecurePass class
24097 * Create a new SecurePass
24098 * @param {Object} config The config object
24101 Roo.bootstrap.SecurePass = function (config) {
24102 // these go here, so the translation tool can replace them..
24104 PwdEmpty: "Please type a password, and then retype it to confirm.",
24105 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24106 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24107 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24108 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24109 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24110 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24111 TooWeak: "Your password is Too Weak."
24113 this.meterLabel = "Password strength:";
24114 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24115 this.meterClass = [
24116 "roo-password-meter-tooweak",
24117 "roo-password-meter-weak",
24118 "roo-password-meter-medium",
24119 "roo-password-meter-strong",
24120 "roo-password-meter-grey"
24125 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24128 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24130 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24132 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24133 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24134 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24135 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24136 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24137 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24138 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24148 * @cfg {String/Object} Label for the strength meter (defaults to
24149 * 'Password strength:')
24154 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24155 * ['Weak', 'Medium', 'Strong'])
24158 pwdStrengths: false,
24171 initEvents: function ()
24173 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24175 if (this.el.is('input[type=password]') && Roo.isSafari) {
24176 this.el.on('keydown', this.SafariOnKeyDown, this);
24179 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24182 onRender: function (ct, position)
24184 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24185 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24186 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24188 this.trigger.createChild({
24193 cls: 'roo-password-meter-grey col-xs-12',
24196 //width: this.meterWidth + 'px'
24200 cls: 'roo-password-meter-text'
24206 if (this.hideTrigger) {
24207 this.trigger.setDisplayed(false);
24209 this.setSize(this.width || '', this.height || '');
24212 onDestroy: function ()
24214 if (this.trigger) {
24215 this.trigger.removeAllListeners();
24216 this.trigger.remove();
24219 this.wrap.remove();
24221 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24224 checkStrength: function ()
24226 var pwd = this.inputEl().getValue();
24227 if (pwd == this._lastPwd) {
24232 if (this.ClientSideStrongPassword(pwd)) {
24234 } else if (this.ClientSideMediumPassword(pwd)) {
24236 } else if (this.ClientSideWeakPassword(pwd)) {
24242 Roo.log('strength1: ' + strength);
24244 //var pm = this.trigger.child('div/div/div').dom;
24245 var pm = this.trigger.child('div/div');
24246 pm.removeClass(this.meterClass);
24247 pm.addClass(this.meterClass[strength]);
24250 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24252 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24254 this._lastPwd = pwd;
24258 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24260 this._lastPwd = '';
24262 var pm = this.trigger.child('div/div');
24263 pm.removeClass(this.meterClass);
24264 pm.addClass('roo-password-meter-grey');
24267 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24270 this.inputEl().dom.type='password';
24273 validateValue: function (value)
24275 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24278 if (value.length == 0) {
24279 if (this.allowBlank) {
24280 this.clearInvalid();
24284 this.markInvalid(this.errors.PwdEmpty);
24285 this.errorMsg = this.errors.PwdEmpty;
24293 if (!value.match(/[\x21-\x7e]+/)) {
24294 this.markInvalid(this.errors.PwdBadChar);
24295 this.errorMsg = this.errors.PwdBadChar;
24298 if (value.length < 6) {
24299 this.markInvalid(this.errors.PwdShort);
24300 this.errorMsg = this.errors.PwdShort;
24303 if (value.length > 16) {
24304 this.markInvalid(this.errors.PwdLong);
24305 this.errorMsg = this.errors.PwdLong;
24309 if (this.ClientSideStrongPassword(value)) {
24311 } else if (this.ClientSideMediumPassword(value)) {
24313 } else if (this.ClientSideWeakPassword(value)) {
24320 if (strength < 2) {
24321 //this.markInvalid(this.errors.TooWeak);
24322 this.errorMsg = this.errors.TooWeak;
24327 console.log('strength2: ' + strength);
24329 //var pm = this.trigger.child('div/div/div').dom;
24331 var pm = this.trigger.child('div/div');
24332 pm.removeClass(this.meterClass);
24333 pm.addClass(this.meterClass[strength]);
24335 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24337 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24339 this.errorMsg = '';
24343 CharacterSetChecks: function (type)
24346 this.fResult = false;
24349 isctype: function (character, type)
24352 case this.kCapitalLetter:
24353 if (character >= 'A' && character <= 'Z') {
24358 case this.kSmallLetter:
24359 if (character >= 'a' && character <= 'z') {
24365 if (character >= '0' && character <= '9') {
24370 case this.kPunctuation:
24371 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24382 IsLongEnough: function (pwd, size)
24384 return !(pwd == null || isNaN(size) || pwd.length < size);
24387 SpansEnoughCharacterSets: function (word, nb)
24389 if (!this.IsLongEnough(word, nb))
24394 var characterSetChecks = new Array(
24395 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24396 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24399 for (var index = 0; index < word.length; ++index) {
24400 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24401 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24402 characterSetChecks[nCharSet].fResult = true;
24409 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24410 if (characterSetChecks[nCharSet].fResult) {
24415 if (nCharSets < nb) {
24421 ClientSideStrongPassword: function (pwd)
24423 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24426 ClientSideMediumPassword: function (pwd)
24428 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24431 ClientSideWeakPassword: function (pwd)
24433 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24436 })//<script type="text/javascript">
24439 * Based Ext JS Library 1.1.1
24440 * Copyright(c) 2006-2007, Ext JS, LLC.
24446 * @class Roo.HtmlEditorCore
24447 * @extends Roo.Component
24448 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24450 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24453 Roo.HtmlEditorCore = function(config){
24456 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24461 * @event initialize
24462 * Fires when the editor is fully initialized (including the iframe)
24463 * @param {Roo.HtmlEditorCore} this
24468 * Fires when the editor is first receives the focus. Any insertion must wait
24469 * until after this event.
24470 * @param {Roo.HtmlEditorCore} this
24474 * @event beforesync
24475 * Fires before the textarea is updated with content from the editor iframe. Return false
24476 * to cancel the sync.
24477 * @param {Roo.HtmlEditorCore} this
24478 * @param {String} html
24482 * @event beforepush
24483 * Fires before the iframe editor is updated with content from the textarea. Return false
24484 * to cancel the push.
24485 * @param {Roo.HtmlEditorCore} this
24486 * @param {String} html
24491 * Fires when the textarea is updated with content from the editor iframe.
24492 * @param {Roo.HtmlEditorCore} this
24493 * @param {String} html
24498 * Fires when the iframe editor is updated with content from the textarea.
24499 * @param {Roo.HtmlEditorCore} this
24500 * @param {String} html
24505 * @event editorevent
24506 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24507 * @param {Roo.HtmlEditorCore} this
24513 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24515 // defaults : white / black...
24516 this.applyBlacklists();
24523 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24527 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24533 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24538 * @cfg {Number} height (in pixels)
24542 * @cfg {Number} width (in pixels)
24547 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24550 stylesheets: false,
24555 // private properties
24556 validationEvent : false,
24558 initialized : false,
24560 sourceEditMode : false,
24561 onFocus : Roo.emptyFn,
24563 hideMode:'offsets',
24567 // blacklist + whitelisted elements..
24574 * Protected method that will not generally be called directly. It
24575 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24576 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24578 getDocMarkup : function(){
24582 // inherit styels from page...??
24583 if (this.stylesheets === false) {
24585 Roo.get(document.head).select('style').each(function(node) {
24586 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24589 Roo.get(document.head).select('link').each(function(node) {
24590 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24593 } else if (!this.stylesheets.length) {
24595 st = '<style type="text/css">' +
24596 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24599 for (var i in this.stylesheets) {
24600 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24605 st += '<style type="text/css">' +
24606 'IMG { cursor: pointer } ' +
24609 var cls = 'roo-htmleditor-body';
24611 if(this.bodyCls.length){
24612 cls += ' ' + this.bodyCls;
24615 return '<html><head>' + st +
24616 //<style type="text/css">' +
24617 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24619 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24623 onRender : function(ct, position)
24626 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24627 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24630 this.el.dom.style.border = '0 none';
24631 this.el.dom.setAttribute('tabIndex', -1);
24632 this.el.addClass('x-hidden hide');
24636 if(Roo.isIE){ // fix IE 1px bogus margin
24637 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24641 this.frameId = Roo.id();
24645 var iframe = this.owner.wrap.createChild({
24647 cls: 'form-control', // bootstrap..
24649 name: this.frameId,
24650 frameBorder : 'no',
24651 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24656 this.iframe = iframe.dom;
24658 this.assignDocWin();
24660 this.doc.designMode = 'on';
24663 this.doc.write(this.getDocMarkup());
24667 var task = { // must defer to wait for browser to be ready
24669 //console.log("run task?" + this.doc.readyState);
24670 this.assignDocWin();
24671 if(this.doc.body || this.doc.readyState == 'complete'){
24673 this.doc.designMode="on";
24677 Roo.TaskMgr.stop(task);
24678 this.initEditor.defer(10, this);
24685 Roo.TaskMgr.start(task);
24690 onResize : function(w, h)
24692 Roo.log('resize: ' +w + ',' + h );
24693 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24697 if(typeof w == 'number'){
24699 this.iframe.style.width = w + 'px';
24701 if(typeof h == 'number'){
24703 this.iframe.style.height = h + 'px';
24705 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24712 * Toggles the editor between standard and source edit mode.
24713 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24715 toggleSourceEdit : function(sourceEditMode){
24717 this.sourceEditMode = sourceEditMode === true;
24719 if(this.sourceEditMode){
24721 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24724 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24725 //this.iframe.className = '';
24728 //this.setSize(this.owner.wrap.getSize());
24729 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24736 * Protected method that will not generally be called directly. If you need/want
24737 * custom HTML cleanup, this is the method you should override.
24738 * @param {String} html The HTML to be cleaned
24739 * return {String} The cleaned HTML
24741 cleanHtml : function(html){
24742 html = String(html);
24743 if(html.length > 5){
24744 if(Roo.isSafari){ // strip safari nonsense
24745 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24748 if(html == ' '){
24755 * HTML Editor -> Textarea
24756 * Protected method that will not generally be called directly. Syncs the contents
24757 * of the editor iframe with the textarea.
24759 syncValue : function(){
24760 if(this.initialized){
24761 var bd = (this.doc.body || this.doc.documentElement);
24762 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24763 var html = bd.innerHTML;
24765 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24766 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24768 html = '<div style="'+m[0]+'">' + html + '</div>';
24771 html = this.cleanHtml(html);
24772 // fix up the special chars.. normaly like back quotes in word...
24773 // however we do not want to do this with chinese..
24774 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24776 var cc = match.charCodeAt();
24778 // Get the character value, handling surrogate pairs
24779 if (match.length == 2) {
24780 // It's a surrogate pair, calculate the Unicode code point
24781 var high = match.charCodeAt(0) - 0xD800;
24782 var low = match.charCodeAt(1) - 0xDC00;
24783 cc = (high * 0x400) + low + 0x10000;
24785 (cc >= 0x4E00 && cc < 0xA000 ) ||
24786 (cc >= 0x3400 && cc < 0x4E00 ) ||
24787 (cc >= 0xf900 && cc < 0xfb00 )
24792 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24793 return "&#" + cc + ";";
24800 if(this.owner.fireEvent('beforesync', this, html) !== false){
24801 this.el.dom.value = html;
24802 this.owner.fireEvent('sync', this, html);
24808 * Protected method that will not generally be called directly. Pushes the value of the textarea
24809 * into the iframe editor.
24811 pushValue : function(){
24812 if(this.initialized){
24813 var v = this.el.dom.value.trim();
24815 // if(v.length < 1){
24819 if(this.owner.fireEvent('beforepush', this, v) !== false){
24820 var d = (this.doc.body || this.doc.documentElement);
24822 this.cleanUpPaste();
24823 this.el.dom.value = d.innerHTML;
24824 this.owner.fireEvent('push', this, v);
24830 deferFocus : function(){
24831 this.focus.defer(10, this);
24835 focus : function(){
24836 if(this.win && !this.sourceEditMode){
24843 assignDocWin: function()
24845 var iframe = this.iframe;
24848 this.doc = iframe.contentWindow.document;
24849 this.win = iframe.contentWindow;
24851 // if (!Roo.get(this.frameId)) {
24854 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24855 // this.win = Roo.get(this.frameId).dom.contentWindow;
24857 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24861 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24862 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24867 initEditor : function(){
24868 //console.log("INIT EDITOR");
24869 this.assignDocWin();
24873 this.doc.designMode="on";
24875 this.doc.write(this.getDocMarkup());
24878 var dbody = (this.doc.body || this.doc.documentElement);
24879 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24880 // this copies styles from the containing element into thsi one..
24881 // not sure why we need all of this..
24882 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24884 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24885 //ss['background-attachment'] = 'fixed'; // w3c
24886 dbody.bgProperties = 'fixed'; // ie
24887 //Roo.DomHelper.applyStyles(dbody, ss);
24888 Roo.EventManager.on(this.doc, {
24889 //'mousedown': this.onEditorEvent,
24890 'mouseup': this.onEditorEvent,
24891 'dblclick': this.onEditorEvent,
24892 'click': this.onEditorEvent,
24893 'keyup': this.onEditorEvent,
24898 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24900 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24901 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24903 this.initialized = true;
24905 this.owner.fireEvent('initialize', this);
24910 onDestroy : function(){
24916 //for (var i =0; i < this.toolbars.length;i++) {
24917 // // fixme - ask toolbars for heights?
24918 // this.toolbars[i].onDestroy();
24921 //this.wrap.dom.innerHTML = '';
24922 //this.wrap.remove();
24927 onFirstFocus : function(){
24929 this.assignDocWin();
24932 this.activated = true;
24935 if(Roo.isGecko){ // prevent silly gecko errors
24937 var s = this.win.getSelection();
24938 if(!s.focusNode || s.focusNode.nodeType != 3){
24939 var r = s.getRangeAt(0);
24940 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24945 this.execCmd('useCSS', true);
24946 this.execCmd('styleWithCSS', false);
24949 this.owner.fireEvent('activate', this);
24953 adjustFont: function(btn){
24954 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24955 //if(Roo.isSafari){ // safari
24958 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24959 if(Roo.isSafari){ // safari
24960 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24961 v = (v < 10) ? 10 : v;
24962 v = (v > 48) ? 48 : v;
24963 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24968 v = Math.max(1, v+adjust);
24970 this.execCmd('FontSize', v );
24973 onEditorEvent : function(e)
24975 this.owner.fireEvent('editorevent', this, e);
24976 // this.updateToolbar();
24977 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24980 insertTag : function(tg)
24982 // could be a bit smarter... -> wrap the current selected tRoo..
24983 if (tg.toLowerCase() == 'span' ||
24984 tg.toLowerCase() == 'code' ||
24985 tg.toLowerCase() == 'sup' ||
24986 tg.toLowerCase() == 'sub'
24989 range = this.createRange(this.getSelection());
24990 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24991 wrappingNode.appendChild(range.extractContents());
24992 range.insertNode(wrappingNode);
24999 this.execCmd("formatblock", tg);
25003 insertText : function(txt)
25007 var range = this.createRange();
25008 range.deleteContents();
25009 //alert(Sender.getAttribute('label'));
25011 range.insertNode(this.doc.createTextNode(txt));
25017 * Executes a Midas editor command on the editor document and performs necessary focus and
25018 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25019 * @param {String} cmd The Midas command
25020 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25022 relayCmd : function(cmd, value){
25024 this.execCmd(cmd, value);
25025 this.owner.fireEvent('editorevent', this);
25026 //this.updateToolbar();
25027 this.owner.deferFocus();
25031 * Executes a Midas editor command directly on the editor document.
25032 * For visual commands, you should use {@link #relayCmd} instead.
25033 * <b>This should only be called after the editor is initialized.</b>
25034 * @param {String} cmd The Midas command
25035 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25037 execCmd : function(cmd, value){
25038 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25045 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25047 * @param {String} text | dom node..
25049 insertAtCursor : function(text)
25052 if(!this.activated){
25058 var r = this.doc.selection.createRange();
25069 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25073 // from jquery ui (MIT licenced)
25075 var win = this.win;
25077 if (win.getSelection && win.getSelection().getRangeAt) {
25078 range = win.getSelection().getRangeAt(0);
25079 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25080 range.insertNode(node);
25081 } else if (win.document.selection && win.document.selection.createRange) {
25082 // no firefox support
25083 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25084 win.document.selection.createRange().pasteHTML(txt);
25086 // no firefox support
25087 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25088 this.execCmd('InsertHTML', txt);
25097 mozKeyPress : function(e){
25099 var c = e.getCharCode(), cmd;
25102 c = String.fromCharCode(c).toLowerCase();
25116 this.cleanUpPaste.defer(100, this);
25124 e.preventDefault();
25132 fixKeys : function(){ // load time branching for fastest keydown performance
25134 return function(e){
25135 var k = e.getKey(), r;
25138 r = this.doc.selection.createRange();
25141 r.pasteHTML('    ');
25148 r = this.doc.selection.createRange();
25150 var target = r.parentElement();
25151 if(!target || target.tagName.toLowerCase() != 'li'){
25153 r.pasteHTML('<br />');
25159 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25160 this.cleanUpPaste.defer(100, this);
25166 }else if(Roo.isOpera){
25167 return function(e){
25168 var k = e.getKey();
25172 this.execCmd('InsertHTML','    ');
25175 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25176 this.cleanUpPaste.defer(100, this);
25181 }else if(Roo.isSafari){
25182 return function(e){
25183 var k = e.getKey();
25187 this.execCmd('InsertText','\t');
25191 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25192 this.cleanUpPaste.defer(100, this);
25200 getAllAncestors: function()
25202 var p = this.getSelectedNode();
25205 a.push(p); // push blank onto stack..
25206 p = this.getParentElement();
25210 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25214 a.push(this.doc.body);
25218 lastSelNode : false,
25221 getSelection : function()
25223 this.assignDocWin();
25224 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25227 getSelectedNode: function()
25229 // this may only work on Gecko!!!
25231 // should we cache this!!!!
25236 var range = this.createRange(this.getSelection()).cloneRange();
25239 var parent = range.parentElement();
25241 var testRange = range.duplicate();
25242 testRange.moveToElementText(parent);
25243 if (testRange.inRange(range)) {
25246 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25249 parent = parent.parentElement;
25254 // is ancestor a text element.
25255 var ac = range.commonAncestorContainer;
25256 if (ac.nodeType == 3) {
25257 ac = ac.parentNode;
25260 var ar = ac.childNodes;
25263 var other_nodes = [];
25264 var has_other_nodes = false;
25265 for (var i=0;i<ar.length;i++) {
25266 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25269 // fullly contained node.
25271 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25276 // probably selected..
25277 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25278 other_nodes.push(ar[i]);
25282 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25287 has_other_nodes = true;
25289 if (!nodes.length && other_nodes.length) {
25290 nodes= other_nodes;
25292 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25298 createRange: function(sel)
25300 // this has strange effects when using with
25301 // top toolbar - not sure if it's a great idea.
25302 //this.editor.contentWindow.focus();
25303 if (typeof sel != "undefined") {
25305 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25307 return this.doc.createRange();
25310 return this.doc.createRange();
25313 getParentElement: function()
25316 this.assignDocWin();
25317 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25319 var range = this.createRange(sel);
25322 var p = range.commonAncestorContainer;
25323 while (p.nodeType == 3) { // text node
25334 * Range intersection.. the hard stuff...
25338 * [ -- selected range --- ]
25342 * if end is before start or hits it. fail.
25343 * if start is after end or hits it fail.
25345 * if either hits (but other is outside. - then it's not
25351 // @see http://www.thismuchiknow.co.uk/?p=64.
25352 rangeIntersectsNode : function(range, node)
25354 var nodeRange = node.ownerDocument.createRange();
25356 nodeRange.selectNode(node);
25358 nodeRange.selectNodeContents(node);
25361 var rangeStartRange = range.cloneRange();
25362 rangeStartRange.collapse(true);
25364 var rangeEndRange = range.cloneRange();
25365 rangeEndRange.collapse(false);
25367 var nodeStartRange = nodeRange.cloneRange();
25368 nodeStartRange.collapse(true);
25370 var nodeEndRange = nodeRange.cloneRange();
25371 nodeEndRange.collapse(false);
25373 return rangeStartRange.compareBoundaryPoints(
25374 Range.START_TO_START, nodeEndRange) == -1 &&
25375 rangeEndRange.compareBoundaryPoints(
25376 Range.START_TO_START, nodeStartRange) == 1;
25380 rangeCompareNode : function(range, node)
25382 var nodeRange = node.ownerDocument.createRange();
25384 nodeRange.selectNode(node);
25386 nodeRange.selectNodeContents(node);
25390 range.collapse(true);
25392 nodeRange.collapse(true);
25394 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25395 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25397 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25399 var nodeIsBefore = ss == 1;
25400 var nodeIsAfter = ee == -1;
25402 if (nodeIsBefore && nodeIsAfter) {
25405 if (!nodeIsBefore && nodeIsAfter) {
25406 return 1; //right trailed.
25409 if (nodeIsBefore && !nodeIsAfter) {
25410 return 2; // left trailed.
25416 // private? - in a new class?
25417 cleanUpPaste : function()
25419 // cleans up the whole document..
25420 Roo.log('cleanuppaste');
25422 this.cleanUpChildren(this.doc.body);
25423 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25424 if (clean != this.doc.body.innerHTML) {
25425 this.doc.body.innerHTML = clean;
25430 cleanWordChars : function(input) {// change the chars to hex code
25431 var he = Roo.HtmlEditorCore;
25433 var output = input;
25434 Roo.each(he.swapCodes, function(sw) {
25435 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25437 output = output.replace(swapper, sw[1]);
25444 cleanUpChildren : function (n)
25446 if (!n.childNodes.length) {
25449 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25450 this.cleanUpChild(n.childNodes[i]);
25457 cleanUpChild : function (node)
25460 //console.log(node);
25461 if (node.nodeName == "#text") {
25462 // clean up silly Windows -- stuff?
25465 if (node.nodeName == "#comment") {
25466 node.parentNode.removeChild(node);
25467 // clean up silly Windows -- stuff?
25470 var lcname = node.tagName.toLowerCase();
25471 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25472 // whitelist of tags..
25474 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25476 node.parentNode.removeChild(node);
25481 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25483 // spans with no attributes - just remove them..
25484 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25485 remove_keep_children = true;
25488 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25489 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25491 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25492 // remove_keep_children = true;
25495 if (remove_keep_children) {
25496 this.cleanUpChildren(node);
25497 // inserts everything just before this node...
25498 while (node.childNodes.length) {
25499 var cn = node.childNodes[0];
25500 node.removeChild(cn);
25501 node.parentNode.insertBefore(cn, node);
25503 node.parentNode.removeChild(node);
25507 if (!node.attributes || !node.attributes.length) {
25512 this.cleanUpChildren(node);
25516 function cleanAttr(n,v)
25519 if (v.match(/^\./) || v.match(/^\//)) {
25522 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25525 if (v.match(/^#/)) {
25528 if (v.match(/^\{/)) { // allow template editing.
25531 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25532 node.removeAttribute(n);
25536 var cwhite = this.cwhite;
25537 var cblack = this.cblack;
25539 function cleanStyle(n,v)
25541 if (v.match(/expression/)) { //XSS?? should we even bother..
25542 node.removeAttribute(n);
25546 var parts = v.split(/;/);
25549 Roo.each(parts, function(p) {
25550 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25554 var l = p.split(':').shift().replace(/\s+/g,'');
25555 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25557 if ( cwhite.length && cblack.indexOf(l) > -1) {
25558 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25559 //node.removeAttribute(n);
25563 // only allow 'c whitelisted system attributes'
25564 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25565 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25566 //node.removeAttribute(n);
25576 if (clean.length) {
25577 node.setAttribute(n, clean.join(';'));
25579 node.removeAttribute(n);
25585 for (var i = node.attributes.length-1; i > -1 ; i--) {
25586 var a = node.attributes[i];
25589 if (a.name.toLowerCase().substr(0,2)=='on') {
25590 node.removeAttribute(a.name);
25593 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25594 node.removeAttribute(a.name);
25597 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25598 cleanAttr(a.name,a.value); // fixme..
25601 if (a.name == 'style') {
25602 cleanStyle(a.name,a.value);
25605 /// clean up MS crap..
25606 // tecnically this should be a list of valid class'es..
25609 if (a.name == 'class') {
25610 if (a.value.match(/^Mso/)) {
25611 node.removeAttribute('class');
25614 if (a.value.match(/^body$/)) {
25615 node.removeAttribute('class');
25626 this.cleanUpChildren(node);
25632 * Clean up MS wordisms...
25634 cleanWord : function(node)
25637 this.cleanWord(this.doc.body);
25642 node.nodeName == 'SPAN' &&
25643 !node.hasAttributes() &&
25644 node.childNodes.length == 1 &&
25645 node.firstChild.nodeName == "#text"
25647 var textNode = node.firstChild;
25648 node.removeChild(textNode);
25649 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25650 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25652 node.parentNode.insertBefore(textNode, node);
25653 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25654 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25656 node.parentNode.removeChild(node);
25659 if (node.nodeName == "#text") {
25660 // clean up silly Windows -- stuff?
25663 if (node.nodeName == "#comment") {
25664 node.parentNode.removeChild(node);
25665 // clean up silly Windows -- stuff?
25669 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25670 node.parentNode.removeChild(node);
25673 //Roo.log(node.tagName);
25674 // remove - but keep children..
25675 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25676 //Roo.log('-- removed');
25677 while (node.childNodes.length) {
25678 var cn = node.childNodes[0];
25679 node.removeChild(cn);
25680 node.parentNode.insertBefore(cn, node);
25681 // move node to parent - and clean it..
25682 this.cleanWord(cn);
25684 node.parentNode.removeChild(node);
25685 /// no need to iterate chidlren = it's got none..
25686 //this.iterateChildren(node, this.cleanWord);
25690 if (node.className.length) {
25692 var cn = node.className.split(/\W+/);
25694 Roo.each(cn, function(cls) {
25695 if (cls.match(/Mso[a-zA-Z]+/)) {
25700 node.className = cna.length ? cna.join(' ') : '';
25702 node.removeAttribute("class");
25706 if (node.hasAttribute("lang")) {
25707 node.removeAttribute("lang");
25710 if (node.hasAttribute("style")) {
25712 var styles = node.getAttribute("style").split(";");
25714 Roo.each(styles, function(s) {
25715 if (!s.match(/:/)) {
25718 var kv = s.split(":");
25719 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25722 // what ever is left... we allow.
25725 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25726 if (!nstyle.length) {
25727 node.removeAttribute('style');
25730 this.iterateChildren(node, this.cleanWord);
25736 * iterateChildren of a Node, calling fn each time, using this as the scole..
25737 * @param {DomNode} node node to iterate children of.
25738 * @param {Function} fn method of this class to call on each item.
25740 iterateChildren : function(node, fn)
25742 if (!node.childNodes.length) {
25745 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25746 fn.call(this, node.childNodes[i])
25752 * cleanTableWidths.
25754 * Quite often pasting from word etc.. results in tables with column and widths.
25755 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25758 cleanTableWidths : function(node)
25763 this.cleanTableWidths(this.doc.body);
25768 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25771 Roo.log(node.tagName);
25772 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25773 this.iterateChildren(node, this.cleanTableWidths);
25776 if (node.hasAttribute('width')) {
25777 node.removeAttribute('width');
25781 if (node.hasAttribute("style")) {
25784 var styles = node.getAttribute("style").split(";");
25786 Roo.each(styles, function(s) {
25787 if (!s.match(/:/)) {
25790 var kv = s.split(":");
25791 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25794 // what ever is left... we allow.
25797 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25798 if (!nstyle.length) {
25799 node.removeAttribute('style');
25803 this.iterateChildren(node, this.cleanTableWidths);
25811 domToHTML : function(currentElement, depth, nopadtext) {
25813 depth = depth || 0;
25814 nopadtext = nopadtext || false;
25816 if (!currentElement) {
25817 return this.domToHTML(this.doc.body);
25820 //Roo.log(currentElement);
25822 var allText = false;
25823 var nodeName = currentElement.nodeName;
25824 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25826 if (nodeName == '#text') {
25828 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25833 if (nodeName != 'BODY') {
25836 // Prints the node tagName, such as <A>, <IMG>, etc
25839 for(i = 0; i < currentElement.attributes.length;i++) {
25841 var aname = currentElement.attributes.item(i).name;
25842 if (!currentElement.attributes.item(i).value.length) {
25845 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25848 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25857 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25860 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25865 // Traverse the tree
25867 var currentElementChild = currentElement.childNodes.item(i);
25868 var allText = true;
25869 var innerHTML = '';
25871 while (currentElementChild) {
25872 // Formatting code (indent the tree so it looks nice on the screen)
25873 var nopad = nopadtext;
25874 if (lastnode == 'SPAN') {
25878 if (currentElementChild.nodeName == '#text') {
25879 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25880 toadd = nopadtext ? toadd : toadd.trim();
25881 if (!nopad && toadd.length > 80) {
25882 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25884 innerHTML += toadd;
25887 currentElementChild = currentElement.childNodes.item(i);
25893 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25895 // Recursively traverse the tree structure of the child node
25896 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25897 lastnode = currentElementChild.nodeName;
25899 currentElementChild=currentElement.childNodes.item(i);
25905 // The remaining code is mostly for formatting the tree
25906 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25911 ret+= "</"+tagName+">";
25917 applyBlacklists : function()
25919 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25920 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25924 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25925 if (b.indexOf(tag) > -1) {
25928 this.white.push(tag);
25932 Roo.each(w, function(tag) {
25933 if (b.indexOf(tag) > -1) {
25936 if (this.white.indexOf(tag) > -1) {
25939 this.white.push(tag);
25944 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25945 if (w.indexOf(tag) > -1) {
25948 this.black.push(tag);
25952 Roo.each(b, function(tag) {
25953 if (w.indexOf(tag) > -1) {
25956 if (this.black.indexOf(tag) > -1) {
25959 this.black.push(tag);
25964 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25965 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25969 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25970 if (b.indexOf(tag) > -1) {
25973 this.cwhite.push(tag);
25977 Roo.each(w, function(tag) {
25978 if (b.indexOf(tag) > -1) {
25981 if (this.cwhite.indexOf(tag) > -1) {
25984 this.cwhite.push(tag);
25989 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25990 if (w.indexOf(tag) > -1) {
25993 this.cblack.push(tag);
25997 Roo.each(b, function(tag) {
25998 if (w.indexOf(tag) > -1) {
26001 if (this.cblack.indexOf(tag) > -1) {
26004 this.cblack.push(tag);
26009 setStylesheets : function(stylesheets)
26011 if(typeof(stylesheets) == 'string'){
26012 Roo.get(this.iframe.contentDocument.head).createChild({
26014 rel : 'stylesheet',
26023 Roo.each(stylesheets, function(s) {
26028 Roo.get(_this.iframe.contentDocument.head).createChild({
26030 rel : 'stylesheet',
26039 removeStylesheets : function()
26043 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26048 setStyle : function(style)
26050 Roo.get(this.iframe.contentDocument.head).createChild({
26059 // hide stuff that is not compatible
26073 * @event specialkey
26077 * @cfg {String} fieldClass @hide
26080 * @cfg {String} focusClass @hide
26083 * @cfg {String} autoCreate @hide
26086 * @cfg {String} inputType @hide
26089 * @cfg {String} invalidClass @hide
26092 * @cfg {String} invalidText @hide
26095 * @cfg {String} msgFx @hide
26098 * @cfg {String} validateOnBlur @hide
26102 Roo.HtmlEditorCore.white = [
26103 'area', 'br', 'img', 'input', 'hr', 'wbr',
26105 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26106 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26107 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26108 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26109 'table', 'ul', 'xmp',
26111 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26114 'dir', 'menu', 'ol', 'ul', 'dl',
26120 Roo.HtmlEditorCore.black = [
26121 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26123 'base', 'basefont', 'bgsound', 'blink', 'body',
26124 'frame', 'frameset', 'head', 'html', 'ilayer',
26125 'iframe', 'layer', 'link', 'meta', 'object',
26126 'script', 'style' ,'title', 'xml' // clean later..
26128 Roo.HtmlEditorCore.clean = [
26129 'script', 'style', 'title', 'xml'
26131 Roo.HtmlEditorCore.remove = [
26136 Roo.HtmlEditorCore.ablack = [
26140 Roo.HtmlEditorCore.aclean = [
26141 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26145 Roo.HtmlEditorCore.pwhite= [
26146 'http', 'https', 'mailto'
26149 // white listed style attributes.
26150 Roo.HtmlEditorCore.cwhite= [
26151 // 'text-align', /// default is to allow most things..
26157 // black listed style attributes.
26158 Roo.HtmlEditorCore.cblack= [
26159 // 'font-size' -- this can be set by the project
26163 Roo.HtmlEditorCore.swapCodes =[
26164 [ 8211, "–" ],
26165 [ 8212, "—" ],
26182 * @class Roo.bootstrap.HtmlEditor
26183 * @extends Roo.bootstrap.TextArea
26184 * Bootstrap HtmlEditor class
26187 * Create a new HtmlEditor
26188 * @param {Object} config The config object
26191 Roo.bootstrap.HtmlEditor = function(config){
26192 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26193 if (!this.toolbars) {
26194 this.toolbars = [];
26197 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26200 * @event initialize
26201 * Fires when the editor is fully initialized (including the iframe)
26202 * @param {HtmlEditor} this
26207 * Fires when the editor is first receives the focus. Any insertion must wait
26208 * until after this event.
26209 * @param {HtmlEditor} this
26213 * @event beforesync
26214 * Fires before the textarea is updated with content from the editor iframe. Return false
26215 * to cancel the sync.
26216 * @param {HtmlEditor} this
26217 * @param {String} html
26221 * @event beforepush
26222 * Fires before the iframe editor is updated with content from the textarea. Return false
26223 * to cancel the push.
26224 * @param {HtmlEditor} this
26225 * @param {String} html
26230 * Fires when the textarea is updated with content from the editor iframe.
26231 * @param {HtmlEditor} this
26232 * @param {String} html
26237 * Fires when the iframe editor is updated with content from the textarea.
26238 * @param {HtmlEditor} this
26239 * @param {String} html
26243 * @event editmodechange
26244 * Fires when the editor switches edit modes
26245 * @param {HtmlEditor} this
26246 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26248 editmodechange: true,
26250 * @event editorevent
26251 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26252 * @param {HtmlEditor} this
26256 * @event firstfocus
26257 * Fires when on first focus - needed by toolbars..
26258 * @param {HtmlEditor} this
26263 * Auto save the htmlEditor value as a file into Events
26264 * @param {HtmlEditor} this
26268 * @event savedpreview
26269 * preview the saved version of htmlEditor
26270 * @param {HtmlEditor} this
26277 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26281 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26286 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26291 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26296 * @cfg {Number} height (in pixels)
26300 * @cfg {Number} width (in pixels)
26305 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26308 stylesheets: false,
26313 // private properties
26314 validationEvent : false,
26316 initialized : false,
26319 onFocus : Roo.emptyFn,
26321 hideMode:'offsets',
26323 tbContainer : false,
26327 toolbarContainer :function() {
26328 return this.wrap.select('.x-html-editor-tb',true).first();
26332 * Protected method that will not generally be called directly. It
26333 * is called when the editor creates its toolbar. Override this method if you need to
26334 * add custom toolbar buttons.
26335 * @param {HtmlEditor} editor
26337 createToolbar : function(){
26338 Roo.log('renewing');
26339 Roo.log("create toolbars");
26341 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26342 this.toolbars[0].render(this.toolbarContainer());
26346 // if (!editor.toolbars || !editor.toolbars.length) {
26347 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26350 // for (var i =0 ; i < editor.toolbars.length;i++) {
26351 // editor.toolbars[i] = Roo.factory(
26352 // typeof(editor.toolbars[i]) == 'string' ?
26353 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26354 // Roo.bootstrap.HtmlEditor);
26355 // editor.toolbars[i].init(editor);
26361 onRender : function(ct, position)
26363 // Roo.log("Call onRender: " + this.xtype);
26365 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26367 this.wrap = this.inputEl().wrap({
26368 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26371 this.editorcore.onRender(ct, position);
26373 if (this.resizable) {
26374 this.resizeEl = new Roo.Resizable(this.wrap, {
26378 minHeight : this.height,
26379 height: this.height,
26380 handles : this.resizable,
26383 resize : function(r, w, h) {
26384 _t.onResize(w,h); // -something
26390 this.createToolbar(this);
26393 if(!this.width && this.resizable){
26394 this.setSize(this.wrap.getSize());
26396 if (this.resizeEl) {
26397 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26398 // should trigger onReize..
26404 onResize : function(w, h)
26406 Roo.log('resize: ' +w + ',' + h );
26407 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26411 if(this.inputEl() ){
26412 if(typeof w == 'number'){
26413 var aw = w - this.wrap.getFrameWidth('lr');
26414 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26417 if(typeof h == 'number'){
26418 var tbh = -11; // fixme it needs to tool bar size!
26419 for (var i =0; i < this.toolbars.length;i++) {
26420 // fixme - ask toolbars for heights?
26421 tbh += this.toolbars[i].el.getHeight();
26422 //if (this.toolbars[i].footer) {
26423 // tbh += this.toolbars[i].footer.el.getHeight();
26431 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26432 ah -= 5; // knock a few pixes off for look..
26433 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26437 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26438 this.editorcore.onResize(ew,eh);
26443 * Toggles the editor between standard and source edit mode.
26444 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26446 toggleSourceEdit : function(sourceEditMode)
26448 this.editorcore.toggleSourceEdit(sourceEditMode);
26450 if(this.editorcore.sourceEditMode){
26451 Roo.log('editor - showing textarea');
26454 // Roo.log(this.syncValue());
26456 this.inputEl().removeClass(['hide', 'x-hidden']);
26457 this.inputEl().dom.removeAttribute('tabIndex');
26458 this.inputEl().focus();
26460 Roo.log('editor - hiding textarea');
26462 // Roo.log(this.pushValue());
26465 this.inputEl().addClass(['hide', 'x-hidden']);
26466 this.inputEl().dom.setAttribute('tabIndex', -1);
26467 //this.deferFocus();
26470 if(this.resizable){
26471 this.setSize(this.wrap.getSize());
26474 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26477 // private (for BoxComponent)
26478 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26480 // private (for BoxComponent)
26481 getResizeEl : function(){
26485 // private (for BoxComponent)
26486 getPositionEl : function(){
26491 initEvents : function(){
26492 this.originalValue = this.getValue();
26496 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26499 // markInvalid : Roo.emptyFn,
26501 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26504 // clearInvalid : Roo.emptyFn,
26506 setValue : function(v){
26507 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26508 this.editorcore.pushValue();
26513 deferFocus : function(){
26514 this.focus.defer(10, this);
26518 focus : function(){
26519 this.editorcore.focus();
26525 onDestroy : function(){
26531 for (var i =0; i < this.toolbars.length;i++) {
26532 // fixme - ask toolbars for heights?
26533 this.toolbars[i].onDestroy();
26536 this.wrap.dom.innerHTML = '';
26537 this.wrap.remove();
26542 onFirstFocus : function(){
26543 //Roo.log("onFirstFocus");
26544 this.editorcore.onFirstFocus();
26545 for (var i =0; i < this.toolbars.length;i++) {
26546 this.toolbars[i].onFirstFocus();
26552 syncValue : function()
26554 this.editorcore.syncValue();
26557 pushValue : function()
26559 this.editorcore.pushValue();
26563 // hide stuff that is not compatible
26577 * @event specialkey
26581 * @cfg {String} fieldClass @hide
26584 * @cfg {String} focusClass @hide
26587 * @cfg {String} autoCreate @hide
26590 * @cfg {String} inputType @hide
26594 * @cfg {String} invalidText @hide
26597 * @cfg {String} msgFx @hide
26600 * @cfg {String} validateOnBlur @hide
26609 Roo.namespace('Roo.bootstrap.htmleditor');
26611 * @class Roo.bootstrap.HtmlEditorToolbar1
26617 new Roo.bootstrap.HtmlEditor({
26620 new Roo.bootstrap.HtmlEditorToolbar1({
26621 disable : { fonts: 1 , format: 1, ..., ... , ...],
26627 * @cfg {Object} disable List of elements to disable..
26628 * @cfg {Array} btns List of additional buttons.
26632 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26635 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26638 Roo.apply(this, config);
26640 // default disabled, based on 'good practice'..
26641 this.disable = this.disable || {};
26642 Roo.applyIf(this.disable, {
26645 specialElements : true
26647 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26649 this.editor = config.editor;
26650 this.editorcore = config.editor.editorcore;
26652 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26654 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26655 // dont call parent... till later.
26657 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26662 editorcore : false,
26667 "h1","h2","h3","h4","h5","h6",
26669 "abbr", "acronym", "address", "cite", "samp", "var",
26673 onRender : function(ct, position)
26675 // Roo.log("Call onRender: " + this.xtype);
26677 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26679 this.el.dom.style.marginBottom = '0';
26681 var editorcore = this.editorcore;
26682 var editor= this.editor;
26685 var btn = function(id,cmd , toggle, handler, html){
26687 var event = toggle ? 'toggle' : 'click';
26692 xns: Roo.bootstrap,
26696 enableToggle:toggle !== false,
26698 pressed : toggle ? false : null,
26701 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26702 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26708 // var cb_box = function...
26713 xns: Roo.bootstrap,
26718 xns: Roo.bootstrap,
26722 Roo.each(this.formats, function(f) {
26723 style.menu.items.push({
26725 xns: Roo.bootstrap,
26726 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26731 editorcore.insertTag(this.tagname);
26738 children.push(style);
26740 btn('bold',false,true);
26741 btn('italic',false,true);
26742 btn('align-left', 'justifyleft',true);
26743 btn('align-center', 'justifycenter',true);
26744 btn('align-right' , 'justifyright',true);
26745 btn('link', false, false, function(btn) {
26746 //Roo.log("create link?");
26747 var url = prompt(this.createLinkText, this.defaultLinkValue);
26748 if(url && url != 'http:/'+'/'){
26749 this.editorcore.relayCmd('createlink', url);
26752 btn('list','insertunorderedlist',true);
26753 btn('pencil', false,true, function(btn){
26755 this.toggleSourceEdit(btn.pressed);
26758 if (this.editor.btns.length > 0) {
26759 for (var i = 0; i<this.editor.btns.length; i++) {
26760 children.push(this.editor.btns[i]);
26768 xns: Roo.bootstrap,
26773 xns: Roo.bootstrap,
26778 cog.menu.items.push({
26780 xns: Roo.bootstrap,
26781 html : Clean styles,
26786 editorcore.insertTag(this.tagname);
26795 this.xtype = 'NavSimplebar';
26797 for(var i=0;i< children.length;i++) {
26799 this.buttons.add(this.addxtypeChild(children[i]));
26803 editor.on('editorevent', this.updateToolbar, this);
26805 onBtnClick : function(id)
26807 this.editorcore.relayCmd(id);
26808 this.editorcore.focus();
26812 * Protected method that will not generally be called directly. It triggers
26813 * a toolbar update by reading the markup state of the current selection in the editor.
26815 updateToolbar: function(){
26817 if(!this.editorcore.activated){
26818 this.editor.onFirstFocus(); // is this neeed?
26822 var btns = this.buttons;
26823 var doc = this.editorcore.doc;
26824 btns.get('bold').setActive(doc.queryCommandState('bold'));
26825 btns.get('italic').setActive(doc.queryCommandState('italic'));
26826 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26828 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26829 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26830 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26832 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26833 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26836 var ans = this.editorcore.getAllAncestors();
26837 if (this.formatCombo) {
26840 var store = this.formatCombo.store;
26841 this.formatCombo.setValue("");
26842 for (var i =0; i < ans.length;i++) {
26843 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26845 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26853 // hides menus... - so this cant be on a menu...
26854 Roo.bootstrap.MenuMgr.hideAll();
26856 Roo.bootstrap.MenuMgr.hideAll();
26857 //this.editorsyncValue();
26859 onFirstFocus: function() {
26860 this.buttons.each(function(item){
26864 toggleSourceEdit : function(sourceEditMode){
26867 if(sourceEditMode){
26868 Roo.log("disabling buttons");
26869 this.buttons.each( function(item){
26870 if(item.cmd != 'pencil'){
26876 Roo.log("enabling buttons");
26877 if(this.editorcore.initialized){
26878 this.buttons.each( function(item){
26884 Roo.log("calling toggole on editor");
26885 // tell the editor that it's been pressed..
26886 this.editor.toggleSourceEdit(sourceEditMode);
26900 * @class Roo.bootstrap.Markdown
26901 * @extends Roo.bootstrap.TextArea
26902 * Bootstrap Showdown editable area
26903 * @cfg {string} content
26906 * Create a new Showdown
26909 Roo.bootstrap.Markdown = function(config){
26910 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26914 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26918 initEvents : function()
26921 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26922 this.markdownEl = this.el.createChild({
26923 cls : 'roo-markdown-area'
26925 this.inputEl().addClass('d-none');
26926 if (this.getValue() == '') {
26927 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26930 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26932 this.markdownEl.on('click', this.toggleTextEdit, this);
26933 this.on('blur', this.toggleTextEdit, this);
26934 this.on('specialkey', this.resizeTextArea, this);
26937 toggleTextEdit : function()
26939 var sh = this.markdownEl.getHeight();
26940 this.inputEl().addClass('d-none');
26941 this.markdownEl.addClass('d-none');
26942 if (!this.editing) {
26944 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26945 this.inputEl().removeClass('d-none');
26946 this.inputEl().focus();
26947 this.editing = true;
26950 // show showdown...
26951 this.updateMarkdown();
26952 this.markdownEl.removeClass('d-none');
26953 this.editing = false;
26956 updateMarkdown : function()
26958 if (this.getValue() == '') {
26959 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26963 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26966 resizeTextArea: function () {
26969 Roo.log([sh, this.getValue().split("\n").length * 30]);
26970 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26972 setValue : function(val)
26974 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26975 if (!this.editing) {
26976 this.updateMarkdown();
26982 if (!this.editing) {
26983 this.toggleTextEdit();
26991 * @class Roo.bootstrap.Table.AbstractSelectionModel
26992 * @extends Roo.util.Observable
26993 * Abstract base class for grid SelectionModels. It provides the interface that should be
26994 * implemented by descendant classes. This class should not be directly instantiated.
26997 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26998 this.locked = false;
26999 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27003 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27004 /** @ignore Called by the grid automatically. Do not call directly. */
27005 init : function(grid){
27011 * Locks the selections.
27014 this.locked = true;
27018 * Unlocks the selections.
27020 unlock : function(){
27021 this.locked = false;
27025 * Returns true if the selections are locked.
27026 * @return {Boolean}
27028 isLocked : function(){
27029 return this.locked;
27033 initEvents : function ()
27039 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27040 * @class Roo.bootstrap.Table.RowSelectionModel
27041 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27042 * It supports multiple selections and keyboard selection/navigation.
27044 * @param {Object} config
27047 Roo.bootstrap.Table.RowSelectionModel = function(config){
27048 Roo.apply(this, config);
27049 this.selections = new Roo.util.MixedCollection(false, function(o){
27054 this.lastActive = false;
27058 * @event selectionchange
27059 * Fires when the selection changes
27060 * @param {SelectionModel} this
27062 "selectionchange" : true,
27064 * @event afterselectionchange
27065 * Fires after the selection changes (eg. by key press or clicking)
27066 * @param {SelectionModel} this
27068 "afterselectionchange" : true,
27070 * @event beforerowselect
27071 * Fires when a row is selected being selected, return false to cancel.
27072 * @param {SelectionModel} this
27073 * @param {Number} rowIndex The selected index
27074 * @param {Boolean} keepExisting False if other selections will be cleared
27076 "beforerowselect" : true,
27079 * Fires when a row is selected.
27080 * @param {SelectionModel} this
27081 * @param {Number} rowIndex The selected index
27082 * @param {Roo.data.Record} r The record
27084 "rowselect" : true,
27086 * @event rowdeselect
27087 * Fires when a row is deselected.
27088 * @param {SelectionModel} this
27089 * @param {Number} rowIndex The selected index
27091 "rowdeselect" : true
27093 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27094 this.locked = false;
27097 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27099 * @cfg {Boolean} singleSelect
27100 * True to allow selection of only one row at a time (defaults to false)
27102 singleSelect : false,
27105 initEvents : function()
27108 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27109 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27110 //}else{ // allow click to work like normal
27111 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27113 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27114 this.grid.on("rowclick", this.handleMouseDown, this);
27116 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27117 "up" : function(e){
27119 this.selectPrevious(e.shiftKey);
27120 }else if(this.last !== false && this.lastActive !== false){
27121 var last = this.last;
27122 this.selectRange(this.last, this.lastActive-1);
27123 this.grid.getView().focusRow(this.lastActive);
27124 if(last !== false){
27128 this.selectFirstRow();
27130 this.fireEvent("afterselectionchange", this);
27132 "down" : function(e){
27134 this.selectNext(e.shiftKey);
27135 }else if(this.last !== false && this.lastActive !== false){
27136 var last = this.last;
27137 this.selectRange(this.last, this.lastActive+1);
27138 this.grid.getView().focusRow(this.lastActive);
27139 if(last !== false){
27143 this.selectFirstRow();
27145 this.fireEvent("afterselectionchange", this);
27149 this.grid.store.on('load', function(){
27150 this.selections.clear();
27153 var view = this.grid.view;
27154 view.on("refresh", this.onRefresh, this);
27155 view.on("rowupdated", this.onRowUpdated, this);
27156 view.on("rowremoved", this.onRemove, this);
27161 onRefresh : function()
27163 var ds = this.grid.store, i, v = this.grid.view;
27164 var s = this.selections;
27165 s.each(function(r){
27166 if((i = ds.indexOfId(r.id)) != -1){
27175 onRemove : function(v, index, r){
27176 this.selections.remove(r);
27180 onRowUpdated : function(v, index, r){
27181 if(this.isSelected(r)){
27182 v.onRowSelect(index);
27188 * @param {Array} records The records to select
27189 * @param {Boolean} keepExisting (optional) True to keep existing selections
27191 selectRecords : function(records, keepExisting)
27194 this.clearSelections();
27196 var ds = this.grid.store;
27197 for(var i = 0, len = records.length; i < len; i++){
27198 this.selectRow(ds.indexOf(records[i]), true);
27203 * Gets the number of selected rows.
27206 getCount : function(){
27207 return this.selections.length;
27211 * Selects the first row in the grid.
27213 selectFirstRow : function(){
27218 * Select the last row.
27219 * @param {Boolean} keepExisting (optional) True to keep existing selections
27221 selectLastRow : function(keepExisting){
27222 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27223 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27227 * Selects the row immediately following the last selected row.
27228 * @param {Boolean} keepExisting (optional) True to keep existing selections
27230 selectNext : function(keepExisting)
27232 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27233 this.selectRow(this.last+1, keepExisting);
27234 this.grid.getView().focusRow(this.last);
27239 * Selects the row that precedes the last selected row.
27240 * @param {Boolean} keepExisting (optional) True to keep existing selections
27242 selectPrevious : function(keepExisting){
27244 this.selectRow(this.last-1, keepExisting);
27245 this.grid.getView().focusRow(this.last);
27250 * Returns the selected records
27251 * @return {Array} Array of selected records
27253 getSelections : function(){
27254 return [].concat(this.selections.items);
27258 * Returns the first selected record.
27261 getSelected : function(){
27262 return this.selections.itemAt(0);
27267 * Clears all selections.
27269 clearSelections : function(fast)
27275 var ds = this.grid.store;
27276 var s = this.selections;
27277 s.each(function(r){
27278 this.deselectRow(ds.indexOfId(r.id));
27282 this.selections.clear();
27289 * Selects all rows.
27291 selectAll : function(){
27295 this.selections.clear();
27296 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27297 this.selectRow(i, true);
27302 * Returns True if there is a selection.
27303 * @return {Boolean}
27305 hasSelection : function(){
27306 return this.selections.length > 0;
27310 * Returns True if the specified row is selected.
27311 * @param {Number/Record} record The record or index of the record to check
27312 * @return {Boolean}
27314 isSelected : function(index){
27315 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27316 return (r && this.selections.key(r.id) ? true : false);
27320 * Returns True if the specified record id is selected.
27321 * @param {String} id The id of record to check
27322 * @return {Boolean}
27324 isIdSelected : function(id){
27325 return (this.selections.key(id) ? true : false);
27330 handleMouseDBClick : function(e, t){
27334 handleMouseDown : function(e, t)
27336 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27337 if(this.isLocked() || rowIndex < 0 ){
27340 if(e.shiftKey && this.last !== false){
27341 var last = this.last;
27342 this.selectRange(last, rowIndex, e.ctrlKey);
27343 this.last = last; // reset the last
27347 var isSelected = this.isSelected(rowIndex);
27348 //Roo.log("select row:" + rowIndex);
27350 this.deselectRow(rowIndex);
27352 this.selectRow(rowIndex, true);
27356 if(e.button !== 0 && isSelected){
27357 alert('rowIndex 2: ' + rowIndex);
27358 view.focusRow(rowIndex);
27359 }else if(e.ctrlKey && isSelected){
27360 this.deselectRow(rowIndex);
27361 }else if(!isSelected){
27362 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27363 view.focusRow(rowIndex);
27367 this.fireEvent("afterselectionchange", this);
27370 handleDragableRowClick : function(grid, rowIndex, e)
27372 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27373 this.selectRow(rowIndex, false);
27374 grid.view.focusRow(rowIndex);
27375 this.fireEvent("afterselectionchange", this);
27380 * Selects multiple rows.
27381 * @param {Array} rows Array of the indexes of the row to select
27382 * @param {Boolean} keepExisting (optional) True to keep existing selections
27384 selectRows : function(rows, keepExisting){
27386 this.clearSelections();
27388 for(var i = 0, len = rows.length; i < len; i++){
27389 this.selectRow(rows[i], true);
27394 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27395 * @param {Number} startRow The index of the first row in the range
27396 * @param {Number} endRow The index of the last row in the range
27397 * @param {Boolean} keepExisting (optional) True to retain existing selections
27399 selectRange : function(startRow, endRow, keepExisting){
27404 this.clearSelections();
27406 if(startRow <= endRow){
27407 for(var i = startRow; i <= endRow; i++){
27408 this.selectRow(i, true);
27411 for(var i = startRow; i >= endRow; i--){
27412 this.selectRow(i, true);
27418 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27419 * @param {Number} startRow The index of the first row in the range
27420 * @param {Number} endRow The index of the last row in the range
27422 deselectRange : function(startRow, endRow, preventViewNotify){
27426 for(var i = startRow; i <= endRow; i++){
27427 this.deselectRow(i, preventViewNotify);
27433 * @param {Number} row The index of the row to select
27434 * @param {Boolean} keepExisting (optional) True to keep existing selections
27436 selectRow : function(index, keepExisting, preventViewNotify)
27438 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27441 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27442 if(!keepExisting || this.singleSelect){
27443 this.clearSelections();
27446 var r = this.grid.store.getAt(index);
27447 //console.log('selectRow - record id :' + r.id);
27449 this.selections.add(r);
27450 this.last = this.lastActive = index;
27451 if(!preventViewNotify){
27452 var proxy = new Roo.Element(
27453 this.grid.getRowDom(index)
27455 proxy.addClass('bg-info info');
27457 this.fireEvent("rowselect", this, index, r);
27458 this.fireEvent("selectionchange", this);
27464 * @param {Number} row The index of the row to deselect
27466 deselectRow : function(index, preventViewNotify)
27471 if(this.last == index){
27474 if(this.lastActive == index){
27475 this.lastActive = false;
27478 var r = this.grid.store.getAt(index);
27483 this.selections.remove(r);
27484 //.console.log('deselectRow - record id :' + r.id);
27485 if(!preventViewNotify){
27487 var proxy = new Roo.Element(
27488 this.grid.getRowDom(index)
27490 proxy.removeClass('bg-info info');
27492 this.fireEvent("rowdeselect", this, index);
27493 this.fireEvent("selectionchange", this);
27497 restoreLast : function(){
27499 this.last = this._last;
27504 acceptsNav : function(row, col, cm){
27505 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27509 onEditorKey : function(field, e){
27510 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27515 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27517 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27519 }else if(k == e.ENTER && !e.ctrlKey){
27523 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27525 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27527 }else if(k == e.ESC){
27531 g.startEditing(newCell[0], newCell[1]);
27537 * Ext JS Library 1.1.1
27538 * Copyright(c) 2006-2007, Ext JS, LLC.
27540 * Originally Released Under LGPL - original licence link has changed is not relivant.
27543 * <script type="text/javascript">
27547 * @class Roo.bootstrap.PagingToolbar
27548 * @extends Roo.bootstrap.NavSimplebar
27549 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27551 * Create a new PagingToolbar
27552 * @param {Object} config The config object
27553 * @param {Roo.data.Store} store
27555 Roo.bootstrap.PagingToolbar = function(config)
27557 // old args format still supported... - xtype is prefered..
27558 // created from xtype...
27560 this.ds = config.dataSource;
27562 if (config.store && !this.ds) {
27563 this.store= Roo.factory(config.store, Roo.data);
27564 this.ds = this.store;
27565 this.ds.xmodule = this.xmodule || false;
27568 this.toolbarItems = [];
27569 if (config.items) {
27570 this.toolbarItems = config.items;
27573 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27578 this.bind(this.ds);
27581 if (Roo.bootstrap.version == 4) {
27582 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27584 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27589 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27591 * @cfg {Roo.data.Store} dataSource
27592 * The underlying data store providing the paged data
27595 * @cfg {String/HTMLElement/Element} container
27596 * container The id or element that will contain the toolbar
27599 * @cfg {Boolean} displayInfo
27600 * True to display the displayMsg (defaults to false)
27603 * @cfg {Number} pageSize
27604 * The number of records to display per page (defaults to 20)
27608 * @cfg {String} displayMsg
27609 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27611 displayMsg : 'Displaying {0} - {1} of {2}',
27613 * @cfg {String} emptyMsg
27614 * The message to display when no records are found (defaults to "No data to display")
27616 emptyMsg : 'No data to display',
27618 * Customizable piece of the default paging text (defaults to "Page")
27621 beforePageText : "Page",
27623 * Customizable piece of the default paging text (defaults to "of %0")
27626 afterPageText : "of {0}",
27628 * Customizable piece of the default paging text (defaults to "First Page")
27631 firstText : "First Page",
27633 * Customizable piece of the default paging text (defaults to "Previous Page")
27636 prevText : "Previous Page",
27638 * Customizable piece of the default paging text (defaults to "Next Page")
27641 nextText : "Next Page",
27643 * Customizable piece of the default paging text (defaults to "Last Page")
27646 lastText : "Last Page",
27648 * Customizable piece of the default paging text (defaults to "Refresh")
27651 refreshText : "Refresh",
27655 onRender : function(ct, position)
27657 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27658 this.navgroup.parentId = this.id;
27659 this.navgroup.onRender(this.el, null);
27660 // add the buttons to the navgroup
27662 if(this.displayInfo){
27663 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27664 this.displayEl = this.el.select('.x-paging-info', true).first();
27665 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27666 // this.displayEl = navel.el.select('span',true).first();
27672 Roo.each(_this.buttons, function(e){ // this might need to use render????
27673 Roo.factory(e).render(_this.el);
27677 Roo.each(_this.toolbarItems, function(e) {
27678 _this.navgroup.addItem(e);
27682 this.first = this.navgroup.addItem({
27683 tooltip: this.firstText,
27684 cls: "prev btn-outline-secondary",
27685 html : ' <i class="fa fa-step-backward"></i>',
27687 preventDefault: true,
27688 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27691 this.prev = this.navgroup.addItem({
27692 tooltip: this.prevText,
27693 cls: "prev btn-outline-secondary",
27694 html : ' <i class="fa fa-backward"></i>',
27696 preventDefault: true,
27697 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27699 //this.addSeparator();
27702 var field = this.navgroup.addItem( {
27704 cls : 'x-paging-position btn-outline-secondary',
27706 html : this.beforePageText +
27707 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27708 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27711 this.field = field.el.select('input', true).first();
27712 this.field.on("keydown", this.onPagingKeydown, this);
27713 this.field.on("focus", function(){this.dom.select();});
27716 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27717 //this.field.setHeight(18);
27718 //this.addSeparator();
27719 this.next = this.navgroup.addItem({
27720 tooltip: this.nextText,
27721 cls: "next btn-outline-secondary",
27722 html : ' <i class="fa fa-forward"></i>',
27724 preventDefault: true,
27725 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27727 this.last = this.navgroup.addItem({
27728 tooltip: this.lastText,
27729 html : ' <i class="fa fa-step-forward"></i>',
27730 cls: "next btn-outline-secondary",
27732 preventDefault: true,
27733 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27735 //this.addSeparator();
27736 this.loading = this.navgroup.addItem({
27737 tooltip: this.refreshText,
27738 cls: "btn-outline-secondary",
27739 html : ' <i class="fa fa-refresh"></i>',
27740 preventDefault: true,
27741 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27747 updateInfo : function(){
27748 if(this.displayEl){
27749 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27750 var msg = count == 0 ?
27754 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27756 this.displayEl.update(msg);
27761 onLoad : function(ds, r, o)
27763 this.cursor = o.params && o.params.start ? o.params.start : 0;
27765 var d = this.getPageData(),
27770 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27771 this.field.dom.value = ap;
27772 this.first.setDisabled(ap == 1);
27773 this.prev.setDisabled(ap == 1);
27774 this.next.setDisabled(ap == ps);
27775 this.last.setDisabled(ap == ps);
27776 this.loading.enable();
27781 getPageData : function(){
27782 var total = this.ds.getTotalCount();
27785 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27786 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27791 onLoadError : function(){
27792 this.loading.enable();
27796 onPagingKeydown : function(e){
27797 var k = e.getKey();
27798 var d = this.getPageData();
27800 var v = this.field.dom.value, pageNum;
27801 if(!v || isNaN(pageNum = parseInt(v, 10))){
27802 this.field.dom.value = d.activePage;
27805 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27806 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27809 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))
27811 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27812 this.field.dom.value = pageNum;
27813 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27816 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27818 var v = this.field.dom.value, pageNum;
27819 var increment = (e.shiftKey) ? 10 : 1;
27820 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27823 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27824 this.field.dom.value = d.activePage;
27827 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27829 this.field.dom.value = parseInt(v, 10) + increment;
27830 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27831 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27838 beforeLoad : function(){
27840 this.loading.disable();
27845 onClick : function(which){
27854 ds.load({params:{start: 0, limit: this.pageSize}});
27857 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27860 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27863 var total = ds.getTotalCount();
27864 var extra = total % this.pageSize;
27865 var lastStart = extra ? (total - extra) : total-this.pageSize;
27866 ds.load({params:{start: lastStart, limit: this.pageSize}});
27869 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27875 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27876 * @param {Roo.data.Store} store The data store to unbind
27878 unbind : function(ds){
27879 ds.un("beforeload", this.beforeLoad, this);
27880 ds.un("load", this.onLoad, this);
27881 ds.un("loadexception", this.onLoadError, this);
27882 ds.un("remove", this.updateInfo, this);
27883 ds.un("add", this.updateInfo, this);
27884 this.ds = undefined;
27888 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27889 * @param {Roo.data.Store} store The data store to bind
27891 bind : function(ds){
27892 ds.on("beforeload", this.beforeLoad, this);
27893 ds.on("load", this.onLoad, this);
27894 ds.on("loadexception", this.onLoadError, this);
27895 ds.on("remove", this.updateInfo, this);
27896 ds.on("add", this.updateInfo, this);
27907 * @class Roo.bootstrap.MessageBar
27908 * @extends Roo.bootstrap.Component
27909 * Bootstrap MessageBar class
27910 * @cfg {String} html contents of the MessageBar
27911 * @cfg {String} weight (info | success | warning | danger) default info
27912 * @cfg {String} beforeClass insert the bar before the given class
27913 * @cfg {Boolean} closable (true | false) default false
27914 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27917 * Create a new Element
27918 * @param {Object} config The config object
27921 Roo.bootstrap.MessageBar = function(config){
27922 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27925 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27931 beforeClass: 'bootstrap-sticky-wrap',
27933 getAutoCreate : function(){
27937 cls: 'alert alert-dismissable alert-' + this.weight,
27942 html: this.html || ''
27948 cfg.cls += ' alert-messages-fixed';
27962 onRender : function(ct, position)
27964 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27967 var cfg = Roo.apply({}, this.getAutoCreate());
27971 cfg.cls += ' ' + this.cls;
27974 cfg.style = this.style;
27976 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27978 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27981 this.el.select('>button.close').on('click', this.hide, this);
27987 if (!this.rendered) {
27993 this.fireEvent('show', this);
27999 if (!this.rendered) {
28005 this.fireEvent('hide', this);
28008 update : function()
28010 // var e = this.el.dom.firstChild;
28012 // if(this.closable){
28013 // e = e.nextSibling;
28016 // e.data = this.html || '';
28018 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28034 * @class Roo.bootstrap.Graph
28035 * @extends Roo.bootstrap.Component
28036 * Bootstrap Graph class
28040 @cfg {String} graphtype bar | vbar | pie
28041 @cfg {number} g_x coodinator | centre x (pie)
28042 @cfg {number} g_y coodinator | centre y (pie)
28043 @cfg {number} g_r radius (pie)
28044 @cfg {number} g_height height of the chart (respected by all elements in the set)
28045 @cfg {number} g_width width of the chart (respected by all elements in the set)
28046 @cfg {Object} title The title of the chart
28049 -opts (object) options for the chart
28051 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28052 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28054 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.
28055 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28057 o stretch (boolean)
28059 -opts (object) options for the pie
28062 o startAngle (number)
28063 o endAngle (number)
28067 * Create a new Input
28068 * @param {Object} config The config object
28071 Roo.bootstrap.Graph = function(config){
28072 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28078 * The img click event for the img.
28079 * @param {Roo.EventObject} e
28085 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28096 //g_colors: this.colors,
28103 getAutoCreate : function(){
28114 onRender : function(ct,position){
28117 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28119 if (typeof(Raphael) == 'undefined') {
28120 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28124 this.raphael = Raphael(this.el.dom);
28126 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28127 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28128 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28129 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28131 r.text(160, 10, "Single Series Chart").attr(txtattr);
28132 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28133 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28134 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28136 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28137 r.barchart(330, 10, 300, 220, data1);
28138 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28139 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28142 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28143 // r.barchart(30, 30, 560, 250, xdata, {
28144 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28145 // axis : "0 0 1 1",
28146 // axisxlabels : xdata
28147 // //yvalues : cols,
28150 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28152 // this.load(null,xdata,{
28153 // axis : "0 0 1 1",
28154 // axisxlabels : xdata
28159 load : function(graphtype,xdata,opts)
28161 this.raphael.clear();
28163 graphtype = this.graphtype;
28168 var r = this.raphael,
28169 fin = function () {
28170 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28172 fout = function () {
28173 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28175 pfin = function() {
28176 this.sector.stop();
28177 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28180 this.label[0].stop();
28181 this.label[0].attr({ r: 7.5 });
28182 this.label[1].attr({ "font-weight": 800 });
28185 pfout = function() {
28186 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28189 this.label[0].animate({ r: 5 }, 500, "bounce");
28190 this.label[1].attr({ "font-weight": 400 });
28196 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28199 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28202 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28203 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28205 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28212 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28217 setTitle: function(o)
28222 initEvents: function() {
28225 this.el.on('click', this.onClick, this);
28229 onClick : function(e)
28231 Roo.log('img onclick');
28232 this.fireEvent('click', this, e);
28244 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28247 * @class Roo.bootstrap.dash.NumberBox
28248 * @extends Roo.bootstrap.Component
28249 * Bootstrap NumberBox class
28250 * @cfg {String} headline Box headline
28251 * @cfg {String} content Box content
28252 * @cfg {String} icon Box icon
28253 * @cfg {String} footer Footer text
28254 * @cfg {String} fhref Footer href
28257 * Create a new NumberBox
28258 * @param {Object} config The config object
28262 Roo.bootstrap.dash.NumberBox = function(config){
28263 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28267 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28276 getAutoCreate : function(){
28280 cls : 'small-box ',
28288 cls : 'roo-headline',
28289 html : this.headline
28293 cls : 'roo-content',
28294 html : this.content
28308 cls : 'ion ' + this.icon
28317 cls : 'small-box-footer',
28318 href : this.fhref || '#',
28322 cfg.cn.push(footer);
28329 onRender : function(ct,position){
28330 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28337 setHeadline: function (value)
28339 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28342 setFooter: function (value, href)
28344 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28347 this.el.select('a.small-box-footer',true).first().attr('href', href);
28352 setContent: function (value)
28354 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28357 initEvents: function()
28371 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28374 * @class Roo.bootstrap.dash.TabBox
28375 * @extends Roo.bootstrap.Component
28376 * Bootstrap TabBox class
28377 * @cfg {String} title Title of the TabBox
28378 * @cfg {String} icon Icon of the TabBox
28379 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28380 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28383 * Create a new TabBox
28384 * @param {Object} config The config object
28388 Roo.bootstrap.dash.TabBox = function(config){
28389 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28394 * When a pane is added
28395 * @param {Roo.bootstrap.dash.TabPane} pane
28399 * @event activatepane
28400 * When a pane is activated
28401 * @param {Roo.bootstrap.dash.TabPane} pane
28403 "activatepane" : true
28411 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28416 tabScrollable : false,
28418 getChildContainer : function()
28420 return this.el.select('.tab-content', true).first();
28423 getAutoCreate : function(){
28427 cls: 'pull-left header',
28435 cls: 'fa ' + this.icon
28441 cls: 'nav nav-tabs pull-right',
28447 if(this.tabScrollable){
28454 cls: 'nav nav-tabs pull-right',
28465 cls: 'nav-tabs-custom',
28470 cls: 'tab-content no-padding',
28478 initEvents : function()
28480 //Roo.log('add add pane handler');
28481 this.on('addpane', this.onAddPane, this);
28484 * Updates the box title
28485 * @param {String} html to set the title to.
28487 setTitle : function(value)
28489 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28491 onAddPane : function(pane)
28493 this.panes.push(pane);
28494 //Roo.log('addpane');
28496 // tabs are rendere left to right..
28497 if(!this.showtabs){
28501 var ctr = this.el.select('.nav-tabs', true).first();
28504 var existing = ctr.select('.nav-tab',true);
28505 var qty = existing.getCount();;
28508 var tab = ctr.createChild({
28510 cls : 'nav-tab' + (qty ? '' : ' active'),
28518 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28521 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28523 pane.el.addClass('active');
28528 onTabClick : function(ev,un,ob,pane)
28530 //Roo.log('tab - prev default');
28531 ev.preventDefault();
28534 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28535 pane.tab.addClass('active');
28536 //Roo.log(pane.title);
28537 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28538 // technically we should have a deactivate event.. but maybe add later.
28539 // and it should not de-activate the selected tab...
28540 this.fireEvent('activatepane', pane);
28541 pane.el.addClass('active');
28542 pane.fireEvent('activate');
28547 getActivePane : function()
28550 Roo.each(this.panes, function(p) {
28551 if(p.el.hasClass('active')){
28572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28574 * @class Roo.bootstrap.TabPane
28575 * @extends Roo.bootstrap.Component
28576 * Bootstrap TabPane class
28577 * @cfg {Boolean} active (false | true) Default false
28578 * @cfg {String} title title of panel
28582 * Create a new TabPane
28583 * @param {Object} config The config object
28586 Roo.bootstrap.dash.TabPane = function(config){
28587 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28593 * When a pane is activated
28594 * @param {Roo.bootstrap.dash.TabPane} pane
28601 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28606 // the tabBox that this is attached to.
28609 getAutoCreate : function()
28617 cfg.cls += ' active';
28622 initEvents : function()
28624 //Roo.log('trigger add pane handler');
28625 this.parent().fireEvent('addpane', this)
28629 * Updates the tab title
28630 * @param {String} html to set the title to.
28632 setTitle: function(str)
28638 this.tab.select('a', true).first().dom.innerHTML = str;
28655 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28658 * @class Roo.bootstrap.menu.Menu
28659 * @extends Roo.bootstrap.Component
28660 * Bootstrap Menu class - container for Menu
28661 * @cfg {String} html Text of the menu
28662 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28663 * @cfg {String} icon Font awesome icon
28664 * @cfg {String} pos Menu align to (top | bottom) default bottom
28668 * Create a new Menu
28669 * @param {Object} config The config object
28673 Roo.bootstrap.menu.Menu = function(config){
28674 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28678 * @event beforeshow
28679 * Fires before this menu is displayed
28680 * @param {Roo.bootstrap.menu.Menu} this
28684 * @event beforehide
28685 * Fires before this menu is hidden
28686 * @param {Roo.bootstrap.menu.Menu} this
28691 * Fires after this menu is displayed
28692 * @param {Roo.bootstrap.menu.Menu} this
28697 * Fires after this menu is hidden
28698 * @param {Roo.bootstrap.menu.Menu} this
28703 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28704 * @param {Roo.bootstrap.menu.Menu} this
28705 * @param {Roo.EventObject} e
28712 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28716 weight : 'default',
28721 getChildContainer : function() {
28722 if(this.isSubMenu){
28726 return this.el.select('ul.dropdown-menu', true).first();
28729 getAutoCreate : function()
28734 cls : 'roo-menu-text',
28742 cls : 'fa ' + this.icon
28753 cls : 'dropdown-button btn btn-' + this.weight,
28758 cls : 'dropdown-toggle btn btn-' + this.weight,
28768 cls : 'dropdown-menu'
28774 if(this.pos == 'top'){
28775 cfg.cls += ' dropup';
28778 if(this.isSubMenu){
28781 cls : 'dropdown-menu'
28788 onRender : function(ct, position)
28790 this.isSubMenu = ct.hasClass('dropdown-submenu');
28792 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28795 initEvents : function()
28797 if(this.isSubMenu){
28801 this.hidden = true;
28803 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28804 this.triggerEl.on('click', this.onTriggerPress, this);
28806 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28807 this.buttonEl.on('click', this.onClick, this);
28813 if(this.isSubMenu){
28817 return this.el.select('ul.dropdown-menu', true).first();
28820 onClick : function(e)
28822 this.fireEvent("click", this, e);
28825 onTriggerPress : function(e)
28827 if (this.isVisible()) {
28834 isVisible : function(){
28835 return !this.hidden;
28840 this.fireEvent("beforeshow", this);
28842 this.hidden = false;
28843 this.el.addClass('open');
28845 Roo.get(document).on("mouseup", this.onMouseUp, this);
28847 this.fireEvent("show", this);
28854 this.fireEvent("beforehide", this);
28856 this.hidden = true;
28857 this.el.removeClass('open');
28859 Roo.get(document).un("mouseup", this.onMouseUp);
28861 this.fireEvent("hide", this);
28864 onMouseUp : function()
28878 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28881 * @class Roo.bootstrap.menu.Item
28882 * @extends Roo.bootstrap.Component
28883 * Bootstrap MenuItem class
28884 * @cfg {Boolean} submenu (true | false) default false
28885 * @cfg {String} html text of the item
28886 * @cfg {String} href the link
28887 * @cfg {Boolean} disable (true | false) default false
28888 * @cfg {Boolean} preventDefault (true | false) default true
28889 * @cfg {String} icon Font awesome icon
28890 * @cfg {String} pos Submenu align to (left | right) default right
28894 * Create a new Item
28895 * @param {Object} config The config object
28899 Roo.bootstrap.menu.Item = function(config){
28900 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28904 * Fires when the mouse is hovering over this menu
28905 * @param {Roo.bootstrap.menu.Item} this
28906 * @param {Roo.EventObject} e
28911 * Fires when the mouse exits this menu
28912 * @param {Roo.bootstrap.menu.Item} this
28913 * @param {Roo.EventObject} e
28919 * The raw click event for the entire grid.
28920 * @param {Roo.EventObject} e
28926 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28931 preventDefault: true,
28936 getAutoCreate : function()
28941 cls : 'roo-menu-item-text',
28949 cls : 'fa ' + this.icon
28958 href : this.href || '#',
28965 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28969 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28971 if(this.pos == 'left'){
28972 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28979 initEvents : function()
28981 this.el.on('mouseover', this.onMouseOver, this);
28982 this.el.on('mouseout', this.onMouseOut, this);
28984 this.el.select('a', true).first().on('click', this.onClick, this);
28988 onClick : function(e)
28990 if(this.preventDefault){
28991 e.preventDefault();
28994 this.fireEvent("click", this, e);
28997 onMouseOver : function(e)
28999 if(this.submenu && this.pos == 'left'){
29000 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29003 this.fireEvent("mouseover", this, e);
29006 onMouseOut : function(e)
29008 this.fireEvent("mouseout", this, e);
29020 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29023 * @class Roo.bootstrap.menu.Separator
29024 * @extends Roo.bootstrap.Component
29025 * Bootstrap Separator class
29028 * Create a new Separator
29029 * @param {Object} config The config object
29033 Roo.bootstrap.menu.Separator = function(config){
29034 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29037 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29039 getAutoCreate : function(){
29042 cls: 'dropdown-divider divider'
29060 * @class Roo.bootstrap.Tooltip
29061 * Bootstrap Tooltip class
29062 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29063 * to determine which dom element triggers the tooltip.
29065 * It needs to add support for additional attributes like tooltip-position
29068 * Create a new Toolti
29069 * @param {Object} config The config object
29072 Roo.bootstrap.Tooltip = function(config){
29073 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29075 this.alignment = Roo.bootstrap.Tooltip.alignment;
29077 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29078 this.alignment = config.alignment;
29083 Roo.apply(Roo.bootstrap.Tooltip, {
29085 * @function init initialize tooltip monitoring.
29089 currentTip : false,
29090 currentRegion : false,
29096 Roo.get(document).on('mouseover', this.enter ,this);
29097 Roo.get(document).on('mouseout', this.leave, this);
29100 this.currentTip = new Roo.bootstrap.Tooltip();
29103 enter : function(ev)
29105 var dom = ev.getTarget();
29107 //Roo.log(['enter',dom]);
29108 var el = Roo.fly(dom);
29109 if (this.currentEl) {
29111 //Roo.log(this.currentEl);
29112 //Roo.log(this.currentEl.contains(dom));
29113 if (this.currentEl == el) {
29116 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29122 if (this.currentTip.el) {
29123 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29127 if(!el || el.dom == document){
29133 if (!el.attr('tooltip')) {
29134 pel = el.findParent("[tooltip]");
29136 bindEl = Roo.get(pel);
29142 // you can not look for children, as if el is the body.. then everythign is the child..
29143 if (!pel && !el.attr('tooltip')) { //
29144 if (!el.select("[tooltip]").elements.length) {
29147 // is the mouse over this child...?
29148 bindEl = el.select("[tooltip]").first();
29149 var xy = ev.getXY();
29150 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29151 //Roo.log("not in region.");
29154 //Roo.log("child element over..");
29157 this.currentEl = el;
29158 this.currentTip.bind(bindEl);
29159 this.currentRegion = Roo.lib.Region.getRegion(dom);
29160 this.currentTip.enter();
29163 leave : function(ev)
29165 var dom = ev.getTarget();
29166 //Roo.log(['leave',dom]);
29167 if (!this.currentEl) {
29172 if (dom != this.currentEl.dom) {
29175 var xy = ev.getXY();
29176 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29179 // only activate leave if mouse cursor is outside... bounding box..
29184 if (this.currentTip) {
29185 this.currentTip.leave();
29187 //Roo.log('clear currentEl');
29188 this.currentEl = false;
29193 'left' : ['r-l', [-2,0], 'right'],
29194 'right' : ['l-r', [2,0], 'left'],
29195 'bottom' : ['t-b', [0,2], 'top'],
29196 'top' : [ 'b-t', [0,-2], 'bottom']
29202 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29207 delay : null, // can be { show : 300 , hide: 500}
29211 hoverState : null, //???
29213 placement : 'bottom',
29217 getAutoCreate : function(){
29224 cls : 'tooltip-arrow arrow'
29227 cls : 'tooltip-inner'
29234 bind : function(el)
29239 initEvents : function()
29241 this.arrowEl = this.el.select('.arrow', true).first();
29242 this.innerEl = this.el.select('.tooltip-inner', true).first();
29245 enter : function () {
29247 if (this.timeout != null) {
29248 clearTimeout(this.timeout);
29251 this.hoverState = 'in';
29252 //Roo.log("enter - show");
29253 if (!this.delay || !this.delay.show) {
29258 this.timeout = setTimeout(function () {
29259 if (_t.hoverState == 'in') {
29262 }, this.delay.show);
29266 clearTimeout(this.timeout);
29268 this.hoverState = 'out';
29269 if (!this.delay || !this.delay.hide) {
29275 this.timeout = setTimeout(function () {
29276 //Roo.log("leave - timeout");
29278 if (_t.hoverState == 'out') {
29280 Roo.bootstrap.Tooltip.currentEl = false;
29285 show : function (msg)
29288 this.render(document.body);
29291 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29293 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29295 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29297 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29298 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29300 var placement = typeof this.placement == 'function' ?
29301 this.placement.call(this, this.el, on_el) :
29304 var autoToken = /\s?auto?\s?/i;
29305 var autoPlace = autoToken.test(placement);
29307 placement = placement.replace(autoToken, '') || 'top';
29311 //this.el.setXY([0,0]);
29313 //this.el.dom.style.display='block';
29315 //this.el.appendTo(on_el);
29317 var p = this.getPosition();
29318 var box = this.el.getBox();
29324 var align = this.alignment[placement];
29326 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29328 if(placement == 'top' || placement == 'bottom'){
29330 placement = 'right';
29333 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29334 placement = 'left';
29337 var scroll = Roo.select('body', true).first().getScroll();
29339 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29343 align = this.alignment[placement];
29345 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29349 var elems = document.getElementsByTagName('div');
29350 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29351 for (var i = 0; i < elems.length; i++) {
29352 var zindex = Number.parseInt(
29353 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29356 if (zindex > highest) {
29363 this.el.dom.style.zIndex = highest;
29365 this.el.alignTo(this.bindEl, align[0],align[1]);
29366 //var arrow = this.el.select('.arrow',true).first();
29367 //arrow.set(align[2],
29369 this.el.addClass(placement);
29370 this.el.addClass("bs-tooltip-"+ placement);
29372 this.el.addClass('in fade show');
29374 this.hoverState = null;
29376 if (this.el.hasClass('fade')) {
29391 //this.el.setXY([0,0]);
29392 this.el.removeClass(['show', 'in']);
29408 * @class Roo.bootstrap.LocationPicker
29409 * @extends Roo.bootstrap.Component
29410 * Bootstrap LocationPicker class
29411 * @cfg {Number} latitude Position when init default 0
29412 * @cfg {Number} longitude Position when init default 0
29413 * @cfg {Number} zoom default 15
29414 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29415 * @cfg {Boolean} mapTypeControl default false
29416 * @cfg {Boolean} disableDoubleClickZoom default false
29417 * @cfg {Boolean} scrollwheel default true
29418 * @cfg {Boolean} streetViewControl default false
29419 * @cfg {Number} radius default 0
29420 * @cfg {String} locationName
29421 * @cfg {Boolean} draggable default true
29422 * @cfg {Boolean} enableAutocomplete default false
29423 * @cfg {Boolean} enableReverseGeocode default true
29424 * @cfg {String} markerTitle
29427 * Create a new LocationPicker
29428 * @param {Object} config The config object
29432 Roo.bootstrap.LocationPicker = function(config){
29434 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29439 * Fires when the picker initialized.
29440 * @param {Roo.bootstrap.LocationPicker} this
29441 * @param {Google Location} location
29445 * @event positionchanged
29446 * Fires when the picker position changed.
29447 * @param {Roo.bootstrap.LocationPicker} this
29448 * @param {Google Location} location
29450 positionchanged : true,
29453 * Fires when the map resize.
29454 * @param {Roo.bootstrap.LocationPicker} this
29459 * Fires when the map show.
29460 * @param {Roo.bootstrap.LocationPicker} this
29465 * Fires when the map hide.
29466 * @param {Roo.bootstrap.LocationPicker} this
29471 * Fires when click the map.
29472 * @param {Roo.bootstrap.LocationPicker} this
29473 * @param {Map event} e
29477 * @event mapRightClick
29478 * Fires when right click the map.
29479 * @param {Roo.bootstrap.LocationPicker} this
29480 * @param {Map event} e
29482 mapRightClick : true,
29484 * @event markerClick
29485 * Fires when click the marker.
29486 * @param {Roo.bootstrap.LocationPicker} this
29487 * @param {Map event} e
29489 markerClick : true,
29491 * @event markerRightClick
29492 * Fires when right click the marker.
29493 * @param {Roo.bootstrap.LocationPicker} this
29494 * @param {Map event} e
29496 markerRightClick : true,
29498 * @event OverlayViewDraw
29499 * Fires when OverlayView Draw
29500 * @param {Roo.bootstrap.LocationPicker} this
29502 OverlayViewDraw : true,
29504 * @event OverlayViewOnAdd
29505 * Fires when OverlayView Draw
29506 * @param {Roo.bootstrap.LocationPicker} this
29508 OverlayViewOnAdd : true,
29510 * @event OverlayViewOnRemove
29511 * Fires when OverlayView Draw
29512 * @param {Roo.bootstrap.LocationPicker} this
29514 OverlayViewOnRemove : true,
29516 * @event OverlayViewShow
29517 * Fires when OverlayView Draw
29518 * @param {Roo.bootstrap.LocationPicker} this
29519 * @param {Pixel} cpx
29521 OverlayViewShow : true,
29523 * @event OverlayViewHide
29524 * Fires when OverlayView Draw
29525 * @param {Roo.bootstrap.LocationPicker} this
29527 OverlayViewHide : true,
29529 * @event loadexception
29530 * Fires when load google lib failed.
29531 * @param {Roo.bootstrap.LocationPicker} this
29533 loadexception : true
29538 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29540 gMapContext: false,
29546 mapTypeControl: false,
29547 disableDoubleClickZoom: false,
29549 streetViewControl: false,
29553 enableAutocomplete: false,
29554 enableReverseGeocode: true,
29557 getAutoCreate: function()
29562 cls: 'roo-location-picker'
29568 initEvents: function(ct, position)
29570 if(!this.el.getWidth() || this.isApplied()){
29574 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29579 initial: function()
29581 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29582 this.fireEvent('loadexception', this);
29586 if(!this.mapTypeId){
29587 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29590 this.gMapContext = this.GMapContext();
29592 this.initOverlayView();
29594 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29598 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29599 _this.setPosition(_this.gMapContext.marker.position);
29602 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29603 _this.fireEvent('mapClick', this, event);
29607 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29608 _this.fireEvent('mapRightClick', this, event);
29612 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29613 _this.fireEvent('markerClick', this, event);
29617 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29618 _this.fireEvent('markerRightClick', this, event);
29622 this.setPosition(this.gMapContext.location);
29624 this.fireEvent('initial', this, this.gMapContext.location);
29627 initOverlayView: function()
29631 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29635 _this.fireEvent('OverlayViewDraw', _this);
29640 _this.fireEvent('OverlayViewOnAdd', _this);
29643 onRemove: function()
29645 _this.fireEvent('OverlayViewOnRemove', _this);
29648 show: function(cpx)
29650 _this.fireEvent('OverlayViewShow', _this, cpx);
29655 _this.fireEvent('OverlayViewHide', _this);
29661 fromLatLngToContainerPixel: function(event)
29663 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29666 isApplied: function()
29668 return this.getGmapContext() == false ? false : true;
29671 getGmapContext: function()
29673 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29676 GMapContext: function()
29678 var position = new google.maps.LatLng(this.latitude, this.longitude);
29680 var _map = new google.maps.Map(this.el.dom, {
29683 mapTypeId: this.mapTypeId,
29684 mapTypeControl: this.mapTypeControl,
29685 disableDoubleClickZoom: this.disableDoubleClickZoom,
29686 scrollwheel: this.scrollwheel,
29687 streetViewControl: this.streetViewControl,
29688 locationName: this.locationName,
29689 draggable: this.draggable,
29690 enableAutocomplete: this.enableAutocomplete,
29691 enableReverseGeocode: this.enableReverseGeocode
29694 var _marker = new google.maps.Marker({
29695 position: position,
29697 title: this.markerTitle,
29698 draggable: this.draggable
29705 location: position,
29706 radius: this.radius,
29707 locationName: this.locationName,
29708 addressComponents: {
29709 formatted_address: null,
29710 addressLine1: null,
29711 addressLine2: null,
29713 streetNumber: null,
29717 stateOrProvince: null
29720 domContainer: this.el.dom,
29721 geodecoder: new google.maps.Geocoder()
29725 drawCircle: function(center, radius, options)
29727 if (this.gMapContext.circle != null) {
29728 this.gMapContext.circle.setMap(null);
29732 options = Roo.apply({}, options, {
29733 strokeColor: "#0000FF",
29734 strokeOpacity: .35,
29736 fillColor: "#0000FF",
29740 options.map = this.gMapContext.map;
29741 options.radius = radius;
29742 options.center = center;
29743 this.gMapContext.circle = new google.maps.Circle(options);
29744 return this.gMapContext.circle;
29750 setPosition: function(location)
29752 this.gMapContext.location = location;
29753 this.gMapContext.marker.setPosition(location);
29754 this.gMapContext.map.panTo(location);
29755 this.drawCircle(location, this.gMapContext.radius, {});
29759 if (this.gMapContext.settings.enableReverseGeocode) {
29760 this.gMapContext.geodecoder.geocode({
29761 latLng: this.gMapContext.location
29762 }, function(results, status) {
29764 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29765 _this.gMapContext.locationName = results[0].formatted_address;
29766 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29768 _this.fireEvent('positionchanged', this, location);
29775 this.fireEvent('positionchanged', this, location);
29780 google.maps.event.trigger(this.gMapContext.map, "resize");
29782 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29784 this.fireEvent('resize', this);
29787 setPositionByLatLng: function(latitude, longitude)
29789 this.setPosition(new google.maps.LatLng(latitude, longitude));
29792 getCurrentPosition: function()
29795 latitude: this.gMapContext.location.lat(),
29796 longitude: this.gMapContext.location.lng()
29800 getAddressName: function()
29802 return this.gMapContext.locationName;
29805 getAddressComponents: function()
29807 return this.gMapContext.addressComponents;
29810 address_component_from_google_geocode: function(address_components)
29814 for (var i = 0; i < address_components.length; i++) {
29815 var component = address_components[i];
29816 if (component.types.indexOf("postal_code") >= 0) {
29817 result.postalCode = component.short_name;
29818 } else if (component.types.indexOf("street_number") >= 0) {
29819 result.streetNumber = component.short_name;
29820 } else if (component.types.indexOf("route") >= 0) {
29821 result.streetName = component.short_name;
29822 } else if (component.types.indexOf("neighborhood") >= 0) {
29823 result.city = component.short_name;
29824 } else if (component.types.indexOf("locality") >= 0) {
29825 result.city = component.short_name;
29826 } else if (component.types.indexOf("sublocality") >= 0) {
29827 result.district = component.short_name;
29828 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29829 result.stateOrProvince = component.short_name;
29830 } else if (component.types.indexOf("country") >= 0) {
29831 result.country = component.short_name;
29835 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29836 result.addressLine2 = "";
29840 setZoomLevel: function(zoom)
29842 this.gMapContext.map.setZoom(zoom);
29855 this.fireEvent('show', this);
29866 this.fireEvent('hide', this);
29871 Roo.apply(Roo.bootstrap.LocationPicker, {
29873 OverlayView : function(map, options)
29875 options = options || {};
29882 * @class Roo.bootstrap.Alert
29883 * @extends Roo.bootstrap.Component
29884 * Bootstrap Alert class - shows an alert area box
29886 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29887 Enter a valid email address
29890 * @cfg {String} title The title of alert
29891 * @cfg {String} html The content of alert
29892 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29893 * @cfg {String} fa font-awesomeicon
29894 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29895 * @cfg {Boolean} close true to show a x closer
29899 * Create a new alert
29900 * @param {Object} config The config object
29904 Roo.bootstrap.Alert = function(config){
29905 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29909 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29915 faicon: false, // BC
29919 getAutoCreate : function()
29931 style : this.close ? '' : 'display:none'
29935 cls : 'roo-alert-icon'
29940 cls : 'roo-alert-title',
29945 cls : 'roo-alert-text',
29952 cfg.cn[0].cls += ' fa ' + this.faicon;
29955 cfg.cn[0].cls += ' fa ' + this.fa;
29959 cfg.cls += ' alert-' + this.weight;
29965 initEvents: function()
29967 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29968 this.titleEl = this.el.select('.roo-alert-title',true).first();
29969 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29970 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29971 if (this.seconds > 0) {
29972 this.hide.defer(this.seconds, this);
29976 * Set the Title Message HTML
29977 * @param {String} html
29979 setTitle : function(str)
29981 this.titleEl.dom.innerHTML = str;
29985 * Set the Body Message HTML
29986 * @param {String} html
29988 setHtml : function(str)
29990 this.htmlEl.dom.innerHTML = str;
29993 * Set the Weight of the alert
29994 * @param {String} (success|info|warning|danger) weight
29997 setWeight : function(weight)
30000 this.el.removeClass('alert-' + this.weight);
30003 this.weight = weight;
30005 this.el.addClass('alert-' + this.weight);
30008 * Set the Icon of the alert
30009 * @param {String} see fontawsome names (name without the 'fa-' bit)
30011 setIcon : function(icon)
30014 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30017 this.faicon = icon;
30019 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30044 * @class Roo.bootstrap.UploadCropbox
30045 * @extends Roo.bootstrap.Component
30046 * Bootstrap UploadCropbox class
30047 * @cfg {String} emptyText show when image has been loaded
30048 * @cfg {String} rotateNotify show when image too small to rotate
30049 * @cfg {Number} errorTimeout default 3000
30050 * @cfg {Number} minWidth default 300
30051 * @cfg {Number} minHeight default 300
30052 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30053 * @cfg {Boolean} isDocument (true|false) default false
30054 * @cfg {String} url action url
30055 * @cfg {String} paramName default 'imageUpload'
30056 * @cfg {String} method default POST
30057 * @cfg {Boolean} loadMask (true|false) default true
30058 * @cfg {Boolean} loadingText default 'Loading...'
30061 * Create a new UploadCropbox
30062 * @param {Object} config The config object
30065 Roo.bootstrap.UploadCropbox = function(config){
30066 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30070 * @event beforeselectfile
30071 * Fire before select file
30072 * @param {Roo.bootstrap.UploadCropbox} this
30074 "beforeselectfile" : true,
30077 * Fire after initEvent
30078 * @param {Roo.bootstrap.UploadCropbox} this
30083 * Fire after initEvent
30084 * @param {Roo.bootstrap.UploadCropbox} this
30085 * @param {String} data
30090 * Fire when preparing the file data
30091 * @param {Roo.bootstrap.UploadCropbox} this
30092 * @param {Object} file
30097 * Fire when get exception
30098 * @param {Roo.bootstrap.UploadCropbox} this
30099 * @param {XMLHttpRequest} xhr
30101 "exception" : true,
30103 * @event beforeloadcanvas
30104 * Fire before load the canvas
30105 * @param {Roo.bootstrap.UploadCropbox} this
30106 * @param {String} src
30108 "beforeloadcanvas" : true,
30111 * Fire when trash image
30112 * @param {Roo.bootstrap.UploadCropbox} this
30117 * Fire when download the image
30118 * @param {Roo.bootstrap.UploadCropbox} this
30122 * @event footerbuttonclick
30123 * Fire when footerbuttonclick
30124 * @param {Roo.bootstrap.UploadCropbox} this
30125 * @param {String} type
30127 "footerbuttonclick" : true,
30131 * @param {Roo.bootstrap.UploadCropbox} this
30136 * Fire when rotate the image
30137 * @param {Roo.bootstrap.UploadCropbox} this
30138 * @param {String} pos
30143 * Fire when inspect the file
30144 * @param {Roo.bootstrap.UploadCropbox} this
30145 * @param {Object} file
30150 * Fire when xhr upload the file
30151 * @param {Roo.bootstrap.UploadCropbox} this
30152 * @param {Object} data
30157 * Fire when arrange the file data
30158 * @param {Roo.bootstrap.UploadCropbox} this
30159 * @param {Object} formData
30164 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30167 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30169 emptyText : 'Click to upload image',
30170 rotateNotify : 'Image is too small to rotate',
30171 errorTimeout : 3000,
30185 cropType : 'image/jpeg',
30187 canvasLoaded : false,
30188 isDocument : false,
30190 paramName : 'imageUpload',
30192 loadingText : 'Loading...',
30195 getAutoCreate : function()
30199 cls : 'roo-upload-cropbox',
30203 cls : 'roo-upload-cropbox-selector',
30208 cls : 'roo-upload-cropbox-body',
30209 style : 'cursor:pointer',
30213 cls : 'roo-upload-cropbox-preview'
30217 cls : 'roo-upload-cropbox-thumb'
30221 cls : 'roo-upload-cropbox-empty-notify',
30222 html : this.emptyText
30226 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30227 html : this.rotateNotify
30233 cls : 'roo-upload-cropbox-footer',
30236 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30246 onRender : function(ct, position)
30248 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30250 if (this.buttons.length) {
30252 Roo.each(this.buttons, function(bb) {
30254 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30256 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30262 this.maskEl = this.el;
30266 initEvents : function()
30268 this.urlAPI = (window.createObjectURL && window) ||
30269 (window.URL && URL.revokeObjectURL && URL) ||
30270 (window.webkitURL && webkitURL);
30272 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30273 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30275 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30276 this.selectorEl.hide();
30278 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30279 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30281 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30282 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30283 this.thumbEl.hide();
30285 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30286 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30288 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30289 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30290 this.errorEl.hide();
30292 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30293 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30294 this.footerEl.hide();
30296 this.setThumbBoxSize();
30302 this.fireEvent('initial', this);
30309 window.addEventListener("resize", function() { _this.resize(); } );
30311 this.bodyEl.on('click', this.beforeSelectFile, this);
30314 this.bodyEl.on('touchstart', this.onTouchStart, this);
30315 this.bodyEl.on('touchmove', this.onTouchMove, this);
30316 this.bodyEl.on('touchend', this.onTouchEnd, this);
30320 this.bodyEl.on('mousedown', this.onMouseDown, this);
30321 this.bodyEl.on('mousemove', this.onMouseMove, this);
30322 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30323 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30324 Roo.get(document).on('mouseup', this.onMouseUp, this);
30327 this.selectorEl.on('change', this.onFileSelected, this);
30333 this.baseScale = 1;
30335 this.baseRotate = 1;
30336 this.dragable = false;
30337 this.pinching = false;
30340 this.cropData = false;
30341 this.notifyEl.dom.innerHTML = this.emptyText;
30343 this.selectorEl.dom.value = '';
30347 resize : function()
30349 if(this.fireEvent('resize', this) != false){
30350 this.setThumbBoxPosition();
30351 this.setCanvasPosition();
30355 onFooterButtonClick : function(e, el, o, type)
30358 case 'rotate-left' :
30359 this.onRotateLeft(e);
30361 case 'rotate-right' :
30362 this.onRotateRight(e);
30365 this.beforeSelectFile(e);
30380 this.fireEvent('footerbuttonclick', this, type);
30383 beforeSelectFile : function(e)
30385 e.preventDefault();
30387 if(this.fireEvent('beforeselectfile', this) != false){
30388 this.selectorEl.dom.click();
30392 onFileSelected : function(e)
30394 e.preventDefault();
30396 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30400 var file = this.selectorEl.dom.files[0];
30402 if(this.fireEvent('inspect', this, file) != false){
30403 this.prepare(file);
30408 trash : function(e)
30410 this.fireEvent('trash', this);
30413 download : function(e)
30415 this.fireEvent('download', this);
30418 loadCanvas : function(src)
30420 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30424 this.imageEl = document.createElement('img');
30428 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30430 this.imageEl.src = src;
30434 onLoadCanvas : function()
30436 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30437 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30439 this.bodyEl.un('click', this.beforeSelectFile, this);
30441 this.notifyEl.hide();
30442 this.thumbEl.show();
30443 this.footerEl.show();
30445 this.baseRotateLevel();
30447 if(this.isDocument){
30448 this.setThumbBoxSize();
30451 this.setThumbBoxPosition();
30453 this.baseScaleLevel();
30459 this.canvasLoaded = true;
30462 this.maskEl.unmask();
30467 setCanvasPosition : function()
30469 if(!this.canvasEl){
30473 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30474 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30476 this.previewEl.setLeft(pw);
30477 this.previewEl.setTop(ph);
30481 onMouseDown : function(e)
30485 this.dragable = true;
30486 this.pinching = false;
30488 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30489 this.dragable = false;
30493 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30494 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30498 onMouseMove : function(e)
30502 if(!this.canvasLoaded){
30506 if (!this.dragable){
30510 var minX = Math.ceil(this.thumbEl.getLeft(true));
30511 var minY = Math.ceil(this.thumbEl.getTop(true));
30513 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30514 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30516 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30517 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30519 x = x - this.mouseX;
30520 y = y - this.mouseY;
30522 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30523 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30525 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30526 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30528 this.previewEl.setLeft(bgX);
30529 this.previewEl.setTop(bgY);
30531 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30532 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30535 onMouseUp : function(e)
30539 this.dragable = false;
30542 onMouseWheel : function(e)
30546 this.startScale = this.scale;
30548 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30550 if(!this.zoomable()){
30551 this.scale = this.startScale;
30560 zoomable : function()
30562 var minScale = this.thumbEl.getWidth() / this.minWidth;
30564 if(this.minWidth < this.minHeight){
30565 minScale = this.thumbEl.getHeight() / this.minHeight;
30568 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30569 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30573 (this.rotate == 0 || this.rotate == 180) &&
30575 width > this.imageEl.OriginWidth ||
30576 height > this.imageEl.OriginHeight ||
30577 (width < this.minWidth && height < this.minHeight)
30585 (this.rotate == 90 || this.rotate == 270) &&
30587 width > this.imageEl.OriginWidth ||
30588 height > this.imageEl.OriginHeight ||
30589 (width < this.minHeight && height < this.minWidth)
30596 !this.isDocument &&
30597 (this.rotate == 0 || this.rotate == 180) &&
30599 width < this.minWidth ||
30600 width > this.imageEl.OriginWidth ||
30601 height < this.minHeight ||
30602 height > this.imageEl.OriginHeight
30609 !this.isDocument &&
30610 (this.rotate == 90 || this.rotate == 270) &&
30612 width < this.minHeight ||
30613 width > this.imageEl.OriginWidth ||
30614 height < this.minWidth ||
30615 height > this.imageEl.OriginHeight
30625 onRotateLeft : function(e)
30627 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30629 var minScale = this.thumbEl.getWidth() / this.minWidth;
30631 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30632 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30634 this.startScale = this.scale;
30636 while (this.getScaleLevel() < minScale){
30638 this.scale = this.scale + 1;
30640 if(!this.zoomable()){
30645 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30646 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30651 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30658 this.scale = this.startScale;
30660 this.onRotateFail();
30665 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30667 if(this.isDocument){
30668 this.setThumbBoxSize();
30669 this.setThumbBoxPosition();
30670 this.setCanvasPosition();
30675 this.fireEvent('rotate', this, 'left');
30679 onRotateRight : function(e)
30681 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30683 var minScale = this.thumbEl.getWidth() / this.minWidth;
30685 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30686 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30688 this.startScale = this.scale;
30690 while (this.getScaleLevel() < minScale){
30692 this.scale = this.scale + 1;
30694 if(!this.zoomable()){
30699 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30700 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30705 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30712 this.scale = this.startScale;
30714 this.onRotateFail();
30719 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30721 if(this.isDocument){
30722 this.setThumbBoxSize();
30723 this.setThumbBoxPosition();
30724 this.setCanvasPosition();
30729 this.fireEvent('rotate', this, 'right');
30732 onRotateFail : function()
30734 this.errorEl.show(true);
30738 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30743 this.previewEl.dom.innerHTML = '';
30745 var canvasEl = document.createElement("canvas");
30747 var contextEl = canvasEl.getContext("2d");
30749 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30750 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30751 var center = this.imageEl.OriginWidth / 2;
30753 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30754 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30755 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30756 center = this.imageEl.OriginHeight / 2;
30759 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30761 contextEl.translate(center, center);
30762 contextEl.rotate(this.rotate * Math.PI / 180);
30764 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30766 this.canvasEl = document.createElement("canvas");
30768 this.contextEl = this.canvasEl.getContext("2d");
30770 switch (this.rotate) {
30773 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30774 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30776 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30781 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30782 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30784 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30785 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);
30789 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30794 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30795 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30797 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30798 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);
30802 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);
30807 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30808 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30810 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30811 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30815 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);
30822 this.previewEl.appendChild(this.canvasEl);
30824 this.setCanvasPosition();
30829 if(!this.canvasLoaded){
30833 var imageCanvas = document.createElement("canvas");
30835 var imageContext = imageCanvas.getContext("2d");
30837 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30838 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30840 var center = imageCanvas.width / 2;
30842 imageContext.translate(center, center);
30844 imageContext.rotate(this.rotate * Math.PI / 180);
30846 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30848 var canvas = document.createElement("canvas");
30850 var context = canvas.getContext("2d");
30852 canvas.width = this.minWidth;
30853 canvas.height = this.minHeight;
30855 switch (this.rotate) {
30858 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30859 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30861 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30862 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30864 var targetWidth = this.minWidth - 2 * x;
30865 var targetHeight = this.minHeight - 2 * y;
30869 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30870 scale = targetWidth / width;
30873 if(x > 0 && y == 0){
30874 scale = targetHeight / height;
30877 if(x > 0 && y > 0){
30878 scale = targetWidth / width;
30880 if(width < height){
30881 scale = targetHeight / height;
30885 context.scale(scale, scale);
30887 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30888 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30890 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30891 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30893 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30898 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30899 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30901 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30902 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30904 var targetWidth = this.minWidth - 2 * x;
30905 var targetHeight = this.minHeight - 2 * y;
30909 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30910 scale = targetWidth / width;
30913 if(x > 0 && y == 0){
30914 scale = targetHeight / height;
30917 if(x > 0 && y > 0){
30918 scale = targetWidth / width;
30920 if(width < height){
30921 scale = targetHeight / height;
30925 context.scale(scale, scale);
30927 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30928 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30930 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30931 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30933 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30935 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30940 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30941 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30943 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30944 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30946 var targetWidth = this.minWidth - 2 * x;
30947 var targetHeight = this.minHeight - 2 * y;
30951 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30952 scale = targetWidth / width;
30955 if(x > 0 && y == 0){
30956 scale = targetHeight / height;
30959 if(x > 0 && y > 0){
30960 scale = targetWidth / width;
30962 if(width < height){
30963 scale = targetHeight / height;
30967 context.scale(scale, scale);
30969 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30970 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30972 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30973 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30975 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30976 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30978 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30983 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30984 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30986 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30987 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30989 var targetWidth = this.minWidth - 2 * x;
30990 var targetHeight = this.minHeight - 2 * y;
30994 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30995 scale = targetWidth / width;
30998 if(x > 0 && y == 0){
30999 scale = targetHeight / height;
31002 if(x > 0 && y > 0){
31003 scale = targetWidth / width;
31005 if(width < height){
31006 scale = targetHeight / height;
31010 context.scale(scale, scale);
31012 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31013 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31015 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31016 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31018 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31020 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31027 this.cropData = canvas.toDataURL(this.cropType);
31029 if(this.fireEvent('crop', this, this.cropData) !== false){
31030 this.process(this.file, this.cropData);
31037 setThumbBoxSize : function()
31041 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31042 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31043 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31045 this.minWidth = width;
31046 this.minHeight = height;
31048 if(this.rotate == 90 || this.rotate == 270){
31049 this.minWidth = height;
31050 this.minHeight = width;
31055 width = Math.ceil(this.minWidth * height / this.minHeight);
31057 if(this.minWidth > this.minHeight){
31059 height = Math.ceil(this.minHeight * width / this.minWidth);
31062 this.thumbEl.setStyle({
31063 width : width + 'px',
31064 height : height + 'px'
31071 setThumbBoxPosition : function()
31073 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31074 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31076 this.thumbEl.setLeft(x);
31077 this.thumbEl.setTop(y);
31081 baseRotateLevel : function()
31083 this.baseRotate = 1;
31086 typeof(this.exif) != 'undefined' &&
31087 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31088 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31090 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31093 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31097 baseScaleLevel : function()
31101 if(this.isDocument){
31103 if(this.baseRotate == 6 || this.baseRotate == 8){
31105 height = this.thumbEl.getHeight();
31106 this.baseScale = height / this.imageEl.OriginWidth;
31108 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31109 width = this.thumbEl.getWidth();
31110 this.baseScale = width / this.imageEl.OriginHeight;
31116 height = this.thumbEl.getHeight();
31117 this.baseScale = height / this.imageEl.OriginHeight;
31119 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31120 width = this.thumbEl.getWidth();
31121 this.baseScale = width / this.imageEl.OriginWidth;
31127 if(this.baseRotate == 6 || this.baseRotate == 8){
31129 width = this.thumbEl.getHeight();
31130 this.baseScale = width / this.imageEl.OriginHeight;
31132 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31133 height = this.thumbEl.getWidth();
31134 this.baseScale = height / this.imageEl.OriginHeight;
31137 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31138 height = this.thumbEl.getWidth();
31139 this.baseScale = height / this.imageEl.OriginHeight;
31141 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31142 width = this.thumbEl.getHeight();
31143 this.baseScale = width / this.imageEl.OriginWidth;
31150 width = this.thumbEl.getWidth();
31151 this.baseScale = width / this.imageEl.OriginWidth;
31153 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31154 height = this.thumbEl.getHeight();
31155 this.baseScale = height / this.imageEl.OriginHeight;
31158 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31160 height = this.thumbEl.getHeight();
31161 this.baseScale = height / this.imageEl.OriginHeight;
31163 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31164 width = this.thumbEl.getWidth();
31165 this.baseScale = width / this.imageEl.OriginWidth;
31173 getScaleLevel : function()
31175 return this.baseScale * Math.pow(1.1, this.scale);
31178 onTouchStart : function(e)
31180 if(!this.canvasLoaded){
31181 this.beforeSelectFile(e);
31185 var touches = e.browserEvent.touches;
31191 if(touches.length == 1){
31192 this.onMouseDown(e);
31196 if(touches.length != 2){
31202 for(var i = 0, finger; finger = touches[i]; i++){
31203 coords.push(finger.pageX, finger.pageY);
31206 var x = Math.pow(coords[0] - coords[2], 2);
31207 var y = Math.pow(coords[1] - coords[3], 2);
31209 this.startDistance = Math.sqrt(x + y);
31211 this.startScale = this.scale;
31213 this.pinching = true;
31214 this.dragable = false;
31218 onTouchMove : function(e)
31220 if(!this.pinching && !this.dragable){
31224 var touches = e.browserEvent.touches;
31231 this.onMouseMove(e);
31237 for(var i = 0, finger; finger = touches[i]; i++){
31238 coords.push(finger.pageX, finger.pageY);
31241 var x = Math.pow(coords[0] - coords[2], 2);
31242 var y = Math.pow(coords[1] - coords[3], 2);
31244 this.endDistance = Math.sqrt(x + y);
31246 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31248 if(!this.zoomable()){
31249 this.scale = this.startScale;
31257 onTouchEnd : function(e)
31259 this.pinching = false;
31260 this.dragable = false;
31264 process : function(file, crop)
31267 this.maskEl.mask(this.loadingText);
31270 this.xhr = new XMLHttpRequest();
31272 file.xhr = this.xhr;
31274 this.xhr.open(this.method, this.url, true);
31277 "Accept": "application/json",
31278 "Cache-Control": "no-cache",
31279 "X-Requested-With": "XMLHttpRequest"
31282 for (var headerName in headers) {
31283 var headerValue = headers[headerName];
31285 this.xhr.setRequestHeader(headerName, headerValue);
31291 this.xhr.onload = function()
31293 _this.xhrOnLoad(_this.xhr);
31296 this.xhr.onerror = function()
31298 _this.xhrOnError(_this.xhr);
31301 var formData = new FormData();
31303 formData.append('returnHTML', 'NO');
31306 formData.append('crop', crop);
31309 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31310 formData.append(this.paramName, file, file.name);
31313 if(typeof(file.filename) != 'undefined'){
31314 formData.append('filename', file.filename);
31317 if(typeof(file.mimetype) != 'undefined'){
31318 formData.append('mimetype', file.mimetype);
31321 if(this.fireEvent('arrange', this, formData) != false){
31322 this.xhr.send(formData);
31326 xhrOnLoad : function(xhr)
31329 this.maskEl.unmask();
31332 if (xhr.readyState !== 4) {
31333 this.fireEvent('exception', this, xhr);
31337 var response = Roo.decode(xhr.responseText);
31339 if(!response.success){
31340 this.fireEvent('exception', this, xhr);
31344 var response = Roo.decode(xhr.responseText);
31346 this.fireEvent('upload', this, response);
31350 xhrOnError : function()
31353 this.maskEl.unmask();
31356 Roo.log('xhr on error');
31358 var response = Roo.decode(xhr.responseText);
31364 prepare : function(file)
31367 this.maskEl.mask(this.loadingText);
31373 if(typeof(file) === 'string'){
31374 this.loadCanvas(file);
31378 if(!file || !this.urlAPI){
31383 this.cropType = file.type;
31387 if(this.fireEvent('prepare', this, this.file) != false){
31389 var reader = new FileReader();
31391 reader.onload = function (e) {
31392 if (e.target.error) {
31393 Roo.log(e.target.error);
31397 var buffer = e.target.result,
31398 dataView = new DataView(buffer),
31400 maxOffset = dataView.byteLength - 4,
31404 if (dataView.getUint16(0) === 0xffd8) {
31405 while (offset < maxOffset) {
31406 markerBytes = dataView.getUint16(offset);
31408 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31409 markerLength = dataView.getUint16(offset + 2) + 2;
31410 if (offset + markerLength > dataView.byteLength) {
31411 Roo.log('Invalid meta data: Invalid segment size.');
31415 if(markerBytes == 0xffe1){
31416 _this.parseExifData(
31423 offset += markerLength;
31433 var url = _this.urlAPI.createObjectURL(_this.file);
31435 _this.loadCanvas(url);
31440 reader.readAsArrayBuffer(this.file);
31446 parseExifData : function(dataView, offset, length)
31448 var tiffOffset = offset + 10,
31452 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31453 // No Exif data, might be XMP data instead
31457 // Check for the ASCII code for "Exif" (0x45786966):
31458 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31459 // No Exif data, might be XMP data instead
31462 if (tiffOffset + 8 > dataView.byteLength) {
31463 Roo.log('Invalid Exif data: Invalid segment size.');
31466 // Check for the two null bytes:
31467 if (dataView.getUint16(offset + 8) !== 0x0000) {
31468 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31471 // Check the byte alignment:
31472 switch (dataView.getUint16(tiffOffset)) {
31474 littleEndian = true;
31477 littleEndian = false;
31480 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31483 // Check for the TIFF tag marker (0x002A):
31484 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31485 Roo.log('Invalid Exif data: Missing TIFF marker.');
31488 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31489 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31491 this.parseExifTags(
31494 tiffOffset + dirOffset,
31499 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31504 if (dirOffset + 6 > dataView.byteLength) {
31505 Roo.log('Invalid Exif data: Invalid directory offset.');
31508 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31509 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31510 if (dirEndOffset + 4 > dataView.byteLength) {
31511 Roo.log('Invalid Exif data: Invalid directory size.');
31514 for (i = 0; i < tagsNumber; i += 1) {
31518 dirOffset + 2 + 12 * i, // tag offset
31522 // Return the offset to the next directory:
31523 return dataView.getUint32(dirEndOffset, littleEndian);
31526 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31528 var tag = dataView.getUint16(offset, littleEndian);
31530 this.exif[tag] = this.getExifValue(
31534 dataView.getUint16(offset + 2, littleEndian), // tag type
31535 dataView.getUint32(offset + 4, littleEndian), // tag length
31540 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31542 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31551 Roo.log('Invalid Exif data: Invalid tag type.');
31555 tagSize = tagType.size * length;
31556 // Determine if the value is contained in the dataOffset bytes,
31557 // or if the value at the dataOffset is a pointer to the actual data:
31558 dataOffset = tagSize > 4 ?
31559 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31560 if (dataOffset + tagSize > dataView.byteLength) {
31561 Roo.log('Invalid Exif data: Invalid data offset.');
31564 if (length === 1) {
31565 return tagType.getValue(dataView, dataOffset, littleEndian);
31568 for (i = 0; i < length; i += 1) {
31569 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31572 if (tagType.ascii) {
31574 // Concatenate the chars:
31575 for (i = 0; i < values.length; i += 1) {
31577 // Ignore the terminating NULL byte(s):
31578 if (c === '\u0000') {
31590 Roo.apply(Roo.bootstrap.UploadCropbox, {
31592 'Orientation': 0x0112
31596 1: 0, //'top-left',
31598 3: 180, //'bottom-right',
31599 // 4: 'bottom-left',
31601 6: 90, //'right-top',
31602 // 7: 'right-bottom',
31603 8: 270 //'left-bottom'
31607 // byte, 8-bit unsigned int:
31609 getValue: function (dataView, dataOffset) {
31610 return dataView.getUint8(dataOffset);
31614 // ascii, 8-bit byte:
31616 getValue: function (dataView, dataOffset) {
31617 return String.fromCharCode(dataView.getUint8(dataOffset));
31622 // short, 16 bit int:
31624 getValue: function (dataView, dataOffset, littleEndian) {
31625 return dataView.getUint16(dataOffset, littleEndian);
31629 // long, 32 bit int:
31631 getValue: function (dataView, dataOffset, littleEndian) {
31632 return dataView.getUint32(dataOffset, littleEndian);
31636 // rational = two long values, first is numerator, second is denominator:
31638 getValue: function (dataView, dataOffset, littleEndian) {
31639 return dataView.getUint32(dataOffset, littleEndian) /
31640 dataView.getUint32(dataOffset + 4, littleEndian);
31644 // slong, 32 bit signed int:
31646 getValue: function (dataView, dataOffset, littleEndian) {
31647 return dataView.getInt32(dataOffset, littleEndian);
31651 // srational, two slongs, first is numerator, second is denominator:
31653 getValue: function (dataView, dataOffset, littleEndian) {
31654 return dataView.getInt32(dataOffset, littleEndian) /
31655 dataView.getInt32(dataOffset + 4, littleEndian);
31665 cls : 'btn-group roo-upload-cropbox-rotate-left',
31666 action : 'rotate-left',
31670 cls : 'btn btn-default',
31671 html : '<i class="fa fa-undo"></i>'
31677 cls : 'btn-group roo-upload-cropbox-picture',
31678 action : 'picture',
31682 cls : 'btn btn-default',
31683 html : '<i class="fa fa-picture-o"></i>'
31689 cls : 'btn-group roo-upload-cropbox-rotate-right',
31690 action : 'rotate-right',
31694 cls : 'btn btn-default',
31695 html : '<i class="fa fa-repeat"></i>'
31703 cls : 'btn-group roo-upload-cropbox-rotate-left',
31704 action : 'rotate-left',
31708 cls : 'btn btn-default',
31709 html : '<i class="fa fa-undo"></i>'
31715 cls : 'btn-group roo-upload-cropbox-download',
31716 action : 'download',
31720 cls : 'btn btn-default',
31721 html : '<i class="fa fa-download"></i>'
31727 cls : 'btn-group roo-upload-cropbox-crop',
31732 cls : 'btn btn-default',
31733 html : '<i class="fa fa-crop"></i>'
31739 cls : 'btn-group roo-upload-cropbox-trash',
31744 cls : 'btn btn-default',
31745 html : '<i class="fa fa-trash"></i>'
31751 cls : 'btn-group roo-upload-cropbox-rotate-right',
31752 action : 'rotate-right',
31756 cls : 'btn btn-default',
31757 html : '<i class="fa fa-repeat"></i>'
31765 cls : 'btn-group roo-upload-cropbox-rotate-left',
31766 action : 'rotate-left',
31770 cls : 'btn btn-default',
31771 html : '<i class="fa fa-undo"></i>'
31777 cls : 'btn-group roo-upload-cropbox-rotate-right',
31778 action : 'rotate-right',
31782 cls : 'btn btn-default',
31783 html : '<i class="fa fa-repeat"></i>'
31796 * @class Roo.bootstrap.DocumentManager
31797 * @extends Roo.bootstrap.Component
31798 * Bootstrap DocumentManager class
31799 * @cfg {String} paramName default 'imageUpload'
31800 * @cfg {String} toolTipName default 'filename'
31801 * @cfg {String} method default POST
31802 * @cfg {String} url action url
31803 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31804 * @cfg {Boolean} multiple multiple upload default true
31805 * @cfg {Number} thumbSize default 300
31806 * @cfg {String} fieldLabel
31807 * @cfg {Number} labelWidth default 4
31808 * @cfg {String} labelAlign (left|top) default left
31809 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31810 * @cfg {Number} labellg set the width of label (1-12)
31811 * @cfg {Number} labelmd set the width of label (1-12)
31812 * @cfg {Number} labelsm set the width of label (1-12)
31813 * @cfg {Number} labelxs set the width of label (1-12)
31816 * Create a new DocumentManager
31817 * @param {Object} config The config object
31820 Roo.bootstrap.DocumentManager = function(config){
31821 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31824 this.delegates = [];
31829 * Fire when initial the DocumentManager
31830 * @param {Roo.bootstrap.DocumentManager} this
31835 * inspect selected file
31836 * @param {Roo.bootstrap.DocumentManager} this
31837 * @param {File} file
31842 * Fire when xhr load exception
31843 * @param {Roo.bootstrap.DocumentManager} this
31844 * @param {XMLHttpRequest} xhr
31846 "exception" : true,
31848 * @event afterupload
31849 * Fire when xhr load exception
31850 * @param {Roo.bootstrap.DocumentManager} this
31851 * @param {XMLHttpRequest} xhr
31853 "afterupload" : true,
31856 * prepare the form data
31857 * @param {Roo.bootstrap.DocumentManager} this
31858 * @param {Object} formData
31863 * Fire when remove the file
31864 * @param {Roo.bootstrap.DocumentManager} this
31865 * @param {Object} file
31870 * Fire after refresh the file
31871 * @param {Roo.bootstrap.DocumentManager} this
31876 * Fire after click the image
31877 * @param {Roo.bootstrap.DocumentManager} this
31878 * @param {Object} file
31883 * Fire when upload a image and editable set to true
31884 * @param {Roo.bootstrap.DocumentManager} this
31885 * @param {Object} file
31889 * @event beforeselectfile
31890 * Fire before select file
31891 * @param {Roo.bootstrap.DocumentManager} this
31893 "beforeselectfile" : true,
31896 * Fire before process file
31897 * @param {Roo.bootstrap.DocumentManager} this
31898 * @param {Object} file
31902 * @event previewrendered
31903 * Fire when preview rendered
31904 * @param {Roo.bootstrap.DocumentManager} this
31905 * @param {Object} file
31907 "previewrendered" : true,
31910 "previewResize" : true
31915 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31924 paramName : 'imageUpload',
31925 toolTipName : 'filename',
31928 labelAlign : 'left',
31938 getAutoCreate : function()
31940 var managerWidget = {
31942 cls : 'roo-document-manager',
31946 cls : 'roo-document-manager-selector',
31951 cls : 'roo-document-manager-uploader',
31955 cls : 'roo-document-manager-upload-btn',
31956 html : '<i class="fa fa-plus"></i>'
31967 cls : 'column col-md-12',
31972 if(this.fieldLabel.length){
31977 cls : 'column col-md-12',
31978 html : this.fieldLabel
31982 cls : 'column col-md-12',
31987 if(this.labelAlign == 'left'){
31992 html : this.fieldLabel
32001 if(this.labelWidth > 12){
32002 content[0].style = "width: " + this.labelWidth + 'px';
32005 if(this.labelWidth < 13 && this.labelmd == 0){
32006 this.labelmd = this.labelWidth;
32009 if(this.labellg > 0){
32010 content[0].cls += ' col-lg-' + this.labellg;
32011 content[1].cls += ' col-lg-' + (12 - this.labellg);
32014 if(this.labelmd > 0){
32015 content[0].cls += ' col-md-' + this.labelmd;
32016 content[1].cls += ' col-md-' + (12 - this.labelmd);
32019 if(this.labelsm > 0){
32020 content[0].cls += ' col-sm-' + this.labelsm;
32021 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32024 if(this.labelxs > 0){
32025 content[0].cls += ' col-xs-' + this.labelxs;
32026 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32034 cls : 'row clearfix',
32042 initEvents : function()
32044 this.managerEl = this.el.select('.roo-document-manager', true).first();
32045 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32047 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32048 this.selectorEl.hide();
32051 this.selectorEl.attr('multiple', 'multiple');
32054 this.selectorEl.on('change', this.onFileSelected, this);
32056 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32057 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32059 this.uploader.on('click', this.onUploaderClick, this);
32061 this.renderProgressDialog();
32065 window.addEventListener("resize", function() { _this.refresh(); } );
32067 this.fireEvent('initial', this);
32070 renderProgressDialog : function()
32074 this.progressDialog = new Roo.bootstrap.Modal({
32075 cls : 'roo-document-manager-progress-dialog',
32076 allow_close : false,
32087 btnclick : function() {
32088 _this.uploadCancel();
32094 this.progressDialog.render(Roo.get(document.body));
32096 this.progress = new Roo.bootstrap.Progress({
32097 cls : 'roo-document-manager-progress',
32102 this.progress.render(this.progressDialog.getChildContainer());
32104 this.progressBar = new Roo.bootstrap.ProgressBar({
32105 cls : 'roo-document-manager-progress-bar',
32108 aria_valuemax : 12,
32112 this.progressBar.render(this.progress.getChildContainer());
32115 onUploaderClick : function(e)
32117 e.preventDefault();
32119 if(this.fireEvent('beforeselectfile', this) != false){
32120 this.selectorEl.dom.click();
32125 onFileSelected : function(e)
32127 e.preventDefault();
32129 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32133 Roo.each(this.selectorEl.dom.files, function(file){
32134 if(this.fireEvent('inspect', this, file) != false){
32135 this.files.push(file);
32145 this.selectorEl.dom.value = '';
32147 if(!this.files || !this.files.length){
32151 if(this.boxes > 0 && this.files.length > this.boxes){
32152 this.files = this.files.slice(0, this.boxes);
32155 this.uploader.show();
32157 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32158 this.uploader.hide();
32167 Roo.each(this.files, function(file){
32169 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32170 var f = this.renderPreview(file);
32175 if(file.type.indexOf('image') != -1){
32176 this.delegates.push(
32178 _this.process(file);
32179 }).createDelegate(this)
32187 _this.process(file);
32188 }).createDelegate(this)
32193 this.files = files;
32195 this.delegates = this.delegates.concat(docs);
32197 if(!this.delegates.length){
32202 this.progressBar.aria_valuemax = this.delegates.length;
32209 arrange : function()
32211 if(!this.delegates.length){
32212 this.progressDialog.hide();
32217 var delegate = this.delegates.shift();
32219 this.progressDialog.show();
32221 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32223 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32228 refresh : function()
32230 this.uploader.show();
32232 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32233 this.uploader.hide();
32236 Roo.isTouch ? this.closable(false) : this.closable(true);
32238 this.fireEvent('refresh', this);
32241 onRemove : function(e, el, o)
32243 e.preventDefault();
32245 this.fireEvent('remove', this, o);
32249 remove : function(o)
32253 Roo.each(this.files, function(file){
32254 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32263 this.files = files;
32270 Roo.each(this.files, function(file){
32275 file.target.remove();
32284 onClick : function(e, el, o)
32286 e.preventDefault();
32288 this.fireEvent('click', this, o);
32292 closable : function(closable)
32294 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32296 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32308 xhrOnLoad : function(xhr)
32310 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32314 if (xhr.readyState !== 4) {
32316 this.fireEvent('exception', this, xhr);
32320 var response = Roo.decode(xhr.responseText);
32322 if(!response.success){
32324 this.fireEvent('exception', this, xhr);
32328 var file = this.renderPreview(response.data);
32330 this.files.push(file);
32334 this.fireEvent('afterupload', this, xhr);
32338 xhrOnError : function(xhr)
32340 Roo.log('xhr on error');
32342 var response = Roo.decode(xhr.responseText);
32349 process : function(file)
32351 if(this.fireEvent('process', this, file) !== false){
32352 if(this.editable && file.type.indexOf('image') != -1){
32353 this.fireEvent('edit', this, file);
32357 this.uploadStart(file, false);
32364 uploadStart : function(file, crop)
32366 this.xhr = new XMLHttpRequest();
32368 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32373 file.xhr = this.xhr;
32375 this.managerEl.createChild({
32377 cls : 'roo-document-manager-loading',
32381 tooltip : file.name,
32382 cls : 'roo-document-manager-thumb',
32383 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32389 this.xhr.open(this.method, this.url, true);
32392 "Accept": "application/json",
32393 "Cache-Control": "no-cache",
32394 "X-Requested-With": "XMLHttpRequest"
32397 for (var headerName in headers) {
32398 var headerValue = headers[headerName];
32400 this.xhr.setRequestHeader(headerName, headerValue);
32406 this.xhr.onload = function()
32408 _this.xhrOnLoad(_this.xhr);
32411 this.xhr.onerror = function()
32413 _this.xhrOnError(_this.xhr);
32416 var formData = new FormData();
32418 formData.append('returnHTML', 'NO');
32421 formData.append('crop', crop);
32424 formData.append(this.paramName, file, file.name);
32431 if(this.fireEvent('prepare', this, formData, options) != false){
32433 if(options.manually){
32437 this.xhr.send(formData);
32441 this.uploadCancel();
32444 uploadCancel : function()
32450 this.delegates = [];
32452 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32459 renderPreview : function(file)
32461 if(typeof(file.target) != 'undefined' && file.target){
32465 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32467 var previewEl = this.managerEl.createChild({
32469 cls : 'roo-document-manager-preview',
32473 tooltip : file[this.toolTipName],
32474 cls : 'roo-document-manager-thumb',
32475 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32480 html : '<i class="fa fa-times-circle"></i>'
32485 var close = previewEl.select('button.close', true).first();
32487 close.on('click', this.onRemove, this, file);
32489 file.target = previewEl;
32491 var image = previewEl.select('img', true).first();
32495 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32497 image.on('click', this.onClick, this, file);
32499 this.fireEvent('previewrendered', this, file);
32505 onPreviewLoad : function(file, image)
32507 if(typeof(file.target) == 'undefined' || !file.target){
32511 var width = image.dom.naturalWidth || image.dom.width;
32512 var height = image.dom.naturalHeight || image.dom.height;
32514 if(!this.previewResize) {
32518 if(width > height){
32519 file.target.addClass('wide');
32523 file.target.addClass('tall');
32528 uploadFromSource : function(file, crop)
32530 this.xhr = new XMLHttpRequest();
32532 this.managerEl.createChild({
32534 cls : 'roo-document-manager-loading',
32538 tooltip : file.name,
32539 cls : 'roo-document-manager-thumb',
32540 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32546 this.xhr.open(this.method, this.url, true);
32549 "Accept": "application/json",
32550 "Cache-Control": "no-cache",
32551 "X-Requested-With": "XMLHttpRequest"
32554 for (var headerName in headers) {
32555 var headerValue = headers[headerName];
32557 this.xhr.setRequestHeader(headerName, headerValue);
32563 this.xhr.onload = function()
32565 _this.xhrOnLoad(_this.xhr);
32568 this.xhr.onerror = function()
32570 _this.xhrOnError(_this.xhr);
32573 var formData = new FormData();
32575 formData.append('returnHTML', 'NO');
32577 formData.append('crop', crop);
32579 if(typeof(file.filename) != 'undefined'){
32580 formData.append('filename', file.filename);
32583 if(typeof(file.mimetype) != 'undefined'){
32584 formData.append('mimetype', file.mimetype);
32589 if(this.fireEvent('prepare', this, formData) != false){
32590 this.xhr.send(formData);
32600 * @class Roo.bootstrap.DocumentViewer
32601 * @extends Roo.bootstrap.Component
32602 * Bootstrap DocumentViewer class
32603 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32604 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32607 * Create a new DocumentViewer
32608 * @param {Object} config The config object
32611 Roo.bootstrap.DocumentViewer = function(config){
32612 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32617 * Fire after initEvent
32618 * @param {Roo.bootstrap.DocumentViewer} this
32624 * @param {Roo.bootstrap.DocumentViewer} this
32629 * Fire after download button
32630 * @param {Roo.bootstrap.DocumentViewer} this
32635 * Fire after trash button
32636 * @param {Roo.bootstrap.DocumentViewer} this
32643 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32645 showDownload : true,
32649 getAutoCreate : function()
32653 cls : 'roo-document-viewer',
32657 cls : 'roo-document-viewer-body',
32661 cls : 'roo-document-viewer-thumb',
32665 cls : 'roo-document-viewer-image'
32673 cls : 'roo-document-viewer-footer',
32676 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32680 cls : 'btn-group roo-document-viewer-download',
32684 cls : 'btn btn-default',
32685 html : '<i class="fa fa-download"></i>'
32691 cls : 'btn-group roo-document-viewer-trash',
32695 cls : 'btn btn-default',
32696 html : '<i class="fa fa-trash"></i>'
32709 initEvents : function()
32711 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32712 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32714 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32715 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32717 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32718 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32720 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32721 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32723 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32724 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32726 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32727 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32729 this.bodyEl.on('click', this.onClick, this);
32730 this.downloadBtn.on('click', this.onDownload, this);
32731 this.trashBtn.on('click', this.onTrash, this);
32733 this.downloadBtn.hide();
32734 this.trashBtn.hide();
32736 if(this.showDownload){
32737 this.downloadBtn.show();
32740 if(this.showTrash){
32741 this.trashBtn.show();
32744 if(!this.showDownload && !this.showTrash) {
32745 this.footerEl.hide();
32750 initial : function()
32752 this.fireEvent('initial', this);
32756 onClick : function(e)
32758 e.preventDefault();
32760 this.fireEvent('click', this);
32763 onDownload : function(e)
32765 e.preventDefault();
32767 this.fireEvent('download', this);
32770 onTrash : function(e)
32772 e.preventDefault();
32774 this.fireEvent('trash', this);
32786 * @class Roo.bootstrap.NavProgressBar
32787 * @extends Roo.bootstrap.Component
32788 * Bootstrap NavProgressBar class
32791 * Create a new nav progress bar
32792 * @param {Object} config The config object
32795 Roo.bootstrap.NavProgressBar = function(config){
32796 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32798 this.bullets = this.bullets || [];
32800 // Roo.bootstrap.NavProgressBar.register(this);
32804 * Fires when the active item changes
32805 * @param {Roo.bootstrap.NavProgressBar} this
32806 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32807 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32814 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32819 getAutoCreate : function()
32821 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32825 cls : 'roo-navigation-bar-group',
32829 cls : 'roo-navigation-top-bar'
32833 cls : 'roo-navigation-bullets-bar',
32837 cls : 'roo-navigation-bar'
32844 cls : 'roo-navigation-bottom-bar'
32854 initEvents: function()
32859 onRender : function(ct, position)
32861 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32863 if(this.bullets.length){
32864 Roo.each(this.bullets, function(b){
32873 addItem : function(cfg)
32875 var item = new Roo.bootstrap.NavProgressItem(cfg);
32877 item.parentId = this.id;
32878 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32881 var top = new Roo.bootstrap.Element({
32883 cls : 'roo-navigation-bar-text'
32886 var bottom = new Roo.bootstrap.Element({
32888 cls : 'roo-navigation-bar-text'
32891 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32892 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32894 var topText = new Roo.bootstrap.Element({
32896 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32899 var bottomText = new Roo.bootstrap.Element({
32901 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32904 topText.onRender(top.el, null);
32905 bottomText.onRender(bottom.el, null);
32908 item.bottomEl = bottom;
32911 this.barItems.push(item);
32916 getActive : function()
32918 var active = false;
32920 Roo.each(this.barItems, function(v){
32922 if (!v.isActive()) {
32934 setActiveItem : function(item)
32938 Roo.each(this.barItems, function(v){
32939 if (v.rid == item.rid) {
32943 if (v.isActive()) {
32944 v.setActive(false);
32949 item.setActive(true);
32951 this.fireEvent('changed', this, item, prev);
32954 getBarItem: function(rid)
32958 Roo.each(this.barItems, function(e) {
32959 if (e.rid != rid) {
32970 indexOfItem : function(item)
32974 Roo.each(this.barItems, function(v, i){
32976 if (v.rid != item.rid) {
32987 setActiveNext : function()
32989 var i = this.indexOfItem(this.getActive());
32991 if (i > this.barItems.length) {
32995 this.setActiveItem(this.barItems[i+1]);
32998 setActivePrev : function()
33000 var i = this.indexOfItem(this.getActive());
33006 this.setActiveItem(this.barItems[i-1]);
33009 format : function()
33011 if(!this.barItems.length){
33015 var width = 100 / this.barItems.length;
33017 Roo.each(this.barItems, function(i){
33018 i.el.setStyle('width', width + '%');
33019 i.topEl.el.setStyle('width', width + '%');
33020 i.bottomEl.el.setStyle('width', width + '%');
33029 * Nav Progress Item
33034 * @class Roo.bootstrap.NavProgressItem
33035 * @extends Roo.bootstrap.Component
33036 * Bootstrap NavProgressItem class
33037 * @cfg {String} rid the reference id
33038 * @cfg {Boolean} active (true|false) Is item active default false
33039 * @cfg {Boolean} disabled (true|false) Is item active default false
33040 * @cfg {String} html
33041 * @cfg {String} position (top|bottom) text position default bottom
33042 * @cfg {String} icon show icon instead of number
33045 * Create a new NavProgressItem
33046 * @param {Object} config The config object
33048 Roo.bootstrap.NavProgressItem = function(config){
33049 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33054 * The raw click event for the entire grid.
33055 * @param {Roo.bootstrap.NavProgressItem} this
33056 * @param {Roo.EventObject} e
33063 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33069 position : 'bottom',
33072 getAutoCreate : function()
33074 var iconCls = 'roo-navigation-bar-item-icon';
33076 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33080 cls: 'roo-navigation-bar-item',
33090 cfg.cls += ' active';
33093 cfg.cls += ' disabled';
33099 disable : function()
33101 this.setDisabled(true);
33104 enable : function()
33106 this.setDisabled(false);
33109 initEvents: function()
33111 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33113 this.iconEl.on('click', this.onClick, this);
33116 onClick : function(e)
33118 e.preventDefault();
33124 if(this.fireEvent('click', this, e) === false){
33128 this.parent().setActiveItem(this);
33131 isActive: function ()
33133 return this.active;
33136 setActive : function(state)
33138 if(this.active == state){
33142 this.active = state;
33145 this.el.addClass('active');
33149 this.el.removeClass('active');
33154 setDisabled : function(state)
33156 if(this.disabled == state){
33160 this.disabled = state;
33163 this.el.addClass('disabled');
33167 this.el.removeClass('disabled');
33170 tooltipEl : function()
33172 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33185 * @class Roo.bootstrap.FieldLabel
33186 * @extends Roo.bootstrap.Component
33187 * Bootstrap FieldLabel class
33188 * @cfg {String} html contents of the element
33189 * @cfg {String} tag tag of the element default label
33190 * @cfg {String} cls class of the element
33191 * @cfg {String} target label target
33192 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33193 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33194 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33195 * @cfg {String} iconTooltip default "This field is required"
33196 * @cfg {String} indicatorpos (left|right) default left
33199 * Create a new FieldLabel
33200 * @param {Object} config The config object
33203 Roo.bootstrap.FieldLabel = function(config){
33204 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33209 * Fires after the field has been marked as invalid.
33210 * @param {Roo.form.FieldLabel} this
33211 * @param {String} msg The validation message
33216 * Fires after the field has been validated with no errors.
33217 * @param {Roo.form.FieldLabel} this
33223 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33230 invalidClass : 'has-warning',
33231 validClass : 'has-success',
33232 iconTooltip : 'This field is required',
33233 indicatorpos : 'left',
33235 getAutoCreate : function(){
33238 if (!this.allowBlank) {
33244 cls : 'roo-bootstrap-field-label ' + this.cls,
33249 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33250 tooltip : this.iconTooltip
33259 if(this.indicatorpos == 'right'){
33262 cls : 'roo-bootstrap-field-label ' + this.cls,
33271 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33272 tooltip : this.iconTooltip
33281 initEvents: function()
33283 Roo.bootstrap.Element.superclass.initEvents.call(this);
33285 this.indicator = this.indicatorEl();
33287 if(this.indicator){
33288 this.indicator.removeClass('visible');
33289 this.indicator.addClass('invisible');
33292 Roo.bootstrap.FieldLabel.register(this);
33295 indicatorEl : function()
33297 var indicator = this.el.select('i.roo-required-indicator',true).first();
33308 * Mark this field as valid
33310 markValid : function()
33312 if(this.indicator){
33313 this.indicator.removeClass('visible');
33314 this.indicator.addClass('invisible');
33316 if (Roo.bootstrap.version == 3) {
33317 this.el.removeClass(this.invalidClass);
33318 this.el.addClass(this.validClass);
33320 this.el.removeClass('is-invalid');
33321 this.el.addClass('is-valid');
33325 this.fireEvent('valid', this);
33329 * Mark this field as invalid
33330 * @param {String} msg The validation message
33332 markInvalid : function(msg)
33334 if(this.indicator){
33335 this.indicator.removeClass('invisible');
33336 this.indicator.addClass('visible');
33338 if (Roo.bootstrap.version == 3) {
33339 this.el.removeClass(this.validClass);
33340 this.el.addClass(this.invalidClass);
33342 this.el.removeClass('is-valid');
33343 this.el.addClass('is-invalid');
33347 this.fireEvent('invalid', this, msg);
33353 Roo.apply(Roo.bootstrap.FieldLabel, {
33358 * register a FieldLabel Group
33359 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33361 register : function(label)
33363 if(this.groups.hasOwnProperty(label.target)){
33367 this.groups[label.target] = label;
33371 * fetch a FieldLabel Group based on the target
33372 * @param {string} target
33373 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33375 get: function(target) {
33376 if (typeof(this.groups[target]) == 'undefined') {
33380 return this.groups[target] ;
33389 * page DateSplitField.
33395 * @class Roo.bootstrap.DateSplitField
33396 * @extends Roo.bootstrap.Component
33397 * Bootstrap DateSplitField class
33398 * @cfg {string} fieldLabel - the label associated
33399 * @cfg {Number} labelWidth set the width of label (0-12)
33400 * @cfg {String} labelAlign (top|left)
33401 * @cfg {Boolean} dayAllowBlank (true|false) default false
33402 * @cfg {Boolean} monthAllowBlank (true|false) default false
33403 * @cfg {Boolean} yearAllowBlank (true|false) default false
33404 * @cfg {string} dayPlaceholder
33405 * @cfg {string} monthPlaceholder
33406 * @cfg {string} yearPlaceholder
33407 * @cfg {string} dayFormat default 'd'
33408 * @cfg {string} monthFormat default 'm'
33409 * @cfg {string} yearFormat default 'Y'
33410 * @cfg {Number} labellg set the width of label (1-12)
33411 * @cfg {Number} labelmd set the width of label (1-12)
33412 * @cfg {Number} labelsm set the width of label (1-12)
33413 * @cfg {Number} labelxs set the width of label (1-12)
33417 * Create a new DateSplitField
33418 * @param {Object} config The config object
33421 Roo.bootstrap.DateSplitField = function(config){
33422 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33428 * getting the data of years
33429 * @param {Roo.bootstrap.DateSplitField} this
33430 * @param {Object} years
33435 * getting the data of days
33436 * @param {Roo.bootstrap.DateSplitField} this
33437 * @param {Object} days
33442 * Fires after the field has been marked as invalid.
33443 * @param {Roo.form.Field} this
33444 * @param {String} msg The validation message
33449 * Fires after the field has been validated with no errors.
33450 * @param {Roo.form.Field} this
33456 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33459 labelAlign : 'top',
33461 dayAllowBlank : false,
33462 monthAllowBlank : false,
33463 yearAllowBlank : false,
33464 dayPlaceholder : '',
33465 monthPlaceholder : '',
33466 yearPlaceholder : '',
33470 isFormField : true,
33476 getAutoCreate : function()
33480 cls : 'row roo-date-split-field-group',
33485 cls : 'form-hidden-field roo-date-split-field-group-value',
33491 var labelCls = 'col-md-12';
33492 var contentCls = 'col-md-4';
33494 if(this.fieldLabel){
33498 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33502 html : this.fieldLabel
33507 if(this.labelAlign == 'left'){
33509 if(this.labelWidth > 12){
33510 label.style = "width: " + this.labelWidth + 'px';
33513 if(this.labelWidth < 13 && this.labelmd == 0){
33514 this.labelmd = this.labelWidth;
33517 if(this.labellg > 0){
33518 labelCls = ' col-lg-' + this.labellg;
33519 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33522 if(this.labelmd > 0){
33523 labelCls = ' col-md-' + this.labelmd;
33524 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33527 if(this.labelsm > 0){
33528 labelCls = ' col-sm-' + this.labelsm;
33529 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33532 if(this.labelxs > 0){
33533 labelCls = ' col-xs-' + this.labelxs;
33534 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33538 label.cls += ' ' + labelCls;
33540 cfg.cn.push(label);
33543 Roo.each(['day', 'month', 'year'], function(t){
33546 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33553 inputEl: function ()
33555 return this.el.select('.roo-date-split-field-group-value', true).first();
33558 onRender : function(ct, position)
33562 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33564 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33566 this.dayField = new Roo.bootstrap.ComboBox({
33567 allowBlank : this.dayAllowBlank,
33568 alwaysQuery : true,
33569 displayField : 'value',
33572 forceSelection : true,
33574 placeholder : this.dayPlaceholder,
33575 selectOnFocus : true,
33576 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33577 triggerAction : 'all',
33579 valueField : 'value',
33580 store : new Roo.data.SimpleStore({
33581 data : (function() {
33583 _this.fireEvent('days', _this, days);
33586 fields : [ 'value' ]
33589 select : function (_self, record, index)
33591 _this.setValue(_this.getValue());
33596 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33598 this.monthField = new Roo.bootstrap.MonthField({
33599 after : '<i class=\"fa fa-calendar\"></i>',
33600 allowBlank : this.monthAllowBlank,
33601 placeholder : this.monthPlaceholder,
33604 render : function (_self)
33606 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33607 e.preventDefault();
33611 select : function (_self, oldvalue, newvalue)
33613 _this.setValue(_this.getValue());
33618 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33620 this.yearField = new Roo.bootstrap.ComboBox({
33621 allowBlank : this.yearAllowBlank,
33622 alwaysQuery : true,
33623 displayField : 'value',
33626 forceSelection : true,
33628 placeholder : this.yearPlaceholder,
33629 selectOnFocus : true,
33630 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33631 triggerAction : 'all',
33633 valueField : 'value',
33634 store : new Roo.data.SimpleStore({
33635 data : (function() {
33637 _this.fireEvent('years', _this, years);
33640 fields : [ 'value' ]
33643 select : function (_self, record, index)
33645 _this.setValue(_this.getValue());
33650 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33653 setValue : function(v, format)
33655 this.inputEl.dom.value = v;
33657 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33659 var d = Date.parseDate(v, f);
33666 this.setDay(d.format(this.dayFormat));
33667 this.setMonth(d.format(this.monthFormat));
33668 this.setYear(d.format(this.yearFormat));
33675 setDay : function(v)
33677 this.dayField.setValue(v);
33678 this.inputEl.dom.value = this.getValue();
33683 setMonth : function(v)
33685 this.monthField.setValue(v, true);
33686 this.inputEl.dom.value = this.getValue();
33691 setYear : function(v)
33693 this.yearField.setValue(v);
33694 this.inputEl.dom.value = this.getValue();
33699 getDay : function()
33701 return this.dayField.getValue();
33704 getMonth : function()
33706 return this.monthField.getValue();
33709 getYear : function()
33711 return this.yearField.getValue();
33714 getValue : function()
33716 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33718 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33728 this.inputEl.dom.value = '';
33733 validate : function()
33735 var d = this.dayField.validate();
33736 var m = this.monthField.validate();
33737 var y = this.yearField.validate();
33742 (!this.dayAllowBlank && !d) ||
33743 (!this.monthAllowBlank && !m) ||
33744 (!this.yearAllowBlank && !y)
33749 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33758 this.markInvalid();
33763 markValid : function()
33766 var label = this.el.select('label', true).first();
33767 var icon = this.el.select('i.fa-star', true).first();
33773 this.fireEvent('valid', this);
33777 * Mark this field as invalid
33778 * @param {String} msg The validation message
33780 markInvalid : function(msg)
33783 var label = this.el.select('label', true).first();
33784 var icon = this.el.select('i.fa-star', true).first();
33786 if(label && !icon){
33787 this.el.select('.roo-date-split-field-label', true).createChild({
33789 cls : 'text-danger fa fa-lg fa-star',
33790 tooltip : 'This field is required',
33791 style : 'margin-right:5px;'
33795 this.fireEvent('invalid', this, msg);
33798 clearInvalid : function()
33800 var label = this.el.select('label', true).first();
33801 var icon = this.el.select('i.fa-star', true).first();
33807 this.fireEvent('valid', this);
33810 getName: function()
33820 * http://masonry.desandro.com
33822 * The idea is to render all the bricks based on vertical width...
33824 * The original code extends 'outlayer' - we might need to use that....
33830 * @class Roo.bootstrap.LayoutMasonry
33831 * @extends Roo.bootstrap.Component
33832 * Bootstrap Layout Masonry class
33835 * Create a new Element
33836 * @param {Object} config The config object
33839 Roo.bootstrap.LayoutMasonry = function(config){
33841 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33845 Roo.bootstrap.LayoutMasonry.register(this);
33851 * Fire after layout the items
33852 * @param {Roo.bootstrap.LayoutMasonry} this
33853 * @param {Roo.EventObject} e
33860 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33863 * @cfg {Boolean} isLayoutInstant = no animation?
33865 isLayoutInstant : false, // needed?
33868 * @cfg {Number} boxWidth width of the columns
33873 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33878 * @cfg {Number} padWidth padding below box..
33883 * @cfg {Number} gutter gutter width..
33888 * @cfg {Number} maxCols maximum number of columns
33894 * @cfg {Boolean} isAutoInitial defalut true
33896 isAutoInitial : true,
33901 * @cfg {Boolean} isHorizontal defalut false
33903 isHorizontal : false,
33905 currentSize : null,
33911 bricks: null, //CompositeElement
33915 _isLayoutInited : false,
33917 // isAlternative : false, // only use for vertical layout...
33920 * @cfg {Number} alternativePadWidth padding below box..
33922 alternativePadWidth : 50,
33924 selectedBrick : [],
33926 getAutoCreate : function(){
33928 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33932 cls: 'blog-masonary-wrapper ' + this.cls,
33934 cls : 'mas-boxes masonary'
33941 getChildContainer: function( )
33943 if (this.boxesEl) {
33944 return this.boxesEl;
33947 this.boxesEl = this.el.select('.mas-boxes').first();
33949 return this.boxesEl;
33953 initEvents : function()
33957 if(this.isAutoInitial){
33958 Roo.log('hook children rendered');
33959 this.on('childrenrendered', function() {
33960 Roo.log('children rendered');
33966 initial : function()
33968 this.selectedBrick = [];
33970 this.currentSize = this.el.getBox(true);
33972 Roo.EventManager.onWindowResize(this.resize, this);
33974 if(!this.isAutoInitial){
33982 //this.layout.defer(500,this);
33986 resize : function()
33988 var cs = this.el.getBox(true);
33991 this.currentSize.width == cs.width &&
33992 this.currentSize.x == cs.x &&
33993 this.currentSize.height == cs.height &&
33994 this.currentSize.y == cs.y
33996 Roo.log("no change in with or X or Y");
34000 this.currentSize = cs;
34006 layout : function()
34008 this._resetLayout();
34010 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34012 this.layoutItems( isInstant );
34014 this._isLayoutInited = true;
34016 this.fireEvent('layout', this);
34020 _resetLayout : function()
34022 if(this.isHorizontal){
34023 this.horizontalMeasureColumns();
34027 this.verticalMeasureColumns();
34031 verticalMeasureColumns : function()
34033 this.getContainerWidth();
34035 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34036 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34040 var boxWidth = this.boxWidth + this.padWidth;
34042 if(this.containerWidth < this.boxWidth){
34043 boxWidth = this.containerWidth
34046 var containerWidth = this.containerWidth;
34048 var cols = Math.floor(containerWidth / boxWidth);
34050 this.cols = Math.max( cols, 1 );
34052 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34054 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34056 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34058 this.colWidth = boxWidth + avail - this.padWidth;
34060 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34061 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34064 horizontalMeasureColumns : function()
34066 this.getContainerWidth();
34068 var boxWidth = this.boxWidth;
34070 if(this.containerWidth < boxWidth){
34071 boxWidth = this.containerWidth;
34074 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34076 this.el.setHeight(boxWidth);
34080 getContainerWidth : function()
34082 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34085 layoutItems : function( isInstant )
34087 Roo.log(this.bricks);
34089 var items = Roo.apply([], this.bricks);
34091 if(this.isHorizontal){
34092 this._horizontalLayoutItems( items , isInstant );
34096 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34097 // this._verticalAlternativeLayoutItems( items , isInstant );
34101 this._verticalLayoutItems( items , isInstant );
34105 _verticalLayoutItems : function ( items , isInstant)
34107 if ( !items || !items.length ) {
34112 ['xs', 'xs', 'xs', 'tall'],
34113 ['xs', 'xs', 'tall'],
34114 ['xs', 'xs', 'sm'],
34115 ['xs', 'xs', 'xs'],
34121 ['sm', 'xs', 'xs'],
34125 ['tall', 'xs', 'xs', 'xs'],
34126 ['tall', 'xs', 'xs'],
34138 Roo.each(items, function(item, k){
34140 switch (item.size) {
34141 // these layouts take up a full box,
34152 boxes.push([item]);
34175 var filterPattern = function(box, length)
34183 var pattern = box.slice(0, length);
34187 Roo.each(pattern, function(i){
34188 format.push(i.size);
34191 Roo.each(standard, function(s){
34193 if(String(s) != String(format)){
34202 if(!match && length == 1){
34207 filterPattern(box, length - 1);
34211 queue.push(pattern);
34213 box = box.slice(length, box.length);
34215 filterPattern(box, 4);
34221 Roo.each(boxes, function(box, k){
34227 if(box.length == 1){
34232 filterPattern(box, 4);
34236 this._processVerticalLayoutQueue( queue, isInstant );
34240 // _verticalAlternativeLayoutItems : function( items , isInstant )
34242 // if ( !items || !items.length ) {
34246 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34250 _horizontalLayoutItems : function ( items , isInstant)
34252 if ( !items || !items.length || items.length < 3) {
34258 var eItems = items.slice(0, 3);
34260 items = items.slice(3, items.length);
34263 ['xs', 'xs', 'xs', 'wide'],
34264 ['xs', 'xs', 'wide'],
34265 ['xs', 'xs', 'sm'],
34266 ['xs', 'xs', 'xs'],
34272 ['sm', 'xs', 'xs'],
34276 ['wide', 'xs', 'xs', 'xs'],
34277 ['wide', 'xs', 'xs'],
34290 Roo.each(items, function(item, k){
34292 switch (item.size) {
34303 boxes.push([item]);
34327 var filterPattern = function(box, length)
34335 var pattern = box.slice(0, length);
34339 Roo.each(pattern, function(i){
34340 format.push(i.size);
34343 Roo.each(standard, function(s){
34345 if(String(s) != String(format)){
34354 if(!match && length == 1){
34359 filterPattern(box, length - 1);
34363 queue.push(pattern);
34365 box = box.slice(length, box.length);
34367 filterPattern(box, 4);
34373 Roo.each(boxes, function(box, k){
34379 if(box.length == 1){
34384 filterPattern(box, 4);
34391 var pos = this.el.getBox(true);
34395 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34397 var hit_end = false;
34399 Roo.each(queue, function(box){
34403 Roo.each(box, function(b){
34405 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34415 Roo.each(box, function(b){
34417 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34420 mx = Math.max(mx, b.x);
34424 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34428 Roo.each(box, function(b){
34430 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34444 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34447 /** Sets position of item in DOM
34448 * @param {Element} item
34449 * @param {Number} x - horizontal position
34450 * @param {Number} y - vertical position
34451 * @param {Boolean} isInstant - disables transitions
34453 _processVerticalLayoutQueue : function( queue, isInstant )
34455 var pos = this.el.getBox(true);
34460 for (var i = 0; i < this.cols; i++){
34464 Roo.each(queue, function(box, k){
34466 var col = k % this.cols;
34468 Roo.each(box, function(b,kk){
34470 b.el.position('absolute');
34472 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34473 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34475 if(b.size == 'md-left' || b.size == 'md-right'){
34476 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34477 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34480 b.el.setWidth(width);
34481 b.el.setHeight(height);
34483 b.el.select('iframe',true).setSize(width,height);
34487 for (var i = 0; i < this.cols; i++){
34489 if(maxY[i] < maxY[col]){
34494 col = Math.min(col, i);
34498 x = pos.x + col * (this.colWidth + this.padWidth);
34502 var positions = [];
34504 switch (box.length){
34506 positions = this.getVerticalOneBoxColPositions(x, y, box);
34509 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34512 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34515 positions = this.getVerticalFourBoxColPositions(x, y, box);
34521 Roo.each(box, function(b,kk){
34523 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34525 var sz = b.el.getSize();
34527 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34535 for (var i = 0; i < this.cols; i++){
34536 mY = Math.max(mY, maxY[i]);
34539 this.el.setHeight(mY - pos.y);
34543 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34545 // var pos = this.el.getBox(true);
34548 // var maxX = pos.right;
34550 // var maxHeight = 0;
34552 // Roo.each(items, function(item, k){
34556 // item.el.position('absolute');
34558 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34560 // item.el.setWidth(width);
34562 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34564 // item.el.setHeight(height);
34567 // item.el.setXY([x, y], isInstant ? false : true);
34569 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34572 // y = y + height + this.alternativePadWidth;
34574 // maxHeight = maxHeight + height + this.alternativePadWidth;
34578 // this.el.setHeight(maxHeight);
34582 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34584 var pos = this.el.getBox(true);
34589 var maxX = pos.right;
34591 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34593 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34595 Roo.each(queue, function(box, k){
34597 Roo.each(box, function(b, kk){
34599 b.el.position('absolute');
34601 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34602 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34604 if(b.size == 'md-left' || b.size == 'md-right'){
34605 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34606 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34609 b.el.setWidth(width);
34610 b.el.setHeight(height);
34618 var positions = [];
34620 switch (box.length){
34622 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34625 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34628 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34631 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34637 Roo.each(box, function(b,kk){
34639 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34641 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34649 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34651 Roo.each(eItems, function(b,k){
34653 b.size = (k == 0) ? 'sm' : 'xs';
34654 b.x = (k == 0) ? 2 : 1;
34655 b.y = (k == 0) ? 2 : 1;
34657 b.el.position('absolute');
34659 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34661 b.el.setWidth(width);
34663 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34665 b.el.setHeight(height);
34669 var positions = [];
34672 x : maxX - this.unitWidth * 2 - this.gutter,
34677 x : maxX - this.unitWidth,
34678 y : minY + (this.unitWidth + this.gutter) * 2
34682 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34686 Roo.each(eItems, function(b,k){
34688 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34694 getVerticalOneBoxColPositions : function(x, y, box)
34698 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34700 if(box[0].size == 'md-left'){
34704 if(box[0].size == 'md-right'){
34709 x : x + (this.unitWidth + this.gutter) * rand,
34716 getVerticalTwoBoxColPositions : function(x, y, box)
34720 if(box[0].size == 'xs'){
34724 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34728 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34742 x : x + (this.unitWidth + this.gutter) * 2,
34743 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34750 getVerticalThreeBoxColPositions : function(x, y, box)
34754 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34762 x : x + (this.unitWidth + this.gutter) * 1,
34767 x : x + (this.unitWidth + this.gutter) * 2,
34775 if(box[0].size == 'xs' && box[1].size == 'xs'){
34784 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34788 x : x + (this.unitWidth + this.gutter) * 1,
34802 x : x + (this.unitWidth + this.gutter) * 2,
34807 x : x + (this.unitWidth + this.gutter) * 2,
34808 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34815 getVerticalFourBoxColPositions : function(x, y, box)
34819 if(box[0].size == 'xs'){
34828 y : y + (this.unitHeight + this.gutter) * 1
34833 y : y + (this.unitHeight + this.gutter) * 2
34837 x : x + (this.unitWidth + this.gutter) * 1,
34851 x : x + (this.unitWidth + this.gutter) * 2,
34856 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34857 y : y + (this.unitHeight + this.gutter) * 1
34861 x : x + (this.unitWidth + this.gutter) * 2,
34862 y : y + (this.unitWidth + this.gutter) * 2
34869 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34873 if(box[0].size == 'md-left'){
34875 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34882 if(box[0].size == 'md-right'){
34884 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34885 y : minY + (this.unitWidth + this.gutter) * 1
34891 var rand = Math.floor(Math.random() * (4 - box[0].y));
34894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895 y : minY + (this.unitWidth + this.gutter) * rand
34902 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34906 if(box[0].size == 'xs'){
34909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34915 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
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) * 2
34936 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34940 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34943 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34948 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34949 y : minY + (this.unitWidth + this.gutter) * 1
34953 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34954 y : minY + (this.unitWidth + this.gutter) * 2
34961 if(box[0].size == 'xs' && box[1].size == 'xs'){
34964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34969 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34974 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34975 y : minY + (this.unitWidth + this.gutter) * 1
34983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34988 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34989 y : minY + (this.unitWidth + this.gutter) * 2
34993 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34994 y : minY + (this.unitWidth + this.gutter) * 2
35001 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35005 if(box[0].size == 'xs'){
35008 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35013 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35018 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),
35023 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35024 y : minY + (this.unitWidth + this.gutter) * 1
35032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35037 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35038 y : minY + (this.unitWidth + this.gutter) * 2
35042 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35043 y : minY + (this.unitWidth + this.gutter) * 2
35047 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),
35048 y : minY + (this.unitWidth + this.gutter) * 2
35056 * remove a Masonry Brick
35057 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35059 removeBrick : function(brick_id)
35065 for (var i = 0; i<this.bricks.length; i++) {
35066 if (this.bricks[i].id == brick_id) {
35067 this.bricks.splice(i,1);
35068 this.el.dom.removeChild(Roo.get(brick_id).dom);
35075 * adds a Masonry Brick
35076 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35078 addBrick : function(cfg)
35080 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35081 //this.register(cn);
35082 cn.parentId = this.id;
35083 cn.render(this.el);
35088 * register a Masonry Brick
35089 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35092 register : function(brick)
35094 this.bricks.push(brick);
35095 brick.masonryId = this.id;
35099 * clear all the Masonry Brick
35101 clearAll : function()
35104 //this.getChildContainer().dom.innerHTML = "";
35105 this.el.dom.innerHTML = '';
35108 getSelected : function()
35110 if (!this.selectedBrick) {
35114 return this.selectedBrick;
35118 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35122 * register a Masonry Layout
35123 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35126 register : function(layout)
35128 this.groups[layout.id] = layout;
35131 * fetch a Masonry Layout based on the masonry layout ID
35132 * @param {string} the masonry layout to add
35133 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35136 get: function(layout_id) {
35137 if (typeof(this.groups[layout_id]) == 'undefined') {
35140 return this.groups[layout_id] ;
35152 * http://masonry.desandro.com
35154 * The idea is to render all the bricks based on vertical width...
35156 * The original code extends 'outlayer' - we might need to use that....
35162 * @class Roo.bootstrap.LayoutMasonryAuto
35163 * @extends Roo.bootstrap.Component
35164 * Bootstrap Layout Masonry class
35167 * Create a new Element
35168 * @param {Object} config The config object
35171 Roo.bootstrap.LayoutMasonryAuto = function(config){
35172 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35175 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35178 * @cfg {Boolean} isFitWidth - resize the width..
35180 isFitWidth : false, // options..
35182 * @cfg {Boolean} isOriginLeft = left align?
35184 isOriginLeft : true,
35186 * @cfg {Boolean} isOriginTop = top align?
35188 isOriginTop : false,
35190 * @cfg {Boolean} isLayoutInstant = no animation?
35192 isLayoutInstant : false, // needed?
35194 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35196 isResizingContainer : true,
35198 * @cfg {Number} columnWidth width of the columns
35204 * @cfg {Number} maxCols maximum number of columns
35209 * @cfg {Number} padHeight padding below box..
35215 * @cfg {Boolean} isAutoInitial defalut true
35218 isAutoInitial : true,
35224 initialColumnWidth : 0,
35225 currentSize : null,
35227 colYs : null, // array.
35234 bricks: null, //CompositeElement
35235 cols : 0, // array?
35236 // element : null, // wrapped now this.el
35237 _isLayoutInited : null,
35240 getAutoCreate : function(){
35244 cls: 'blog-masonary-wrapper ' + this.cls,
35246 cls : 'mas-boxes masonary'
35253 getChildContainer: function( )
35255 if (this.boxesEl) {
35256 return this.boxesEl;
35259 this.boxesEl = this.el.select('.mas-boxes').first();
35261 return this.boxesEl;
35265 initEvents : function()
35269 if(this.isAutoInitial){
35270 Roo.log('hook children rendered');
35271 this.on('childrenrendered', function() {
35272 Roo.log('children rendered');
35279 initial : function()
35281 this.reloadItems();
35283 this.currentSize = this.el.getBox(true);
35285 /// was window resize... - let's see if this works..
35286 Roo.EventManager.onWindowResize(this.resize, this);
35288 if(!this.isAutoInitial){
35293 this.layout.defer(500,this);
35296 reloadItems: function()
35298 this.bricks = this.el.select('.masonry-brick', true);
35300 this.bricks.each(function(b) {
35301 //Roo.log(b.getSize());
35302 if (!b.attr('originalwidth')) {
35303 b.attr('originalwidth', b.getSize().width);
35308 Roo.log(this.bricks.elements.length);
35311 resize : function()
35314 var cs = this.el.getBox(true);
35316 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35317 Roo.log("no change in with or X");
35320 this.currentSize = cs;
35324 layout : function()
35327 this._resetLayout();
35328 //this._manageStamps();
35330 // don't animate first layout
35331 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35332 this.layoutItems( isInstant );
35334 // flag for initalized
35335 this._isLayoutInited = true;
35338 layoutItems : function( isInstant )
35340 //var items = this._getItemsForLayout( this.items );
35341 // original code supports filtering layout items.. we just ignore it..
35343 this._layoutItems( this.bricks , isInstant );
35345 this._postLayout();
35347 _layoutItems : function ( items , isInstant)
35349 //this.fireEvent( 'layout', this, items );
35352 if ( !items || !items.elements.length ) {
35353 // no items, emit event with empty array
35358 items.each(function(item) {
35359 Roo.log("layout item");
35361 // get x/y object from method
35362 var position = this._getItemLayoutPosition( item );
35364 position.item = item;
35365 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35366 queue.push( position );
35369 this._processLayoutQueue( queue );
35371 /** Sets position of item in DOM
35372 * @param {Element} item
35373 * @param {Number} x - horizontal position
35374 * @param {Number} y - vertical position
35375 * @param {Boolean} isInstant - disables transitions
35377 _processLayoutQueue : function( queue )
35379 for ( var i=0, len = queue.length; i < len; i++ ) {
35380 var obj = queue[i];
35381 obj.item.position('absolute');
35382 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35388 * Any logic you want to do after each layout,
35389 * i.e. size the container
35391 _postLayout : function()
35393 this.resizeContainer();
35396 resizeContainer : function()
35398 if ( !this.isResizingContainer ) {
35401 var size = this._getContainerSize();
35403 this.el.setSize(size.width,size.height);
35404 this.boxesEl.setSize(size.width,size.height);
35410 _resetLayout : function()
35412 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35413 this.colWidth = this.el.getWidth();
35414 //this.gutter = this.el.getWidth();
35416 this.measureColumns();
35422 this.colYs.push( 0 );
35428 measureColumns : function()
35430 this.getContainerWidth();
35431 // if columnWidth is 0, default to outerWidth of first item
35432 if ( !this.columnWidth ) {
35433 var firstItem = this.bricks.first();
35434 Roo.log(firstItem);
35435 this.columnWidth = this.containerWidth;
35436 if (firstItem && firstItem.attr('originalwidth') ) {
35437 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35439 // columnWidth fall back to item of first element
35440 Roo.log("set column width?");
35441 this.initialColumnWidth = this.columnWidth ;
35443 // if first elem has no width, default to size of container
35448 if (this.initialColumnWidth) {
35449 this.columnWidth = this.initialColumnWidth;
35454 // column width is fixed at the top - however if container width get's smaller we should
35457 // this bit calcs how man columns..
35459 var columnWidth = this.columnWidth += this.gutter;
35461 // calculate columns
35462 var containerWidth = this.containerWidth + this.gutter;
35464 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35465 // fix rounding errors, typically with gutters
35466 var excess = columnWidth - containerWidth % columnWidth;
35469 // if overshoot is less than a pixel, round up, otherwise floor it
35470 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35471 cols = Math[ mathMethod ]( cols );
35472 this.cols = Math.max( cols, 1 );
35473 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35475 // padding positioning..
35476 var totalColWidth = this.cols * this.columnWidth;
35477 var padavail = this.containerWidth - totalColWidth;
35478 // so for 2 columns - we need 3 'pads'
35480 var padNeeded = (1+this.cols) * this.padWidth;
35482 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35484 this.columnWidth += padExtra
35485 //this.padWidth = Math.floor(padavail / ( this.cols));
35487 // adjust colum width so that padding is fixed??
35489 // we have 3 columns ... total = width * 3
35490 // we have X left over... that should be used by
35492 //if (this.expandC) {
35500 getContainerWidth : function()
35502 /* // container is parent if fit width
35503 var container = this.isFitWidth ? this.element.parentNode : this.element;
35504 // check that this.size and size are there
35505 // IE8 triggers resize on body size change, so they might not be
35507 var size = getSize( container ); //FIXME
35508 this.containerWidth = size && size.innerWidth; //FIXME
35511 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35515 _getItemLayoutPosition : function( item ) // what is item?
35517 // we resize the item to our columnWidth..
35519 item.setWidth(this.columnWidth);
35520 item.autoBoxAdjust = false;
35522 var sz = item.getSize();
35524 // how many columns does this brick span
35525 var remainder = this.containerWidth % this.columnWidth;
35527 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35528 // round if off by 1 pixel, otherwise use ceil
35529 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35530 colSpan = Math.min( colSpan, this.cols );
35532 // normally this should be '1' as we dont' currently allow multi width columns..
35534 var colGroup = this._getColGroup( colSpan );
35535 // get the minimum Y value from the columns
35536 var minimumY = Math.min.apply( Math, colGroup );
35537 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35539 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35541 // position the brick
35543 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35544 y: this.currentSize.y + minimumY + this.padHeight
35548 // apply setHeight to necessary columns
35549 var setHeight = minimumY + sz.height + this.padHeight;
35550 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35552 var setSpan = this.cols + 1 - colGroup.length;
35553 for ( var i = 0; i < setSpan; i++ ) {
35554 this.colYs[ shortColIndex + i ] = setHeight ;
35561 * @param {Number} colSpan - number of columns the element spans
35562 * @returns {Array} colGroup
35564 _getColGroup : function( colSpan )
35566 if ( colSpan < 2 ) {
35567 // if brick spans only one column, use all the column Ys
35572 // how many different places could this brick fit horizontally
35573 var groupCount = this.cols + 1 - colSpan;
35574 // for each group potential horizontal position
35575 for ( var i = 0; i < groupCount; i++ ) {
35576 // make an array of colY values for that one group
35577 var groupColYs = this.colYs.slice( i, i + colSpan );
35578 // and get the max value of the array
35579 colGroup[i] = Math.max.apply( Math, groupColYs );
35584 _manageStamp : function( stamp )
35586 var stampSize = stamp.getSize();
35587 var offset = stamp.getBox();
35588 // get the columns that this stamp affects
35589 var firstX = this.isOriginLeft ? offset.x : offset.right;
35590 var lastX = firstX + stampSize.width;
35591 var firstCol = Math.floor( firstX / this.columnWidth );
35592 firstCol = Math.max( 0, firstCol );
35594 var lastCol = Math.floor( lastX / this.columnWidth );
35595 // lastCol should not go over if multiple of columnWidth #425
35596 lastCol -= lastX % this.columnWidth ? 0 : 1;
35597 lastCol = Math.min( this.cols - 1, lastCol );
35599 // set colYs to bottom of the stamp
35600 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35603 for ( var i = firstCol; i <= lastCol; i++ ) {
35604 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35609 _getContainerSize : function()
35611 this.maxY = Math.max.apply( Math, this.colYs );
35616 if ( this.isFitWidth ) {
35617 size.width = this._getContainerFitWidth();
35623 _getContainerFitWidth : function()
35625 var unusedCols = 0;
35626 // count unused columns
35629 if ( this.colYs[i] !== 0 ) {
35634 // fit container to columns that have been used
35635 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35638 needsResizeLayout : function()
35640 var previousWidth = this.containerWidth;
35641 this.getContainerWidth();
35642 return previousWidth !== this.containerWidth;
35657 * @class Roo.bootstrap.MasonryBrick
35658 * @extends Roo.bootstrap.Component
35659 * Bootstrap MasonryBrick class
35662 * Create a new MasonryBrick
35663 * @param {Object} config The config object
35666 Roo.bootstrap.MasonryBrick = function(config){
35668 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35670 Roo.bootstrap.MasonryBrick.register(this);
35676 * When a MasonryBrick is clcik
35677 * @param {Roo.bootstrap.MasonryBrick} this
35678 * @param {Roo.EventObject} e
35684 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35687 * @cfg {String} title
35691 * @cfg {String} html
35695 * @cfg {String} bgimage
35699 * @cfg {String} videourl
35703 * @cfg {String} cls
35707 * @cfg {String} href
35711 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35716 * @cfg {String} placetitle (center|bottom)
35721 * @cfg {Boolean} isFitContainer defalut true
35723 isFitContainer : true,
35726 * @cfg {Boolean} preventDefault defalut false
35728 preventDefault : false,
35731 * @cfg {Boolean} inverse defalut false
35733 maskInverse : false,
35735 getAutoCreate : function()
35737 if(!this.isFitContainer){
35738 return this.getSplitAutoCreate();
35741 var cls = 'masonry-brick masonry-brick-full';
35743 if(this.href.length){
35744 cls += ' masonry-brick-link';
35747 if(this.bgimage.length){
35748 cls += ' masonry-brick-image';
35751 if(this.maskInverse){
35752 cls += ' mask-inverse';
35755 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35756 cls += ' enable-mask';
35760 cls += ' masonry-' + this.size + '-brick';
35763 if(this.placetitle.length){
35765 switch (this.placetitle) {
35767 cls += ' masonry-center-title';
35770 cls += ' masonry-bottom-title';
35777 if(!this.html.length && !this.bgimage.length){
35778 cls += ' masonry-center-title';
35781 if(!this.html.length && this.bgimage.length){
35782 cls += ' masonry-bottom-title';
35787 cls += ' ' + this.cls;
35791 tag: (this.href.length) ? 'a' : 'div',
35796 cls: 'masonry-brick-mask'
35800 cls: 'masonry-brick-paragraph',
35806 if(this.href.length){
35807 cfg.href = this.href;
35810 var cn = cfg.cn[1].cn;
35812 if(this.title.length){
35815 cls: 'masonry-brick-title',
35820 if(this.html.length){
35823 cls: 'masonry-brick-text',
35828 if (!this.title.length && !this.html.length) {
35829 cfg.cn[1].cls += ' hide';
35832 if(this.bgimage.length){
35835 cls: 'masonry-brick-image-view',
35840 if(this.videourl.length){
35841 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35842 // youtube support only?
35845 cls: 'masonry-brick-image-view',
35848 allowfullscreen : true
35856 getSplitAutoCreate : function()
35858 var cls = 'masonry-brick masonry-brick-split';
35860 if(this.href.length){
35861 cls += ' masonry-brick-link';
35864 if(this.bgimage.length){
35865 cls += ' masonry-brick-image';
35869 cls += ' masonry-' + this.size + '-brick';
35872 switch (this.placetitle) {
35874 cls += ' masonry-center-title';
35877 cls += ' masonry-bottom-title';
35880 if(!this.bgimage.length){
35881 cls += ' masonry-center-title';
35884 if(this.bgimage.length){
35885 cls += ' masonry-bottom-title';
35891 cls += ' ' + this.cls;
35895 tag: (this.href.length) ? 'a' : 'div',
35900 cls: 'masonry-brick-split-head',
35904 cls: 'masonry-brick-paragraph',
35911 cls: 'masonry-brick-split-body',
35917 if(this.href.length){
35918 cfg.href = this.href;
35921 if(this.title.length){
35922 cfg.cn[0].cn[0].cn.push({
35924 cls: 'masonry-brick-title',
35929 if(this.html.length){
35930 cfg.cn[1].cn.push({
35932 cls: 'masonry-brick-text',
35937 if(this.bgimage.length){
35938 cfg.cn[0].cn.push({
35940 cls: 'masonry-brick-image-view',
35945 if(this.videourl.length){
35946 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35947 // youtube support only?
35948 cfg.cn[0].cn.cn.push({
35950 cls: 'masonry-brick-image-view',
35953 allowfullscreen : true
35960 initEvents: function()
35962 switch (this.size) {
35995 this.el.on('touchstart', this.onTouchStart, this);
35996 this.el.on('touchmove', this.onTouchMove, this);
35997 this.el.on('touchend', this.onTouchEnd, this);
35998 this.el.on('contextmenu', this.onContextMenu, this);
36000 this.el.on('mouseenter' ,this.enter, this);
36001 this.el.on('mouseleave', this.leave, this);
36002 this.el.on('click', this.onClick, this);
36005 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36006 this.parent().bricks.push(this);
36011 onClick: function(e, el)
36013 var time = this.endTimer - this.startTimer;
36014 // Roo.log(e.preventDefault());
36017 e.preventDefault();
36022 if(!this.preventDefault){
36026 e.preventDefault();
36028 if (this.activeClass != '') {
36029 this.selectBrick();
36032 this.fireEvent('click', this, e);
36035 enter: function(e, el)
36037 e.preventDefault();
36039 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36043 if(this.bgimage.length && this.html.length){
36044 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36048 leave: function(e, el)
36050 e.preventDefault();
36052 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36056 if(this.bgimage.length && this.html.length){
36057 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36061 onTouchStart: function(e, el)
36063 // e.preventDefault();
36065 this.touchmoved = false;
36067 if(!this.isFitContainer){
36071 if(!this.bgimage.length || !this.html.length){
36075 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36077 this.timer = new Date().getTime();
36081 onTouchMove: function(e, el)
36083 this.touchmoved = true;
36086 onContextMenu : function(e,el)
36088 e.preventDefault();
36089 e.stopPropagation();
36093 onTouchEnd: function(e, el)
36095 // e.preventDefault();
36097 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36104 if(!this.bgimage.length || !this.html.length){
36106 if(this.href.length){
36107 window.location.href = this.href;
36113 if(!this.isFitContainer){
36117 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36119 window.location.href = this.href;
36122 //selection on single brick only
36123 selectBrick : function() {
36125 if (!this.parentId) {
36129 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36130 var index = m.selectedBrick.indexOf(this.id);
36133 m.selectedBrick.splice(index,1);
36134 this.el.removeClass(this.activeClass);
36138 for(var i = 0; i < m.selectedBrick.length; i++) {
36139 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36140 b.el.removeClass(b.activeClass);
36143 m.selectedBrick = [];
36145 m.selectedBrick.push(this.id);
36146 this.el.addClass(this.activeClass);
36150 isSelected : function(){
36151 return this.el.hasClass(this.activeClass);
36156 Roo.apply(Roo.bootstrap.MasonryBrick, {
36159 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36161 * register a Masonry Brick
36162 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36165 register : function(brick)
36167 //this.groups[brick.id] = brick;
36168 this.groups.add(brick.id, brick);
36171 * fetch a masonry brick based on the masonry brick ID
36172 * @param {string} the masonry brick to add
36173 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36176 get: function(brick_id)
36178 // if (typeof(this.groups[brick_id]) == 'undefined') {
36181 // return this.groups[brick_id] ;
36183 if(this.groups.key(brick_id)) {
36184 return this.groups.key(brick_id);
36202 * @class Roo.bootstrap.Brick
36203 * @extends Roo.bootstrap.Component
36204 * Bootstrap Brick class
36207 * Create a new Brick
36208 * @param {Object} config The config object
36211 Roo.bootstrap.Brick = function(config){
36212 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36218 * When a Brick is click
36219 * @param {Roo.bootstrap.Brick} this
36220 * @param {Roo.EventObject} e
36226 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36229 * @cfg {String} title
36233 * @cfg {String} html
36237 * @cfg {String} bgimage
36241 * @cfg {String} cls
36245 * @cfg {String} href
36249 * @cfg {String} video
36253 * @cfg {Boolean} square
36257 getAutoCreate : function()
36259 var cls = 'roo-brick';
36261 if(this.href.length){
36262 cls += ' roo-brick-link';
36265 if(this.bgimage.length){
36266 cls += ' roo-brick-image';
36269 if(!this.html.length && !this.bgimage.length){
36270 cls += ' roo-brick-center-title';
36273 if(!this.html.length && this.bgimage.length){
36274 cls += ' roo-brick-bottom-title';
36278 cls += ' ' + this.cls;
36282 tag: (this.href.length) ? 'a' : 'div',
36287 cls: 'roo-brick-paragraph',
36293 if(this.href.length){
36294 cfg.href = this.href;
36297 var cn = cfg.cn[0].cn;
36299 if(this.title.length){
36302 cls: 'roo-brick-title',
36307 if(this.html.length){
36310 cls: 'roo-brick-text',
36317 if(this.bgimage.length){
36320 cls: 'roo-brick-image-view',
36328 initEvents: function()
36330 if(this.title.length || this.html.length){
36331 this.el.on('mouseenter' ,this.enter, this);
36332 this.el.on('mouseleave', this.leave, this);
36335 Roo.EventManager.onWindowResize(this.resize, this);
36337 if(this.bgimage.length){
36338 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36339 this.imageEl.on('load', this.onImageLoad, this);
36346 onImageLoad : function()
36351 resize : function()
36353 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36355 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36357 if(this.bgimage.length){
36358 var image = this.el.select('.roo-brick-image-view', true).first();
36360 image.setWidth(paragraph.getWidth());
36363 image.setHeight(paragraph.getWidth());
36366 this.el.setHeight(image.getHeight());
36367 paragraph.setHeight(image.getHeight());
36373 enter: function(e, el)
36375 e.preventDefault();
36377 if(this.bgimage.length){
36378 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36379 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36383 leave: function(e, el)
36385 e.preventDefault();
36387 if(this.bgimage.length){
36388 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36389 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36404 * @class Roo.bootstrap.NumberField
36405 * @extends Roo.bootstrap.Input
36406 * Bootstrap NumberField class
36412 * Create a new NumberField
36413 * @param {Object} config The config object
36416 Roo.bootstrap.NumberField = function(config){
36417 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36420 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36423 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36425 allowDecimals : true,
36427 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36429 decimalSeparator : ".",
36431 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36433 decimalPrecision : 2,
36435 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36437 allowNegative : true,
36440 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36444 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36446 minValue : Number.NEGATIVE_INFINITY,
36448 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36450 maxValue : Number.MAX_VALUE,
36452 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36454 minText : "The minimum value for this field is {0}",
36456 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36458 maxText : "The maximum value for this field is {0}",
36460 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36461 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36463 nanText : "{0} is not a valid number",
36465 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36467 thousandsDelimiter : false,
36469 * @cfg {String} valueAlign alignment of value
36471 valueAlign : "left",
36473 getAutoCreate : function()
36475 var hiddenInput = {
36479 cls: 'hidden-number-input'
36483 hiddenInput.name = this.name;
36488 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36490 this.name = hiddenInput.name;
36492 if(cfg.cn.length > 0) {
36493 cfg.cn.push(hiddenInput);
36500 initEvents : function()
36502 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36504 var allowed = "0123456789";
36506 if(this.allowDecimals){
36507 allowed += this.decimalSeparator;
36510 if(this.allowNegative){
36514 if(this.thousandsDelimiter) {
36518 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36520 var keyPress = function(e){
36522 var k = e.getKey();
36524 var c = e.getCharCode();
36527 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36528 allowed.indexOf(String.fromCharCode(c)) === -1
36534 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36538 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36543 this.el.on("keypress", keyPress, this);
36546 validateValue : function(value)
36549 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36553 var num = this.parseValue(value);
36556 this.markInvalid(String.format(this.nanText, value));
36560 if(num < this.minValue){
36561 this.markInvalid(String.format(this.minText, this.minValue));
36565 if(num > this.maxValue){
36566 this.markInvalid(String.format(this.maxText, this.maxValue));
36573 getValue : function()
36575 var v = this.hiddenEl().getValue();
36577 return this.fixPrecision(this.parseValue(v));
36580 parseValue : function(value)
36582 if(this.thousandsDelimiter) {
36584 r = new RegExp(",", "g");
36585 value = value.replace(r, "");
36588 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36589 return isNaN(value) ? '' : value;
36592 fixPrecision : function(value)
36594 if(this.thousandsDelimiter) {
36596 r = new RegExp(",", "g");
36597 value = value.replace(r, "");
36600 var nan = isNaN(value);
36602 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36603 return nan ? '' : value;
36605 return parseFloat(value).toFixed(this.decimalPrecision);
36608 setValue : function(v)
36610 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36616 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36618 this.inputEl().dom.value = (v == '') ? '' :
36619 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36621 if(!this.allowZero && v === '0') {
36622 this.hiddenEl().dom.value = '';
36623 this.inputEl().dom.value = '';
36630 decimalPrecisionFcn : function(v)
36632 return Math.floor(v);
36635 beforeBlur : function()
36637 var v = this.parseValue(this.getRawValue());
36639 if(v || v === 0 || v === ''){
36644 hiddenEl : function()
36646 return this.el.select('input.hidden-number-input',true).first();
36658 * @class Roo.bootstrap.DocumentSlider
36659 * @extends Roo.bootstrap.Component
36660 * Bootstrap DocumentSlider class
36663 * Create a new DocumentViewer
36664 * @param {Object} config The config object
36667 Roo.bootstrap.DocumentSlider = function(config){
36668 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36675 * Fire after initEvent
36676 * @param {Roo.bootstrap.DocumentSlider} this
36681 * Fire after update
36682 * @param {Roo.bootstrap.DocumentSlider} this
36688 * @param {Roo.bootstrap.DocumentSlider} this
36694 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36700 getAutoCreate : function()
36704 cls : 'roo-document-slider',
36708 cls : 'roo-document-slider-header',
36712 cls : 'roo-document-slider-header-title'
36718 cls : 'roo-document-slider-body',
36722 cls : 'roo-document-slider-prev',
36726 cls : 'fa fa-chevron-left'
36732 cls : 'roo-document-slider-thumb',
36736 cls : 'roo-document-slider-image'
36742 cls : 'roo-document-slider-next',
36746 cls : 'fa fa-chevron-right'
36758 initEvents : function()
36760 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36761 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36763 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36764 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36766 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36767 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36769 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36770 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36772 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36773 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36775 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36776 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36778 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36779 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36781 this.thumbEl.on('click', this.onClick, this);
36783 this.prevIndicator.on('click', this.prev, this);
36785 this.nextIndicator.on('click', this.next, this);
36789 initial : function()
36791 if(this.files.length){
36792 this.indicator = 1;
36796 this.fireEvent('initial', this);
36799 update : function()
36801 this.imageEl.attr('src', this.files[this.indicator - 1]);
36803 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36805 this.prevIndicator.show();
36807 if(this.indicator == 1){
36808 this.prevIndicator.hide();
36811 this.nextIndicator.show();
36813 if(this.indicator == this.files.length){
36814 this.nextIndicator.hide();
36817 this.thumbEl.scrollTo('top');
36819 this.fireEvent('update', this);
36822 onClick : function(e)
36824 e.preventDefault();
36826 this.fireEvent('click', this);
36831 e.preventDefault();
36833 this.indicator = Math.max(1, this.indicator - 1);
36840 e.preventDefault();
36842 this.indicator = Math.min(this.files.length, this.indicator + 1);
36856 * @class Roo.bootstrap.RadioSet
36857 * @extends Roo.bootstrap.Input
36858 * Bootstrap RadioSet class
36859 * @cfg {String} indicatorpos (left|right) default left
36860 * @cfg {Boolean} inline (true|false) inline the element (default true)
36861 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36863 * Create a new RadioSet
36864 * @param {Object} config The config object
36867 Roo.bootstrap.RadioSet = function(config){
36869 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36873 Roo.bootstrap.RadioSet.register(this);
36878 * Fires when the element is checked or unchecked.
36879 * @param {Roo.bootstrap.RadioSet} this This radio
36880 * @param {Roo.bootstrap.Radio} item The checked item
36885 * Fires when the element is click.
36886 * @param {Roo.bootstrap.RadioSet} this This radio set
36887 * @param {Roo.bootstrap.Radio} item The checked item
36888 * @param {Roo.EventObject} e The event object
36895 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36903 indicatorpos : 'left',
36905 getAutoCreate : function()
36909 cls : 'roo-radio-set-label',
36913 html : this.fieldLabel
36917 if (Roo.bootstrap.version == 3) {
36920 if(this.indicatorpos == 'left'){
36923 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36924 tooltip : 'This field is required'
36929 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36930 tooltip : 'This field is required'
36936 cls : 'roo-radio-set-items'
36939 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36941 if (align === 'left' && this.fieldLabel.length) {
36944 cls : "roo-radio-set-right",
36950 if(this.labelWidth > 12){
36951 label.style = "width: " + this.labelWidth + 'px';
36954 if(this.labelWidth < 13 && this.labelmd == 0){
36955 this.labelmd = this.labelWidth;
36958 if(this.labellg > 0){
36959 label.cls += ' col-lg-' + this.labellg;
36960 items.cls += ' col-lg-' + (12 - this.labellg);
36963 if(this.labelmd > 0){
36964 label.cls += ' col-md-' + this.labelmd;
36965 items.cls += ' col-md-' + (12 - this.labelmd);
36968 if(this.labelsm > 0){
36969 label.cls += ' col-sm-' + this.labelsm;
36970 items.cls += ' col-sm-' + (12 - this.labelsm);
36973 if(this.labelxs > 0){
36974 label.cls += ' col-xs-' + this.labelxs;
36975 items.cls += ' col-xs-' + (12 - this.labelxs);
36981 cls : 'roo-radio-set',
36985 cls : 'roo-radio-set-input',
36988 value : this.value ? this.value : ''
36995 if(this.weight.length){
36996 cfg.cls += ' roo-radio-' + this.weight;
37000 cfg.cls += ' roo-radio-set-inline';
37004 ['xs','sm','md','lg'].map(function(size){
37005 if (settings[size]) {
37006 cfg.cls += ' col-' + size + '-' + settings[size];
37014 initEvents : function()
37016 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37017 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37019 if(!this.fieldLabel.length){
37020 this.labelEl.hide();
37023 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37024 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37026 this.indicator = this.indicatorEl();
37028 if(this.indicator){
37029 this.indicator.addClass('invisible');
37032 this.originalValue = this.getValue();
37036 inputEl: function ()
37038 return this.el.select('.roo-radio-set-input', true).first();
37041 getChildContainer : function()
37043 return this.itemsEl;
37046 register : function(item)
37048 this.radioes.push(item);
37052 validate : function()
37054 if(this.getVisibilityEl().hasClass('hidden')){
37060 Roo.each(this.radioes, function(i){
37069 if(this.allowBlank) {
37073 if(this.disabled || valid){
37078 this.markInvalid();
37083 markValid : function()
37085 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37086 this.indicatorEl().removeClass('visible');
37087 this.indicatorEl().addClass('invisible');
37091 if (Roo.bootstrap.version == 3) {
37092 this.el.removeClass([this.invalidClass, this.validClass]);
37093 this.el.addClass(this.validClass);
37095 this.el.removeClass(['is-invalid','is-valid']);
37096 this.el.addClass(['is-valid']);
37098 this.fireEvent('valid', this);
37101 markInvalid : function(msg)
37103 if(this.allowBlank || this.disabled){
37107 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37108 this.indicatorEl().removeClass('invisible');
37109 this.indicatorEl().addClass('visible');
37111 if (Roo.bootstrap.version == 3) {
37112 this.el.removeClass([this.invalidClass, this.validClass]);
37113 this.el.addClass(this.invalidClass);
37115 this.el.removeClass(['is-invalid','is-valid']);
37116 this.el.addClass(['is-invalid']);
37119 this.fireEvent('invalid', this, msg);
37123 setValue : function(v, suppressEvent)
37125 if(this.value === v){
37132 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37135 Roo.each(this.radioes, function(i){
37137 i.el.removeClass('checked');
37140 Roo.each(this.radioes, function(i){
37142 if(i.value === v || i.value.toString() === v.toString()){
37144 i.el.addClass('checked');
37146 if(suppressEvent !== true){
37147 this.fireEvent('check', this, i);
37158 clearInvalid : function(){
37160 if(!this.el || this.preventMark){
37164 this.el.removeClass([this.invalidClass]);
37166 this.fireEvent('valid', this);
37171 Roo.apply(Roo.bootstrap.RadioSet, {
37175 register : function(set)
37177 this.groups[set.name] = set;
37180 get: function(name)
37182 if (typeof(this.groups[name]) == 'undefined') {
37186 return this.groups[name] ;
37192 * Ext JS Library 1.1.1
37193 * Copyright(c) 2006-2007, Ext JS, LLC.
37195 * Originally Released Under LGPL - original licence link has changed is not relivant.
37198 * <script type="text/javascript">
37203 * @class Roo.bootstrap.SplitBar
37204 * @extends Roo.util.Observable
37205 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37209 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37210 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37211 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37212 split.minSize = 100;
37213 split.maxSize = 600;
37214 split.animate = true;
37215 split.on('moved', splitterMoved);
37218 * Create a new SplitBar
37219 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37220 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37221 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37222 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37223 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37224 position of the SplitBar).
37226 Roo.bootstrap.SplitBar = function(cfg){
37231 // dragElement : elm
37232 // resizingElement: el,
37234 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37235 // placement : Roo.bootstrap.SplitBar.LEFT ,
37236 // existingProxy ???
37239 this.el = Roo.get(cfg.dragElement, true);
37240 this.el.dom.unselectable = "on";
37242 this.resizingEl = Roo.get(cfg.resizingElement, true);
37246 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37247 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37250 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37253 * The minimum size of the resizing element. (Defaults to 0)
37259 * The maximum size of the resizing element. (Defaults to 2000)
37262 this.maxSize = 2000;
37265 * Whether to animate the transition to the new size
37268 this.animate = false;
37271 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37274 this.useShim = false;
37279 if(!cfg.existingProxy){
37281 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37283 this.proxy = Roo.get(cfg.existingProxy).dom;
37286 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37289 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37292 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37295 this.dragSpecs = {};
37298 * @private The adapter to use to positon and resize elements
37300 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37301 this.adapter.init(this);
37303 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37305 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37306 this.el.addClass("roo-splitbar-h");
37309 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37310 this.el.addClass("roo-splitbar-v");
37316 * Fires when the splitter is moved (alias for {@link #event-moved})
37317 * @param {Roo.bootstrap.SplitBar} this
37318 * @param {Number} newSize the new width or height
37323 * Fires when the splitter is moved
37324 * @param {Roo.bootstrap.SplitBar} this
37325 * @param {Number} newSize the new width or height
37329 * @event beforeresize
37330 * Fires before the splitter is dragged
37331 * @param {Roo.bootstrap.SplitBar} this
37333 "beforeresize" : true,
37335 "beforeapply" : true
37338 Roo.util.Observable.call(this);
37341 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37342 onStartProxyDrag : function(x, y){
37343 this.fireEvent("beforeresize", this);
37345 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37347 o.enableDisplayMode("block");
37348 // all splitbars share the same overlay
37349 Roo.bootstrap.SplitBar.prototype.overlay = o;
37351 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37352 this.overlay.show();
37353 Roo.get(this.proxy).setDisplayed("block");
37354 var size = this.adapter.getElementSize(this);
37355 this.activeMinSize = this.getMinimumSize();;
37356 this.activeMaxSize = this.getMaximumSize();;
37357 var c1 = size - this.activeMinSize;
37358 var c2 = Math.max(this.activeMaxSize - size, 0);
37359 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37360 this.dd.resetConstraints();
37361 this.dd.setXConstraint(
37362 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37363 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37365 this.dd.setYConstraint(0, 0);
37367 this.dd.resetConstraints();
37368 this.dd.setXConstraint(0, 0);
37369 this.dd.setYConstraint(
37370 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37371 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37374 this.dragSpecs.startSize = size;
37375 this.dragSpecs.startPoint = [x, y];
37376 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37380 * @private Called after the drag operation by the DDProxy
37382 onEndProxyDrag : function(e){
37383 Roo.get(this.proxy).setDisplayed(false);
37384 var endPoint = Roo.lib.Event.getXY(e);
37386 this.overlay.hide();
37389 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37390 newSize = this.dragSpecs.startSize +
37391 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37392 endPoint[0] - this.dragSpecs.startPoint[0] :
37393 this.dragSpecs.startPoint[0] - endPoint[0]
37396 newSize = this.dragSpecs.startSize +
37397 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37398 endPoint[1] - this.dragSpecs.startPoint[1] :
37399 this.dragSpecs.startPoint[1] - endPoint[1]
37402 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37403 if(newSize != this.dragSpecs.startSize){
37404 if(this.fireEvent('beforeapply', this, newSize) !== false){
37405 this.adapter.setElementSize(this, newSize);
37406 this.fireEvent("moved", this, newSize);
37407 this.fireEvent("resize", this, newSize);
37413 * Get the adapter this SplitBar uses
37414 * @return The adapter object
37416 getAdapter : function(){
37417 return this.adapter;
37421 * Set the adapter this SplitBar uses
37422 * @param {Object} adapter A SplitBar adapter object
37424 setAdapter : function(adapter){
37425 this.adapter = adapter;
37426 this.adapter.init(this);
37430 * Gets the minimum size for the resizing element
37431 * @return {Number} The minimum size
37433 getMinimumSize : function(){
37434 return this.minSize;
37438 * Sets the minimum size for the resizing element
37439 * @param {Number} minSize The minimum size
37441 setMinimumSize : function(minSize){
37442 this.minSize = minSize;
37446 * Gets the maximum size for the resizing element
37447 * @return {Number} The maximum size
37449 getMaximumSize : function(){
37450 return this.maxSize;
37454 * Sets the maximum size for the resizing element
37455 * @param {Number} maxSize The maximum size
37457 setMaximumSize : function(maxSize){
37458 this.maxSize = maxSize;
37462 * Sets the initialize size for the resizing element
37463 * @param {Number} size The initial size
37465 setCurrentSize : function(size){
37466 var oldAnimate = this.animate;
37467 this.animate = false;
37468 this.adapter.setElementSize(this, size);
37469 this.animate = oldAnimate;
37473 * Destroy this splitbar.
37474 * @param {Boolean} removeEl True to remove the element
37476 destroy : function(removeEl){
37478 this.shim.remove();
37481 this.proxy.parentNode.removeChild(this.proxy);
37489 * @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.
37491 Roo.bootstrap.SplitBar.createProxy = function(dir){
37492 var proxy = new Roo.Element(document.createElement("div"));
37493 proxy.unselectable();
37494 var cls = 'roo-splitbar-proxy';
37495 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37496 document.body.appendChild(proxy.dom);
37501 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37502 * Default Adapter. It assumes the splitter and resizing element are not positioned
37503 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37505 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37508 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37509 // do nothing for now
37510 init : function(s){
37514 * Called before drag operations to get the current size of the resizing element.
37515 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37517 getElementSize : function(s){
37518 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37519 return s.resizingEl.getWidth();
37521 return s.resizingEl.getHeight();
37526 * Called after drag operations to set the size of the resizing element.
37527 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37528 * @param {Number} newSize The new size to set
37529 * @param {Function} onComplete A function to be invoked when resizing is complete
37531 setElementSize : function(s, newSize, onComplete){
37532 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37534 s.resizingEl.setWidth(newSize);
37536 onComplete(s, newSize);
37539 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37544 s.resizingEl.setHeight(newSize);
37546 onComplete(s, newSize);
37549 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37556 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37557 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37558 * Adapter that moves the splitter element to align with the resized sizing element.
37559 * Used with an absolute positioned SplitBar.
37560 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37561 * document.body, make sure you assign an id to the body element.
37563 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37564 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37565 this.container = Roo.get(container);
37568 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37569 init : function(s){
37570 this.basic.init(s);
37573 getElementSize : function(s){
37574 return this.basic.getElementSize(s);
37577 setElementSize : function(s, newSize, onComplete){
37578 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37581 moveSplitter : function(s){
37582 var yes = Roo.bootstrap.SplitBar;
37583 switch(s.placement){
37585 s.el.setX(s.resizingEl.getRight());
37588 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37591 s.el.setY(s.resizingEl.getBottom());
37594 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37601 * Orientation constant - Create a vertical SplitBar
37605 Roo.bootstrap.SplitBar.VERTICAL = 1;
37608 * Orientation constant - Create a horizontal SplitBar
37612 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37615 * Placement constant - The resizing element is to the left of the splitter element
37619 Roo.bootstrap.SplitBar.LEFT = 1;
37622 * Placement constant - The resizing element is to the right of the splitter element
37626 Roo.bootstrap.SplitBar.RIGHT = 2;
37629 * Placement constant - The resizing element is positioned above the splitter element
37633 Roo.bootstrap.SplitBar.TOP = 3;
37636 * Placement constant - The resizing element is positioned under splitter element
37640 Roo.bootstrap.SplitBar.BOTTOM = 4;
37641 Roo.namespace("Roo.bootstrap.layout");/*
37643 * Ext JS Library 1.1.1
37644 * Copyright(c) 2006-2007, Ext JS, LLC.
37646 * Originally Released Under LGPL - original licence link has changed is not relivant.
37649 * <script type="text/javascript">
37653 * @class Roo.bootstrap.layout.Manager
37654 * @extends Roo.bootstrap.Component
37655 * Base class for layout managers.
37657 Roo.bootstrap.layout.Manager = function(config)
37659 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37665 /** false to disable window resize monitoring @type Boolean */
37666 this.monitorWindowResize = true;
37671 * Fires when a layout is performed.
37672 * @param {Roo.LayoutManager} this
37676 * @event regionresized
37677 * Fires when the user resizes a region.
37678 * @param {Roo.LayoutRegion} region The resized region
37679 * @param {Number} newSize The new size (width for east/west, height for north/south)
37681 "regionresized" : true,
37683 * @event regioncollapsed
37684 * Fires when a region is collapsed.
37685 * @param {Roo.LayoutRegion} region The collapsed region
37687 "regioncollapsed" : true,
37689 * @event regionexpanded
37690 * Fires when a region is expanded.
37691 * @param {Roo.LayoutRegion} region The expanded region
37693 "regionexpanded" : true
37695 this.updating = false;
37698 this.el = Roo.get(config.el);
37704 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37709 monitorWindowResize : true,
37715 onRender : function(ct, position)
37718 this.el = Roo.get(ct);
37721 //this.fireEvent('render',this);
37725 initEvents: function()
37729 // ie scrollbar fix
37730 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37731 document.body.scroll = "no";
37732 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37733 this.el.position('relative');
37735 this.id = this.el.id;
37736 this.el.addClass("roo-layout-container");
37737 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37738 if(this.el.dom != document.body ) {
37739 this.el.on('resize', this.layout,this);
37740 this.el.on('show', this.layout,this);
37746 * Returns true if this layout is currently being updated
37747 * @return {Boolean}
37749 isUpdating : function(){
37750 return this.updating;
37754 * Suspend the LayoutManager from doing auto-layouts while
37755 * making multiple add or remove calls
37757 beginUpdate : function(){
37758 this.updating = true;
37762 * Restore auto-layouts and optionally disable the manager from performing a layout
37763 * @param {Boolean} noLayout true to disable a layout update
37765 endUpdate : function(noLayout){
37766 this.updating = false;
37772 layout: function(){
37776 onRegionResized : function(region, newSize){
37777 this.fireEvent("regionresized", region, newSize);
37781 onRegionCollapsed : function(region){
37782 this.fireEvent("regioncollapsed", region);
37785 onRegionExpanded : function(region){
37786 this.fireEvent("regionexpanded", region);
37790 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37791 * performs box-model adjustments.
37792 * @return {Object} The size as an object {width: (the width), height: (the height)}
37794 getViewSize : function()
37797 if(this.el.dom != document.body){
37798 size = this.el.getSize();
37800 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37802 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37803 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37808 * Returns the Element this layout is bound to.
37809 * @return {Roo.Element}
37811 getEl : function(){
37816 * Returns the specified region.
37817 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37818 * @return {Roo.LayoutRegion}
37820 getRegion : function(target){
37821 return this.regions[target.toLowerCase()];
37824 onWindowResize : function(){
37825 if(this.monitorWindowResize){
37832 * Ext JS Library 1.1.1
37833 * Copyright(c) 2006-2007, Ext JS, LLC.
37835 * Originally Released Under LGPL - original licence link has changed is not relivant.
37838 * <script type="text/javascript">
37841 * @class Roo.bootstrap.layout.Border
37842 * @extends Roo.bootstrap.layout.Manager
37843 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37844 * please see: examples/bootstrap/nested.html<br><br>
37846 <b>The container the layout is rendered into can be either the body element or any other element.
37847 If it is not the body element, the container needs to either be an absolute positioned element,
37848 or you will need to add "position:relative" to the css of the container. You will also need to specify
37849 the container size if it is not the body element.</b>
37852 * Create a new Border
37853 * @param {Object} config Configuration options
37855 Roo.bootstrap.layout.Border = function(config){
37856 config = config || {};
37857 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37861 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37862 if(config[region]){
37863 config[region].region = region;
37864 this.addRegion(config[region]);
37870 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37872 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37874 parent : false, // this might point to a 'nest' or a ???
37877 * Creates and adds a new region if it doesn't already exist.
37878 * @param {String} target The target region key (north, south, east, west or center).
37879 * @param {Object} config The regions config object
37880 * @return {BorderLayoutRegion} The new region
37882 addRegion : function(config)
37884 if(!this.regions[config.region]){
37885 var r = this.factory(config);
37886 this.bindRegion(r);
37888 return this.regions[config.region];
37892 bindRegion : function(r){
37893 this.regions[r.config.region] = r;
37895 r.on("visibilitychange", this.layout, this);
37896 r.on("paneladded", this.layout, this);
37897 r.on("panelremoved", this.layout, this);
37898 r.on("invalidated", this.layout, this);
37899 r.on("resized", this.onRegionResized, this);
37900 r.on("collapsed", this.onRegionCollapsed, this);
37901 r.on("expanded", this.onRegionExpanded, this);
37905 * Performs a layout update.
37907 layout : function()
37909 if(this.updating) {
37913 // render all the rebions if they have not been done alreayd?
37914 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37915 if(this.regions[region] && !this.regions[region].bodyEl){
37916 this.regions[region].onRender(this.el)
37920 var size = this.getViewSize();
37921 var w = size.width;
37922 var h = size.height;
37927 //var x = 0, y = 0;
37929 var rs = this.regions;
37930 var north = rs["north"];
37931 var south = rs["south"];
37932 var west = rs["west"];
37933 var east = rs["east"];
37934 var center = rs["center"];
37935 //if(this.hideOnLayout){ // not supported anymore
37936 //c.el.setStyle("display", "none");
37938 if(north && north.isVisible()){
37939 var b = north.getBox();
37940 var m = north.getMargins();
37941 b.width = w - (m.left+m.right);
37944 centerY = b.height + b.y + m.bottom;
37945 centerH -= centerY;
37946 north.updateBox(this.safeBox(b));
37948 if(south && south.isVisible()){
37949 var b = south.getBox();
37950 var m = south.getMargins();
37951 b.width = w - (m.left+m.right);
37953 var totalHeight = (b.height + m.top + m.bottom);
37954 b.y = h - totalHeight + m.top;
37955 centerH -= totalHeight;
37956 south.updateBox(this.safeBox(b));
37958 if(west && west.isVisible()){
37959 var b = west.getBox();
37960 var m = west.getMargins();
37961 b.height = centerH - (m.top+m.bottom);
37963 b.y = centerY + m.top;
37964 var totalWidth = (b.width + m.left + m.right);
37965 centerX += totalWidth;
37966 centerW -= totalWidth;
37967 west.updateBox(this.safeBox(b));
37969 if(east && east.isVisible()){
37970 var b = east.getBox();
37971 var m = east.getMargins();
37972 b.height = centerH - (m.top+m.bottom);
37973 var totalWidth = (b.width + m.left + m.right);
37974 b.x = w - totalWidth + m.left;
37975 b.y = centerY + m.top;
37976 centerW -= totalWidth;
37977 east.updateBox(this.safeBox(b));
37980 var m = center.getMargins();
37982 x: centerX + m.left,
37983 y: centerY + m.top,
37984 width: centerW - (m.left+m.right),
37985 height: centerH - (m.top+m.bottom)
37987 //if(this.hideOnLayout){
37988 //center.el.setStyle("display", "block");
37990 center.updateBox(this.safeBox(centerBox));
37993 this.fireEvent("layout", this);
37997 safeBox : function(box){
37998 box.width = Math.max(0, box.width);
37999 box.height = Math.max(0, box.height);
38004 * Adds a ContentPanel (or subclass) to this layout.
38005 * @param {String} target The target region key (north, south, east, west or center).
38006 * @param {Roo.ContentPanel} panel The panel to add
38007 * @return {Roo.ContentPanel} The added panel
38009 add : function(target, panel){
38011 target = target.toLowerCase();
38012 return this.regions[target].add(panel);
38016 * Remove a ContentPanel (or subclass) to this layout.
38017 * @param {String} target The target region key (north, south, east, west or center).
38018 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38019 * @return {Roo.ContentPanel} The removed panel
38021 remove : function(target, panel){
38022 target = target.toLowerCase();
38023 return this.regions[target].remove(panel);
38027 * Searches all regions for a panel with the specified id
38028 * @param {String} panelId
38029 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38031 findPanel : function(panelId){
38032 var rs = this.regions;
38033 for(var target in rs){
38034 if(typeof rs[target] != "function"){
38035 var p = rs[target].getPanel(panelId);
38045 * Searches all regions for a panel with the specified id and activates (shows) it.
38046 * @param {String/ContentPanel} panelId The panels id or the panel itself
38047 * @return {Roo.ContentPanel} The shown panel or null
38049 showPanel : function(panelId) {
38050 var rs = this.regions;
38051 for(var target in rs){
38052 var r = rs[target];
38053 if(typeof r != "function"){
38054 if(r.hasPanel(panelId)){
38055 return r.showPanel(panelId);
38063 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38064 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38067 restoreState : function(provider){
38069 provider = Roo.state.Manager;
38071 var sm = new Roo.LayoutStateManager();
38072 sm.init(this, provider);
38078 * Adds a xtype elements to the layout.
38082 xtype : 'ContentPanel',
38089 xtype : 'NestedLayoutPanel',
38095 items : [ ... list of content panels or nested layout panels.. ]
38099 * @param {Object} cfg Xtype definition of item to add.
38101 addxtype : function(cfg)
38103 // basically accepts a pannel...
38104 // can accept a layout region..!?!?
38105 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38108 // theory? children can only be panels??
38110 //if (!cfg.xtype.match(/Panel$/)) {
38115 if (typeof(cfg.region) == 'undefined') {
38116 Roo.log("Failed to add Panel, region was not set");
38120 var region = cfg.region;
38126 xitems = cfg.items;
38131 if ( region == 'center') {
38132 Roo.log("Center: " + cfg.title);
38138 case 'Content': // ContentPanel (el, cfg)
38139 case 'Scroll': // ContentPanel (el, cfg)
38141 cfg.autoCreate = cfg.autoCreate || true;
38142 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38144 // var el = this.el.createChild();
38145 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38148 this.add(region, ret);
38152 case 'TreePanel': // our new panel!
38153 cfg.el = this.el.createChild();
38154 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38155 this.add(region, ret);
38160 // create a new Layout (which is a Border Layout...
38162 var clayout = cfg.layout;
38163 clayout.el = this.el.createChild();
38164 clayout.items = clayout.items || [];
38168 // replace this exitems with the clayout ones..
38169 xitems = clayout.items;
38171 // force background off if it's in center...
38172 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38173 cfg.background = false;
38175 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38178 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38179 //console.log('adding nested layout panel ' + cfg.toSource());
38180 this.add(region, ret);
38181 nb = {}; /// find first...
38186 // needs grid and region
38188 //var el = this.getRegion(region).el.createChild();
38190 *var el = this.el.createChild();
38191 // create the grid first...
38192 cfg.grid.container = el;
38193 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38196 if (region == 'center' && this.active ) {
38197 cfg.background = false;
38200 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38202 this.add(region, ret);
38204 if (cfg.background) {
38205 // render grid on panel activation (if panel background)
38206 ret.on('activate', function(gp) {
38207 if (!gp.grid.rendered) {
38208 // gp.grid.render(el);
38212 // cfg.grid.render(el);
38218 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38219 // it was the old xcomponent building that caused this before.
38220 // espeically if border is the top element in the tree.
38230 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38232 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38233 this.add(region, ret);
38237 throw "Can not add '" + cfg.xtype + "' to Border";
38243 this.beginUpdate();
38247 Roo.each(xitems, function(i) {
38248 region = nb && i.region ? i.region : false;
38250 var add = ret.addxtype(i);
38253 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38254 if (!i.background) {
38255 abn[region] = nb[region] ;
38262 // make the last non-background panel active..
38263 //if (nb) { Roo.log(abn); }
38266 for(var r in abn) {
38267 region = this.getRegion(r);
38269 // tried using nb[r], but it does not work..
38271 region.showPanel(abn[r]);
38282 factory : function(cfg)
38285 var validRegions = Roo.bootstrap.layout.Border.regions;
38287 var target = cfg.region;
38290 var r = Roo.bootstrap.layout;
38294 return new r.North(cfg);
38296 return new r.South(cfg);
38298 return new r.East(cfg);
38300 return new r.West(cfg);
38302 return new r.Center(cfg);
38304 throw 'Layout region "'+target+'" not supported.';
38311 * Ext JS Library 1.1.1
38312 * Copyright(c) 2006-2007, Ext JS, LLC.
38314 * Originally Released Under LGPL - original licence link has changed is not relivant.
38317 * <script type="text/javascript">
38321 * @class Roo.bootstrap.layout.Basic
38322 * @extends Roo.util.Observable
38323 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38324 * and does not have a titlebar, tabs or any other features. All it does is size and position
38325 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38326 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38327 * @cfg {string} region the region that it inhabits..
38328 * @cfg {bool} skipConfig skip config?
38332 Roo.bootstrap.layout.Basic = function(config){
38334 this.mgr = config.mgr;
38336 this.position = config.region;
38338 var skipConfig = config.skipConfig;
38342 * @scope Roo.BasicLayoutRegion
38346 * @event beforeremove
38347 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38348 * @param {Roo.LayoutRegion} this
38349 * @param {Roo.ContentPanel} panel The panel
38350 * @param {Object} e The cancel event object
38352 "beforeremove" : true,
38354 * @event invalidated
38355 * Fires when the layout for this region is changed.
38356 * @param {Roo.LayoutRegion} this
38358 "invalidated" : true,
38360 * @event visibilitychange
38361 * Fires when this region is shown or hidden
38362 * @param {Roo.LayoutRegion} this
38363 * @param {Boolean} visibility true or false
38365 "visibilitychange" : true,
38367 * @event paneladded
38368 * Fires when a panel is added.
38369 * @param {Roo.LayoutRegion} this
38370 * @param {Roo.ContentPanel} panel The panel
38372 "paneladded" : true,
38374 * @event panelremoved
38375 * Fires when a panel is removed.
38376 * @param {Roo.LayoutRegion} this
38377 * @param {Roo.ContentPanel} panel The panel
38379 "panelremoved" : true,
38381 * @event beforecollapse
38382 * Fires when this region before collapse.
38383 * @param {Roo.LayoutRegion} this
38385 "beforecollapse" : true,
38388 * Fires when this region is collapsed.
38389 * @param {Roo.LayoutRegion} this
38391 "collapsed" : true,
38394 * Fires when this region is expanded.
38395 * @param {Roo.LayoutRegion} this
38400 * Fires when this region is slid into view.
38401 * @param {Roo.LayoutRegion} this
38403 "slideshow" : true,
38406 * Fires when this region slides out of view.
38407 * @param {Roo.LayoutRegion} this
38409 "slidehide" : true,
38411 * @event panelactivated
38412 * Fires when a panel is activated.
38413 * @param {Roo.LayoutRegion} this
38414 * @param {Roo.ContentPanel} panel The activated panel
38416 "panelactivated" : true,
38419 * Fires when the user resizes this region.
38420 * @param {Roo.LayoutRegion} this
38421 * @param {Number} newSize The new size (width for east/west, height for north/south)
38425 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38426 this.panels = new Roo.util.MixedCollection();
38427 this.panels.getKey = this.getPanelId.createDelegate(this);
38429 this.activePanel = null;
38430 // ensure listeners are added...
38432 if (config.listeners || config.events) {
38433 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38434 listeners : config.listeners || {},
38435 events : config.events || {}
38439 if(skipConfig !== true){
38440 this.applyConfig(config);
38444 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38446 getPanelId : function(p){
38450 applyConfig : function(config){
38451 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38452 this.config = config;
38457 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38458 * the width, for horizontal (north, south) the height.
38459 * @param {Number} newSize The new width or height
38461 resizeTo : function(newSize){
38462 var el = this.el ? this.el :
38463 (this.activePanel ? this.activePanel.getEl() : null);
38465 switch(this.position){
38468 el.setWidth(newSize);
38469 this.fireEvent("resized", this, newSize);
38473 el.setHeight(newSize);
38474 this.fireEvent("resized", this, newSize);
38480 getBox : function(){
38481 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38484 getMargins : function(){
38485 return this.margins;
38488 updateBox : function(box){
38490 var el = this.activePanel.getEl();
38491 el.dom.style.left = box.x + "px";
38492 el.dom.style.top = box.y + "px";
38493 this.activePanel.setSize(box.width, box.height);
38497 * Returns the container element for this region.
38498 * @return {Roo.Element}
38500 getEl : function(){
38501 return this.activePanel;
38505 * Returns true if this region is currently visible.
38506 * @return {Boolean}
38508 isVisible : function(){
38509 return this.activePanel ? true : false;
38512 setActivePanel : function(panel){
38513 panel = this.getPanel(panel);
38514 if(this.activePanel && this.activePanel != panel){
38515 this.activePanel.setActiveState(false);
38516 this.activePanel.getEl().setLeftTop(-10000,-10000);
38518 this.activePanel = panel;
38519 panel.setActiveState(true);
38521 panel.setSize(this.box.width, this.box.height);
38523 this.fireEvent("panelactivated", this, panel);
38524 this.fireEvent("invalidated");
38528 * Show the specified panel.
38529 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38530 * @return {Roo.ContentPanel} The shown panel or null
38532 showPanel : function(panel){
38533 panel = this.getPanel(panel);
38535 this.setActivePanel(panel);
38541 * Get the active panel for this region.
38542 * @return {Roo.ContentPanel} The active panel or null
38544 getActivePanel : function(){
38545 return this.activePanel;
38549 * Add the passed ContentPanel(s)
38550 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38551 * @return {Roo.ContentPanel} The panel added (if only one was added)
38553 add : function(panel){
38554 if(arguments.length > 1){
38555 for(var i = 0, len = arguments.length; i < len; i++) {
38556 this.add(arguments[i]);
38560 if(this.hasPanel(panel)){
38561 this.showPanel(panel);
38564 var el = panel.getEl();
38565 if(el.dom.parentNode != this.mgr.el.dom){
38566 this.mgr.el.dom.appendChild(el.dom);
38568 if(panel.setRegion){
38569 panel.setRegion(this);
38571 this.panels.add(panel);
38572 el.setStyle("position", "absolute");
38573 if(!panel.background){
38574 this.setActivePanel(panel);
38575 if(this.config.initialSize && this.panels.getCount()==1){
38576 this.resizeTo(this.config.initialSize);
38579 this.fireEvent("paneladded", this, panel);
38584 * Returns true if the panel is in this region.
38585 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38586 * @return {Boolean}
38588 hasPanel : function(panel){
38589 if(typeof panel == "object"){ // must be panel obj
38590 panel = panel.getId();
38592 return this.getPanel(panel) ? true : false;
38596 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38597 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38598 * @param {Boolean} preservePanel Overrides the config preservePanel option
38599 * @return {Roo.ContentPanel} The panel that was removed
38601 remove : function(panel, preservePanel){
38602 panel = this.getPanel(panel);
38607 this.fireEvent("beforeremove", this, panel, e);
38608 if(e.cancel === true){
38611 var panelId = panel.getId();
38612 this.panels.removeKey(panelId);
38617 * Returns the panel specified or null if it's not in this region.
38618 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38619 * @return {Roo.ContentPanel}
38621 getPanel : function(id){
38622 if(typeof id == "object"){ // must be panel obj
38625 return this.panels.get(id);
38629 * Returns this regions position (north/south/east/west/center).
38632 getPosition: function(){
38633 return this.position;
38637 * Ext JS Library 1.1.1
38638 * Copyright(c) 2006-2007, Ext JS, LLC.
38640 * Originally Released Under LGPL - original licence link has changed is not relivant.
38643 * <script type="text/javascript">
38647 * @class Roo.bootstrap.layout.Region
38648 * @extends Roo.bootstrap.layout.Basic
38649 * This class represents a region in a layout manager.
38651 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38652 * @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})
38653 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38654 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38655 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38656 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38657 * @cfg {String} title The title for the region (overrides panel titles)
38658 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38659 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38660 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38661 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38662 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38663 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38664 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38665 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38666 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38667 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38669 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38670 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38671 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38672 * @cfg {Number} width For East/West panels
38673 * @cfg {Number} height For North/South panels
38674 * @cfg {Boolean} split To show the splitter
38675 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38677 * @cfg {string} cls Extra CSS classes to add to region
38679 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38680 * @cfg {string} region the region that it inhabits..
38683 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38684 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38686 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38687 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38688 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38690 Roo.bootstrap.layout.Region = function(config)
38692 this.applyConfig(config);
38694 var mgr = config.mgr;
38695 var pos = config.region;
38696 config.skipConfig = true;
38697 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38700 this.onRender(mgr.el);
38703 this.visible = true;
38704 this.collapsed = false;
38705 this.unrendered_panels = [];
38708 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38710 position: '', // set by wrapper (eg. north/south etc..)
38711 unrendered_panels : null, // unrendered panels.
38713 tabPosition : false,
38715 mgr: false, // points to 'Border'
38718 createBody : function(){
38719 /** This region's body element
38720 * @type Roo.Element */
38721 this.bodyEl = this.el.createChild({
38723 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38727 onRender: function(ctr, pos)
38729 var dh = Roo.DomHelper;
38730 /** This region's container element
38731 * @type Roo.Element */
38732 this.el = dh.append(ctr.dom, {
38734 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38736 /** This region's title element
38737 * @type Roo.Element */
38739 this.titleEl = dh.append(this.el.dom, {
38741 unselectable: "on",
38742 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38744 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38745 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38749 this.titleEl.enableDisplayMode();
38750 /** This region's title text element
38751 * @type HTMLElement */
38752 this.titleTextEl = this.titleEl.dom.firstChild;
38753 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38755 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38756 this.closeBtn.enableDisplayMode();
38757 this.closeBtn.on("click", this.closeClicked, this);
38758 this.closeBtn.hide();
38760 this.createBody(this.config);
38761 if(this.config.hideWhenEmpty){
38763 this.on("paneladded", this.validateVisibility, this);
38764 this.on("panelremoved", this.validateVisibility, this);
38766 if(this.autoScroll){
38767 this.bodyEl.setStyle("overflow", "auto");
38769 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38771 //if(c.titlebar !== false){
38772 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38773 this.titleEl.hide();
38775 this.titleEl.show();
38776 if(this.config.title){
38777 this.titleTextEl.innerHTML = this.config.title;
38781 if(this.config.collapsed){
38782 this.collapse(true);
38784 if(this.config.hidden){
38788 if (this.unrendered_panels && this.unrendered_panels.length) {
38789 for (var i =0;i< this.unrendered_panels.length; i++) {
38790 this.add(this.unrendered_panels[i]);
38792 this.unrendered_panels = null;
38798 applyConfig : function(c)
38801 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38802 var dh = Roo.DomHelper;
38803 if(c.titlebar !== false){
38804 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38805 this.collapseBtn.on("click", this.collapse, this);
38806 this.collapseBtn.enableDisplayMode();
38808 if(c.showPin === true || this.showPin){
38809 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38810 this.stickBtn.enableDisplayMode();
38811 this.stickBtn.on("click", this.expand, this);
38812 this.stickBtn.hide();
38817 /** This region's collapsed element
38818 * @type Roo.Element */
38821 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38822 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38825 if(c.floatable !== false){
38826 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38827 this.collapsedEl.on("click", this.collapseClick, this);
38830 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38831 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38832 id: "message", unselectable: "on", style:{"float":"left"}});
38833 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38835 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38836 this.expandBtn.on("click", this.expand, this);
38840 if(this.collapseBtn){
38841 this.collapseBtn.setVisible(c.collapsible == true);
38844 this.cmargins = c.cmargins || this.cmargins ||
38845 (this.position == "west" || this.position == "east" ?
38846 {top: 0, left: 2, right:2, bottom: 0} :
38847 {top: 2, left: 0, right:0, bottom: 2});
38849 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38852 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38854 this.autoScroll = c.autoScroll || false;
38859 this.duration = c.duration || .30;
38860 this.slideDuration = c.slideDuration || .45;
38865 * Returns true if this region is currently visible.
38866 * @return {Boolean}
38868 isVisible : function(){
38869 return this.visible;
38873 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38874 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38876 //setCollapsedTitle : function(title){
38877 // title = title || " ";
38878 // if(this.collapsedTitleTextEl){
38879 // this.collapsedTitleTextEl.innerHTML = title;
38883 getBox : function(){
38885 // if(!this.collapsed){
38886 b = this.el.getBox(false, true);
38888 // b = this.collapsedEl.getBox(false, true);
38893 getMargins : function(){
38894 return this.margins;
38895 //return this.collapsed ? this.cmargins : this.margins;
38898 highlight : function(){
38899 this.el.addClass("x-layout-panel-dragover");
38902 unhighlight : function(){
38903 this.el.removeClass("x-layout-panel-dragover");
38906 updateBox : function(box)
38908 if (!this.bodyEl) {
38909 return; // not rendered yet..
38913 if(!this.collapsed){
38914 this.el.dom.style.left = box.x + "px";
38915 this.el.dom.style.top = box.y + "px";
38916 this.updateBody(box.width, box.height);
38918 this.collapsedEl.dom.style.left = box.x + "px";
38919 this.collapsedEl.dom.style.top = box.y + "px";
38920 this.collapsedEl.setSize(box.width, box.height);
38923 this.tabs.autoSizeTabs();
38927 updateBody : function(w, h)
38930 this.el.setWidth(w);
38931 w -= this.el.getBorderWidth("rl");
38932 if(this.config.adjustments){
38933 w += this.config.adjustments[0];
38936 if(h !== null && h > 0){
38937 this.el.setHeight(h);
38938 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38939 h -= this.el.getBorderWidth("tb");
38940 if(this.config.adjustments){
38941 h += this.config.adjustments[1];
38943 this.bodyEl.setHeight(h);
38945 h = this.tabs.syncHeight(h);
38948 if(this.panelSize){
38949 w = w !== null ? w : this.panelSize.width;
38950 h = h !== null ? h : this.panelSize.height;
38952 if(this.activePanel){
38953 var el = this.activePanel.getEl();
38954 w = w !== null ? w : el.getWidth();
38955 h = h !== null ? h : el.getHeight();
38956 this.panelSize = {width: w, height: h};
38957 this.activePanel.setSize(w, h);
38959 if(Roo.isIE && this.tabs){
38960 this.tabs.el.repaint();
38965 * Returns the container element for this region.
38966 * @return {Roo.Element}
38968 getEl : function(){
38973 * Hides this region.
38976 //if(!this.collapsed){
38977 this.el.dom.style.left = "-2000px";
38980 // this.collapsedEl.dom.style.left = "-2000px";
38981 // this.collapsedEl.hide();
38983 this.visible = false;
38984 this.fireEvent("visibilitychange", this, false);
38988 * Shows this region if it was previously hidden.
38991 //if(!this.collapsed){
38994 // this.collapsedEl.show();
38996 this.visible = true;
38997 this.fireEvent("visibilitychange", this, true);
39000 closeClicked : function(){
39001 if(this.activePanel){
39002 this.remove(this.activePanel);
39006 collapseClick : function(e){
39008 e.stopPropagation();
39011 e.stopPropagation();
39017 * Collapses this region.
39018 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39021 collapse : function(skipAnim, skipCheck = false){
39022 if(this.collapsed) {
39026 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39028 this.collapsed = true;
39030 this.split.el.hide();
39032 if(this.config.animate && skipAnim !== true){
39033 this.fireEvent("invalidated", this);
39034 this.animateCollapse();
39036 this.el.setLocation(-20000,-20000);
39038 this.collapsedEl.show();
39039 this.fireEvent("collapsed", this);
39040 this.fireEvent("invalidated", this);
39046 animateCollapse : function(){
39051 * Expands this region if it was previously collapsed.
39052 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39053 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39056 expand : function(e, skipAnim){
39058 e.stopPropagation();
39060 if(!this.collapsed || this.el.hasActiveFx()) {
39064 this.afterSlideIn();
39067 this.collapsed = false;
39068 if(this.config.animate && skipAnim !== true){
39069 this.animateExpand();
39073 this.split.el.show();
39075 this.collapsedEl.setLocation(-2000,-2000);
39076 this.collapsedEl.hide();
39077 this.fireEvent("invalidated", this);
39078 this.fireEvent("expanded", this);
39082 animateExpand : function(){
39086 initTabs : function()
39088 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39090 var ts = new Roo.bootstrap.panel.Tabs({
39091 el: this.bodyEl.dom,
39093 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39094 disableTooltips: this.config.disableTabTips,
39095 toolbar : this.config.toolbar
39098 if(this.config.hideTabs){
39099 ts.stripWrap.setDisplayed(false);
39102 ts.resizeTabs = this.config.resizeTabs === true;
39103 ts.minTabWidth = this.config.minTabWidth || 40;
39104 ts.maxTabWidth = this.config.maxTabWidth || 250;
39105 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39106 ts.monitorResize = false;
39107 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39108 ts.bodyEl.addClass('roo-layout-tabs-body');
39109 this.panels.each(this.initPanelAsTab, this);
39112 initPanelAsTab : function(panel){
39113 var ti = this.tabs.addTab(
39117 this.config.closeOnTab && panel.isClosable(),
39120 if(panel.tabTip !== undefined){
39121 ti.setTooltip(panel.tabTip);
39123 ti.on("activate", function(){
39124 this.setActivePanel(panel);
39127 if(this.config.closeOnTab){
39128 ti.on("beforeclose", function(t, e){
39130 this.remove(panel);
39134 panel.tabItem = ti;
39139 updatePanelTitle : function(panel, title)
39141 if(this.activePanel == panel){
39142 this.updateTitle(title);
39145 var ti = this.tabs.getTab(panel.getEl().id);
39147 if(panel.tabTip !== undefined){
39148 ti.setTooltip(panel.tabTip);
39153 updateTitle : function(title){
39154 if(this.titleTextEl && !this.config.title){
39155 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39159 setActivePanel : function(panel)
39161 panel = this.getPanel(panel);
39162 if(this.activePanel && this.activePanel != panel){
39163 if(this.activePanel.setActiveState(false) === false){
39167 this.activePanel = panel;
39168 panel.setActiveState(true);
39169 if(this.panelSize){
39170 panel.setSize(this.panelSize.width, this.panelSize.height);
39173 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39175 this.updateTitle(panel.getTitle());
39177 this.fireEvent("invalidated", this);
39179 this.fireEvent("panelactivated", this, panel);
39183 * Shows the specified panel.
39184 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39185 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39187 showPanel : function(panel)
39189 panel = this.getPanel(panel);
39192 var tab = this.tabs.getTab(panel.getEl().id);
39193 if(tab.isHidden()){
39194 this.tabs.unhideTab(tab.id);
39198 this.setActivePanel(panel);
39205 * Get the active panel for this region.
39206 * @return {Roo.ContentPanel} The active panel or null
39208 getActivePanel : function(){
39209 return this.activePanel;
39212 validateVisibility : function(){
39213 if(this.panels.getCount() < 1){
39214 this.updateTitle(" ");
39215 this.closeBtn.hide();
39218 if(!this.isVisible()){
39225 * Adds the passed ContentPanel(s) to this region.
39226 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39227 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39229 add : function(panel)
39231 if(arguments.length > 1){
39232 for(var i = 0, len = arguments.length; i < len; i++) {
39233 this.add(arguments[i]);
39238 // if we have not been rendered yet, then we can not really do much of this..
39239 if (!this.bodyEl) {
39240 this.unrendered_panels.push(panel);
39247 if(this.hasPanel(panel)){
39248 this.showPanel(panel);
39251 panel.setRegion(this);
39252 this.panels.add(panel);
39253 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39254 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39255 // and hide them... ???
39256 this.bodyEl.dom.appendChild(panel.getEl().dom);
39257 if(panel.background !== true){
39258 this.setActivePanel(panel);
39260 this.fireEvent("paneladded", this, panel);
39267 this.initPanelAsTab(panel);
39271 if(panel.background !== true){
39272 this.tabs.activate(panel.getEl().id);
39274 this.fireEvent("paneladded", this, panel);
39279 * Hides the tab for the specified panel.
39280 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39282 hidePanel : function(panel){
39283 if(this.tabs && (panel = this.getPanel(panel))){
39284 this.tabs.hideTab(panel.getEl().id);
39289 * Unhides the tab for a previously hidden panel.
39290 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39292 unhidePanel : function(panel){
39293 if(this.tabs && (panel = this.getPanel(panel))){
39294 this.tabs.unhideTab(panel.getEl().id);
39298 clearPanels : function(){
39299 while(this.panels.getCount() > 0){
39300 this.remove(this.panels.first());
39305 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39306 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39307 * @param {Boolean} preservePanel Overrides the config preservePanel option
39308 * @return {Roo.ContentPanel} The panel that was removed
39310 remove : function(panel, preservePanel)
39312 panel = this.getPanel(panel);
39317 this.fireEvent("beforeremove", this, panel, e);
39318 if(e.cancel === true){
39321 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39322 var panelId = panel.getId();
39323 this.panels.removeKey(panelId);
39325 document.body.appendChild(panel.getEl().dom);
39328 this.tabs.removeTab(panel.getEl().id);
39329 }else if (!preservePanel){
39330 this.bodyEl.dom.removeChild(panel.getEl().dom);
39332 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39333 var p = this.panels.first();
39334 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39335 tempEl.appendChild(p.getEl().dom);
39336 this.bodyEl.update("");
39337 this.bodyEl.dom.appendChild(p.getEl().dom);
39339 this.updateTitle(p.getTitle());
39341 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39342 this.setActivePanel(p);
39344 panel.setRegion(null);
39345 if(this.activePanel == panel){
39346 this.activePanel = null;
39348 if(this.config.autoDestroy !== false && preservePanel !== true){
39349 try{panel.destroy();}catch(e){}
39351 this.fireEvent("panelremoved", this, panel);
39356 * Returns the TabPanel component used by this region
39357 * @return {Roo.TabPanel}
39359 getTabs : function(){
39363 createTool : function(parentEl, className){
39364 var btn = Roo.DomHelper.append(parentEl, {
39366 cls: "x-layout-tools-button",
39369 cls: "roo-layout-tools-button-inner " + className,
39373 btn.addClassOnOver("roo-layout-tools-button-over");
39378 * Ext JS Library 1.1.1
39379 * Copyright(c) 2006-2007, Ext JS, LLC.
39381 * Originally Released Under LGPL - original licence link has changed is not relivant.
39384 * <script type="text/javascript">
39390 * @class Roo.SplitLayoutRegion
39391 * @extends Roo.LayoutRegion
39392 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39394 Roo.bootstrap.layout.Split = function(config){
39395 this.cursor = config.cursor;
39396 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39399 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39401 splitTip : "Drag to resize.",
39402 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39403 useSplitTips : false,
39405 applyConfig : function(config){
39406 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39409 onRender : function(ctr,pos) {
39411 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39412 if(!this.config.split){
39417 var splitEl = Roo.DomHelper.append(ctr.dom, {
39419 id: this.el.id + "-split",
39420 cls: "roo-layout-split roo-layout-split-"+this.position,
39423 /** The SplitBar for this region
39424 * @type Roo.SplitBar */
39425 // does not exist yet...
39426 Roo.log([this.position, this.orientation]);
39428 this.split = new Roo.bootstrap.SplitBar({
39429 dragElement : splitEl,
39430 resizingElement: this.el,
39431 orientation : this.orientation
39434 this.split.on("moved", this.onSplitMove, this);
39435 this.split.useShim = this.config.useShim === true;
39436 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39437 if(this.useSplitTips){
39438 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39440 //if(config.collapsible){
39441 // this.split.el.on("dblclick", this.collapse, this);
39444 if(typeof this.config.minSize != "undefined"){
39445 this.split.minSize = this.config.minSize;
39447 if(typeof this.config.maxSize != "undefined"){
39448 this.split.maxSize = this.config.maxSize;
39450 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39451 this.hideSplitter();
39456 getHMaxSize : function(){
39457 var cmax = this.config.maxSize || 10000;
39458 var center = this.mgr.getRegion("center");
39459 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39462 getVMaxSize : function(){
39463 var cmax = this.config.maxSize || 10000;
39464 var center = this.mgr.getRegion("center");
39465 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39468 onSplitMove : function(split, newSize){
39469 this.fireEvent("resized", this, newSize);
39473 * Returns the {@link Roo.SplitBar} for this region.
39474 * @return {Roo.SplitBar}
39476 getSplitBar : function(){
39481 this.hideSplitter();
39482 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39485 hideSplitter : function(){
39487 this.split.el.setLocation(-2000,-2000);
39488 this.split.el.hide();
39494 this.split.el.show();
39496 Roo.bootstrap.layout.Split.superclass.show.call(this);
39499 beforeSlide: function(){
39500 if(Roo.isGecko){// firefox overflow auto bug workaround
39501 this.bodyEl.clip();
39503 this.tabs.bodyEl.clip();
39505 if(this.activePanel){
39506 this.activePanel.getEl().clip();
39508 if(this.activePanel.beforeSlide){
39509 this.activePanel.beforeSlide();
39515 afterSlide : function(){
39516 if(Roo.isGecko){// firefox overflow auto bug workaround
39517 this.bodyEl.unclip();
39519 this.tabs.bodyEl.unclip();
39521 if(this.activePanel){
39522 this.activePanel.getEl().unclip();
39523 if(this.activePanel.afterSlide){
39524 this.activePanel.afterSlide();
39530 initAutoHide : function(){
39531 if(this.autoHide !== false){
39532 if(!this.autoHideHd){
39533 var st = new Roo.util.DelayedTask(this.slideIn, this);
39534 this.autoHideHd = {
39535 "mouseout": function(e){
39536 if(!e.within(this.el, true)){
39540 "mouseover" : function(e){
39546 this.el.on(this.autoHideHd);
39550 clearAutoHide : function(){
39551 if(this.autoHide !== false){
39552 this.el.un("mouseout", this.autoHideHd.mouseout);
39553 this.el.un("mouseover", this.autoHideHd.mouseover);
39557 clearMonitor : function(){
39558 Roo.get(document).un("click", this.slideInIf, this);
39561 // these names are backwards but not changed for compat
39562 slideOut : function(){
39563 if(this.isSlid || this.el.hasActiveFx()){
39566 this.isSlid = true;
39567 if(this.collapseBtn){
39568 this.collapseBtn.hide();
39570 this.closeBtnState = this.closeBtn.getStyle('display');
39571 this.closeBtn.hide();
39573 this.stickBtn.show();
39576 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39577 this.beforeSlide();
39578 this.el.setStyle("z-index", 10001);
39579 this.el.slideIn(this.getSlideAnchor(), {
39580 callback: function(){
39582 this.initAutoHide();
39583 Roo.get(document).on("click", this.slideInIf, this);
39584 this.fireEvent("slideshow", this);
39591 afterSlideIn : function(){
39592 this.clearAutoHide();
39593 this.isSlid = false;
39594 this.clearMonitor();
39595 this.el.setStyle("z-index", "");
39596 if(this.collapseBtn){
39597 this.collapseBtn.show();
39599 this.closeBtn.setStyle('display', this.closeBtnState);
39601 this.stickBtn.hide();
39603 this.fireEvent("slidehide", this);
39606 slideIn : function(cb){
39607 if(!this.isSlid || this.el.hasActiveFx()){
39611 this.isSlid = false;
39612 this.beforeSlide();
39613 this.el.slideOut(this.getSlideAnchor(), {
39614 callback: function(){
39615 this.el.setLeftTop(-10000, -10000);
39617 this.afterSlideIn();
39625 slideInIf : function(e){
39626 if(!e.within(this.el)){
39631 animateCollapse : function(){
39632 this.beforeSlide();
39633 this.el.setStyle("z-index", 20000);
39634 var anchor = this.getSlideAnchor();
39635 this.el.slideOut(anchor, {
39636 callback : function(){
39637 this.el.setStyle("z-index", "");
39638 this.collapsedEl.slideIn(anchor, {duration:.3});
39640 this.el.setLocation(-10000,-10000);
39642 this.fireEvent("collapsed", this);
39649 animateExpand : function(){
39650 this.beforeSlide();
39651 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39652 this.el.setStyle("z-index", 20000);
39653 this.collapsedEl.hide({
39656 this.el.slideIn(this.getSlideAnchor(), {
39657 callback : function(){
39658 this.el.setStyle("z-index", "");
39661 this.split.el.show();
39663 this.fireEvent("invalidated", this);
39664 this.fireEvent("expanded", this);
39692 getAnchor : function(){
39693 return this.anchors[this.position];
39696 getCollapseAnchor : function(){
39697 return this.canchors[this.position];
39700 getSlideAnchor : function(){
39701 return this.sanchors[this.position];
39704 getAlignAdj : function(){
39705 var cm = this.cmargins;
39706 switch(this.position){
39722 getExpandAdj : function(){
39723 var c = this.collapsedEl, cm = this.cmargins;
39724 switch(this.position){
39726 return [-(cm.right+c.getWidth()+cm.left), 0];
39729 return [cm.right+c.getWidth()+cm.left, 0];
39732 return [0, -(cm.top+cm.bottom+c.getHeight())];
39735 return [0, cm.top+cm.bottom+c.getHeight()];
39741 * Ext JS Library 1.1.1
39742 * Copyright(c) 2006-2007, Ext JS, LLC.
39744 * Originally Released Under LGPL - original licence link has changed is not relivant.
39747 * <script type="text/javascript">
39750 * These classes are private internal classes
39752 Roo.bootstrap.layout.Center = function(config){
39753 config.region = "center";
39754 Roo.bootstrap.layout.Region.call(this, config);
39755 this.visible = true;
39756 this.minWidth = config.minWidth || 20;
39757 this.minHeight = config.minHeight || 20;
39760 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39762 // center panel can't be hidden
39766 // center panel can't be hidden
39769 getMinWidth: function(){
39770 return this.minWidth;
39773 getMinHeight: function(){
39774 return this.minHeight;
39788 Roo.bootstrap.layout.North = function(config)
39790 config.region = 'north';
39791 config.cursor = 'n-resize';
39793 Roo.bootstrap.layout.Split.call(this, config);
39797 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39798 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39799 this.split.el.addClass("roo-layout-split-v");
39801 //var size = config.initialSize || config.height;
39802 //if(this.el && typeof size != "undefined"){
39803 // this.el.setHeight(size);
39806 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39808 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39811 onRender : function(ctr, pos)
39813 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39814 var size = this.config.initialSize || this.config.height;
39815 if(this.el && typeof size != "undefined"){
39816 this.el.setHeight(size);
39821 getBox : function(){
39822 if(this.collapsed){
39823 return this.collapsedEl.getBox();
39825 var box = this.el.getBox();
39827 box.height += this.split.el.getHeight();
39832 updateBox : function(box){
39833 if(this.split && !this.collapsed){
39834 box.height -= this.split.el.getHeight();
39835 this.split.el.setLeft(box.x);
39836 this.split.el.setTop(box.y+box.height);
39837 this.split.el.setWidth(box.width);
39839 if(this.collapsed){
39840 this.updateBody(box.width, null);
39842 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39850 Roo.bootstrap.layout.South = function(config){
39851 config.region = 'south';
39852 config.cursor = 's-resize';
39853 Roo.bootstrap.layout.Split.call(this, config);
39855 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39856 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39857 this.split.el.addClass("roo-layout-split-v");
39862 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39863 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39865 onRender : function(ctr, pos)
39867 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39868 var size = this.config.initialSize || this.config.height;
39869 if(this.el && typeof size != "undefined"){
39870 this.el.setHeight(size);
39875 getBox : function(){
39876 if(this.collapsed){
39877 return this.collapsedEl.getBox();
39879 var box = this.el.getBox();
39881 var sh = this.split.el.getHeight();
39888 updateBox : function(box){
39889 if(this.split && !this.collapsed){
39890 var sh = this.split.el.getHeight();
39893 this.split.el.setLeft(box.x);
39894 this.split.el.setTop(box.y-sh);
39895 this.split.el.setWidth(box.width);
39897 if(this.collapsed){
39898 this.updateBody(box.width, null);
39900 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39904 Roo.bootstrap.layout.East = function(config){
39905 config.region = "east";
39906 config.cursor = "e-resize";
39907 Roo.bootstrap.layout.Split.call(this, config);
39909 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39910 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39911 this.split.el.addClass("roo-layout-split-h");
39915 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39916 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39918 onRender : function(ctr, pos)
39920 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39921 var size = this.config.initialSize || this.config.width;
39922 if(this.el && typeof size != "undefined"){
39923 this.el.setWidth(size);
39928 getBox : function(){
39929 if(this.collapsed){
39930 return this.collapsedEl.getBox();
39932 var box = this.el.getBox();
39934 var sw = this.split.el.getWidth();
39941 updateBox : function(box){
39942 if(this.split && !this.collapsed){
39943 var sw = this.split.el.getWidth();
39945 this.split.el.setLeft(box.x);
39946 this.split.el.setTop(box.y);
39947 this.split.el.setHeight(box.height);
39950 if(this.collapsed){
39951 this.updateBody(null, box.height);
39953 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39957 Roo.bootstrap.layout.West = function(config){
39958 config.region = "west";
39959 config.cursor = "w-resize";
39961 Roo.bootstrap.layout.Split.call(this, config);
39963 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39964 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39965 this.split.el.addClass("roo-layout-split-h");
39969 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39970 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39972 onRender: function(ctr, pos)
39974 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39975 var size = this.config.initialSize || this.config.width;
39976 if(typeof size != "undefined"){
39977 this.el.setWidth(size);
39981 getBox : function(){
39982 if(this.collapsed){
39983 return this.collapsedEl.getBox();
39985 var box = this.el.getBox();
39986 if (box.width == 0) {
39987 box.width = this.config.width; // kludge?
39990 box.width += this.split.el.getWidth();
39995 updateBox : function(box){
39996 if(this.split && !this.collapsed){
39997 var sw = this.split.el.getWidth();
39999 this.split.el.setLeft(box.x+box.width);
40000 this.split.el.setTop(box.y);
40001 this.split.el.setHeight(box.height);
40003 if(this.collapsed){
40004 this.updateBody(null, box.height);
40006 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40008 });Roo.namespace("Roo.bootstrap.panel");/*
40010 * Ext JS Library 1.1.1
40011 * Copyright(c) 2006-2007, Ext JS, LLC.
40013 * Originally Released Under LGPL - original licence link has changed is not relivant.
40016 * <script type="text/javascript">
40019 * @class Roo.ContentPanel
40020 * @extends Roo.util.Observable
40021 * A basic ContentPanel element.
40022 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40023 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40024 * @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
40025 * @cfg {Boolean} closable True if the panel can be closed/removed
40026 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40027 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40028 * @cfg {Toolbar} toolbar A toolbar for this panel
40029 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40030 * @cfg {String} title The title for this panel
40031 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40032 * @cfg {String} url Calls {@link #setUrl} with this value
40033 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40034 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40035 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40036 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40037 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40038 * @cfg {Boolean} badges render the badges
40039 * @cfg {String} cls extra classes to use
40040 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40043 * Create a new ContentPanel.
40044 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40045 * @param {String/Object} config A string to set only the title or a config object
40046 * @param {String} content (optional) Set the HTML content for this panel
40047 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40049 Roo.bootstrap.panel.Content = function( config){
40051 this.tpl = config.tpl || false;
40053 var el = config.el;
40054 var content = config.content;
40056 if(config.autoCreate){ // xtype is available if this is called from factory
40059 this.el = Roo.get(el);
40060 if(!this.el && config && config.autoCreate){
40061 if(typeof config.autoCreate == "object"){
40062 if(!config.autoCreate.id){
40063 config.autoCreate.id = config.id||el;
40065 this.el = Roo.DomHelper.append(document.body,
40066 config.autoCreate, true);
40070 cls: (config.cls || '') +
40071 (config.background ? ' bg-' + config.background : '') +
40072 " roo-layout-inactive-content",
40075 if (config.iframe) {
40079 style : 'border: 0px',
40080 src : 'about:blank'
40086 elcfg.html = config.html;
40090 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40091 if (config.iframe) {
40092 this.iframeEl = this.el.select('iframe',true).first();
40097 this.closable = false;
40098 this.loaded = false;
40099 this.active = false;
40102 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40104 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40106 this.wrapEl = this.el; //this.el.wrap();
40108 if (config.toolbar.items) {
40109 ti = config.toolbar.items ;
40110 delete config.toolbar.items ;
40114 this.toolbar.render(this.wrapEl, 'before');
40115 for(var i =0;i < ti.length;i++) {
40116 // Roo.log(['add child', items[i]]);
40117 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40119 this.toolbar.items = nitems;
40120 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40121 delete config.toolbar;
40125 // xtype created footer. - not sure if will work as we normally have to render first..
40126 if (this.footer && !this.footer.el && this.footer.xtype) {
40127 if (!this.wrapEl) {
40128 this.wrapEl = this.el.wrap();
40131 this.footer.container = this.wrapEl.createChild();
40133 this.footer = Roo.factory(this.footer, Roo);
40138 if(typeof config == "string"){
40139 this.title = config;
40141 Roo.apply(this, config);
40145 this.resizeEl = Roo.get(this.resizeEl, true);
40147 this.resizeEl = this.el;
40149 // handle view.xtype
40157 * Fires when this panel is activated.
40158 * @param {Roo.ContentPanel} this
40162 * @event deactivate
40163 * Fires when this panel is activated.
40164 * @param {Roo.ContentPanel} this
40166 "deactivate" : true,
40170 * Fires when this panel is resized if fitToFrame is true.
40171 * @param {Roo.ContentPanel} this
40172 * @param {Number} width The width after any component adjustments
40173 * @param {Number} height The height after any component adjustments
40179 * Fires when this tab is created
40180 * @param {Roo.ContentPanel} this
40191 if(this.autoScroll && !this.iframe){
40192 this.resizeEl.setStyle("overflow", "auto");
40194 // fix randome scrolling
40195 //this.el.on('scroll', function() {
40196 // Roo.log('fix random scolling');
40197 // this.scrollTo('top',0);
40200 content = content || this.content;
40202 this.setContent(content);
40204 if(config && config.url){
40205 this.setUrl(this.url, this.params, this.loadOnce);
40210 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40212 if (this.view && typeof(this.view.xtype) != 'undefined') {
40213 this.view.el = this.el.appendChild(document.createElement("div"));
40214 this.view = Roo.factory(this.view);
40215 this.view.render && this.view.render(false, '');
40219 this.fireEvent('render', this);
40222 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40232 setRegion : function(region){
40233 this.region = region;
40234 this.setActiveClass(region && !this.background);
40238 setActiveClass: function(state)
40241 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40242 this.el.setStyle('position','relative');
40244 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40245 this.el.setStyle('position', 'absolute');
40250 * Returns the toolbar for this Panel if one was configured.
40251 * @return {Roo.Toolbar}
40253 getToolbar : function(){
40254 return this.toolbar;
40257 setActiveState : function(active)
40259 this.active = active;
40260 this.setActiveClass(active);
40262 if(this.fireEvent("deactivate", this) === false){
40267 this.fireEvent("activate", this);
40271 * Updates this panel's element (not for iframe)
40272 * @param {String} content The new content
40273 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40275 setContent : function(content, loadScripts){
40280 this.el.update(content, loadScripts);
40283 ignoreResize : function(w, h){
40284 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40287 this.lastSize = {width: w, height: h};
40292 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40293 * @return {Roo.UpdateManager} The UpdateManager
40295 getUpdateManager : function(){
40299 return this.el.getUpdateManager();
40302 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40303 * Does not work with IFRAME contents
40304 * @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:
40307 url: "your-url.php",
40308 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40309 callback: yourFunction,
40310 scope: yourObject, //(optional scope)
40313 text: "Loading...",
40319 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40320 * 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.
40321 * @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}
40322 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40323 * @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.
40324 * @return {Roo.ContentPanel} this
40332 var um = this.el.getUpdateManager();
40333 um.update.apply(um, arguments);
40339 * 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.
40340 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40341 * @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)
40342 * @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)
40343 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40345 setUrl : function(url, params, loadOnce){
40347 this.iframeEl.dom.src = url;
40351 if(this.refreshDelegate){
40352 this.removeListener("activate", this.refreshDelegate);
40354 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40355 this.on("activate", this.refreshDelegate);
40356 return this.el.getUpdateManager();
40359 _handleRefresh : function(url, params, loadOnce){
40360 if(!loadOnce || !this.loaded){
40361 var updater = this.el.getUpdateManager();
40362 updater.update(url, params, this._setLoaded.createDelegate(this));
40366 _setLoaded : function(){
40367 this.loaded = true;
40371 * Returns this panel's id
40374 getId : function(){
40379 * Returns this panel's element - used by regiosn to add.
40380 * @return {Roo.Element}
40382 getEl : function(){
40383 return this.wrapEl || this.el;
40388 adjustForComponents : function(width, height)
40390 //Roo.log('adjustForComponents ');
40391 if(this.resizeEl != this.el){
40392 width -= this.el.getFrameWidth('lr');
40393 height -= this.el.getFrameWidth('tb');
40396 var te = this.toolbar.getEl();
40397 te.setWidth(width);
40398 height -= te.getHeight();
40401 var te = this.footer.getEl();
40402 te.setWidth(width);
40403 height -= te.getHeight();
40407 if(this.adjustments){
40408 width += this.adjustments[0];
40409 height += this.adjustments[1];
40411 return {"width": width, "height": height};
40414 setSize : function(width, height){
40415 if(this.fitToFrame && !this.ignoreResize(width, height)){
40416 if(this.fitContainer && this.resizeEl != this.el){
40417 this.el.setSize(width, height);
40419 var size = this.adjustForComponents(width, height);
40421 this.iframeEl.setSize(width,height);
40424 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40425 this.fireEvent('resize', this, size.width, size.height);
40432 * Returns this panel's title
40435 getTitle : function(){
40437 if (typeof(this.title) != 'object') {
40442 for (var k in this.title) {
40443 if (!this.title.hasOwnProperty(k)) {
40447 if (k.indexOf('-') >= 0) {
40448 var s = k.split('-');
40449 for (var i = 0; i<s.length; i++) {
40450 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40453 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40460 * Set this panel's title
40461 * @param {String} title
40463 setTitle : function(title){
40464 this.title = title;
40466 this.region.updatePanelTitle(this, title);
40471 * Returns true is this panel was configured to be closable
40472 * @return {Boolean}
40474 isClosable : function(){
40475 return this.closable;
40478 beforeSlide : function(){
40480 this.resizeEl.clip();
40483 afterSlide : function(){
40485 this.resizeEl.unclip();
40489 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40490 * Will fail silently if the {@link #setUrl} method has not been called.
40491 * This does not activate the panel, just updates its content.
40493 refresh : function(){
40494 if(this.refreshDelegate){
40495 this.loaded = false;
40496 this.refreshDelegate();
40501 * Destroys this panel
40503 destroy : function(){
40504 this.el.removeAllListeners();
40505 var tempEl = document.createElement("span");
40506 tempEl.appendChild(this.el.dom);
40507 tempEl.innerHTML = "";
40513 * form - if the content panel contains a form - this is a reference to it.
40514 * @type {Roo.form.Form}
40518 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40519 * This contains a reference to it.
40525 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40535 * @param {Object} cfg Xtype definition of item to add.
40539 getChildContainer: function () {
40540 return this.getEl();
40545 var ret = new Roo.factory(cfg);
40550 if (cfg.xtype.match(/^Form$/)) {
40553 //if (this.footer) {
40554 // el = this.footer.container.insertSibling(false, 'before');
40556 el = this.el.createChild();
40559 this.form = new Roo.form.Form(cfg);
40562 if ( this.form.allItems.length) {
40563 this.form.render(el.dom);
40567 // should only have one of theses..
40568 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40569 // views.. should not be just added - used named prop 'view''
40571 cfg.el = this.el.appendChild(document.createElement("div"));
40574 var ret = new Roo.factory(cfg);
40576 ret.render && ret.render(false, ''); // render blank..
40586 * @class Roo.bootstrap.panel.Grid
40587 * @extends Roo.bootstrap.panel.Content
40589 * Create a new GridPanel.
40590 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40591 * @param {Object} config A the config object
40597 Roo.bootstrap.panel.Grid = function(config)
40601 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40602 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40604 config.el = this.wrapper;
40605 //this.el = this.wrapper;
40607 if (config.container) {
40608 // ctor'ed from a Border/panel.grid
40611 this.wrapper.setStyle("overflow", "hidden");
40612 this.wrapper.addClass('roo-grid-container');
40617 if(config.toolbar){
40618 var tool_el = this.wrapper.createChild();
40619 this.toolbar = Roo.factory(config.toolbar);
40621 if (config.toolbar.items) {
40622 ti = config.toolbar.items ;
40623 delete config.toolbar.items ;
40627 this.toolbar.render(tool_el);
40628 for(var i =0;i < ti.length;i++) {
40629 // Roo.log(['add child', items[i]]);
40630 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40632 this.toolbar.items = nitems;
40634 delete config.toolbar;
40637 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40638 config.grid.scrollBody = true;;
40639 config.grid.monitorWindowResize = false; // turn off autosizing
40640 config.grid.autoHeight = false;
40641 config.grid.autoWidth = false;
40643 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40645 if (config.background) {
40646 // render grid on panel activation (if panel background)
40647 this.on('activate', function(gp) {
40648 if (!gp.grid.rendered) {
40649 gp.grid.render(this.wrapper);
40650 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40655 this.grid.render(this.wrapper);
40656 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40659 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40660 // ??? needed ??? config.el = this.wrapper;
40665 // xtype created footer. - not sure if will work as we normally have to render first..
40666 if (this.footer && !this.footer.el && this.footer.xtype) {
40668 var ctr = this.grid.getView().getFooterPanel(true);
40669 this.footer.dataSource = this.grid.dataSource;
40670 this.footer = Roo.factory(this.footer, Roo);
40671 this.footer.render(ctr);
40681 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40682 getId : function(){
40683 return this.grid.id;
40687 * Returns the grid for this panel
40688 * @return {Roo.bootstrap.Table}
40690 getGrid : function(){
40694 setSize : function(width, height){
40695 if(!this.ignoreResize(width, height)){
40696 var grid = this.grid;
40697 var size = this.adjustForComponents(width, height);
40698 // tfoot is not a footer?
40701 var gridel = grid.getGridEl();
40702 gridel.setSize(size.width, size.height);
40704 var tbd = grid.getGridEl().select('tbody', true).first();
40705 var thd = grid.getGridEl().select('thead',true).first();
40706 var tbf= grid.getGridEl().select('tfoot', true).first();
40709 size.height -= tbf.getHeight();
40712 size.height -= thd.getHeight();
40715 tbd.setSize(size.width, size.height );
40716 // this is for the account management tab -seems to work there.
40717 var thd = grid.getGridEl().select('thead',true).first();
40719 // tbd.setSize(size.width, size.height - thd.getHeight());
40728 beforeSlide : function(){
40729 this.grid.getView().scroller.clip();
40732 afterSlide : function(){
40733 this.grid.getView().scroller.unclip();
40736 destroy : function(){
40737 this.grid.destroy();
40739 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40744 * @class Roo.bootstrap.panel.Nest
40745 * @extends Roo.bootstrap.panel.Content
40747 * Create a new Panel, that can contain a layout.Border.
40750 * @param {Roo.BorderLayout} layout The layout for this panel
40751 * @param {String/Object} config A string to set only the title or a config object
40753 Roo.bootstrap.panel.Nest = function(config)
40755 // construct with only one argument..
40756 /* FIXME - implement nicer consturctors
40757 if (layout.layout) {
40759 layout = config.layout;
40760 delete config.layout;
40762 if (layout.xtype && !layout.getEl) {
40763 // then layout needs constructing..
40764 layout = Roo.factory(layout, Roo);
40768 config.el = config.layout.getEl();
40770 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40772 config.layout.monitorWindowResize = false; // turn off autosizing
40773 this.layout = config.layout;
40774 this.layout.getEl().addClass("roo-layout-nested-layout");
40775 this.layout.parent = this;
40782 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40784 setSize : function(width, height){
40785 if(!this.ignoreResize(width, height)){
40786 var size = this.adjustForComponents(width, height);
40787 var el = this.layout.getEl();
40788 if (size.height < 1) {
40789 el.setWidth(size.width);
40791 el.setSize(size.width, size.height);
40793 var touch = el.dom.offsetWidth;
40794 this.layout.layout();
40795 // ie requires a double layout on the first pass
40796 if(Roo.isIE && !this.initialized){
40797 this.initialized = true;
40798 this.layout.layout();
40803 // activate all subpanels if not currently active..
40805 setActiveState : function(active){
40806 this.active = active;
40807 this.setActiveClass(active);
40810 this.fireEvent("deactivate", this);
40814 this.fireEvent("activate", this);
40815 // not sure if this should happen before or after..
40816 if (!this.layout) {
40817 return; // should not happen..
40820 for (var r in this.layout.regions) {
40821 reg = this.layout.getRegion(r);
40822 if (reg.getActivePanel()) {
40823 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40824 reg.setActivePanel(reg.getActivePanel());
40827 if (!reg.panels.length) {
40830 reg.showPanel(reg.getPanel(0));
40839 * Returns the nested BorderLayout for this panel
40840 * @return {Roo.BorderLayout}
40842 getLayout : function(){
40843 return this.layout;
40847 * Adds a xtype elements to the layout of the nested panel
40851 xtype : 'ContentPanel',
40858 xtype : 'NestedLayoutPanel',
40864 items : [ ... list of content panels or nested layout panels.. ]
40868 * @param {Object} cfg Xtype definition of item to add.
40870 addxtype : function(cfg) {
40871 return this.layout.addxtype(cfg);
40876 * Ext JS Library 1.1.1
40877 * Copyright(c) 2006-2007, Ext JS, LLC.
40879 * Originally Released Under LGPL - original licence link has changed is not relivant.
40882 * <script type="text/javascript">
40885 * @class Roo.TabPanel
40886 * @extends Roo.util.Observable
40887 * A lightweight tab container.
40891 // basic tabs 1, built from existing content
40892 var tabs = new Roo.TabPanel("tabs1");
40893 tabs.addTab("script", "View Script");
40894 tabs.addTab("markup", "View Markup");
40895 tabs.activate("script");
40897 // more advanced tabs, built from javascript
40898 var jtabs = new Roo.TabPanel("jtabs");
40899 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40901 // set up the UpdateManager
40902 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40903 var updater = tab2.getUpdateManager();
40904 updater.setDefaultUrl("ajax1.htm");
40905 tab2.on('activate', updater.refresh, updater, true);
40907 // Use setUrl for Ajax loading
40908 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40909 tab3.setUrl("ajax2.htm", null, true);
40912 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40915 jtabs.activate("jtabs-1");
40918 * Create a new TabPanel.
40919 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40920 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40922 Roo.bootstrap.panel.Tabs = function(config){
40924 * The container element for this TabPanel.
40925 * @type Roo.Element
40927 this.el = Roo.get(config.el);
40930 if(typeof config == "boolean"){
40931 this.tabPosition = config ? "bottom" : "top";
40933 Roo.apply(this, config);
40937 if(this.tabPosition == "bottom"){
40938 // if tabs are at the bottom = create the body first.
40939 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40940 this.el.addClass("roo-tabs-bottom");
40942 // next create the tabs holders
40944 if (this.tabPosition == "west"){
40946 var reg = this.region; // fake it..
40948 if (!reg.mgr.parent) {
40951 reg = reg.mgr.parent.region;
40953 Roo.log("got nest?");
40955 if (reg.mgr.getRegion('west')) {
40956 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40957 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40958 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40959 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40960 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40968 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40969 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40970 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40971 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40976 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40979 // finally - if tabs are at the top, then create the body last..
40980 if(this.tabPosition != "bottom"){
40981 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40982 * @type Roo.Element
40984 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40985 this.el.addClass("roo-tabs-top");
40989 this.bodyEl.setStyle("position", "relative");
40991 this.active = null;
40992 this.activateDelegate = this.activate.createDelegate(this);
40997 * Fires when the active tab changes
40998 * @param {Roo.TabPanel} this
40999 * @param {Roo.TabPanelItem} activePanel The new active tab
41003 * @event beforetabchange
41004 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41005 * @param {Roo.TabPanel} this
41006 * @param {Object} e Set cancel to true on this object to cancel the tab change
41007 * @param {Roo.TabPanelItem} tab The tab being changed to
41009 "beforetabchange" : true
41012 Roo.EventManager.onWindowResize(this.onResize, this);
41013 this.cpad = this.el.getPadding("lr");
41014 this.hiddenCount = 0;
41017 // toolbar on the tabbar support...
41018 if (this.toolbar) {
41019 alert("no toolbar support yet");
41020 this.toolbar = false;
41022 var tcfg = this.toolbar;
41023 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41024 this.toolbar = new Roo.Toolbar(tcfg);
41025 if (Roo.isSafari) {
41026 var tbl = tcfg.container.child('table', true);
41027 tbl.setAttribute('width', '100%');
41035 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41038 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41040 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41042 tabPosition : "top",
41044 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41046 currentTabWidth : 0,
41048 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41052 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41056 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41058 preferredTabWidth : 175,
41060 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41062 resizeTabs : false,
41064 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41066 monitorResize : true,
41068 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41070 toolbar : false, // set by caller..
41072 region : false, /// set by caller
41074 disableTooltips : true, // not used yet...
41077 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41078 * @param {String} id The id of the div to use <b>or create</b>
41079 * @param {String} text The text for the tab
41080 * @param {String} content (optional) Content to put in the TabPanelItem body
41081 * @param {Boolean} closable (optional) True to create a close icon on the tab
41082 * @return {Roo.TabPanelItem} The created TabPanelItem
41084 addTab : function(id, text, content, closable, tpl)
41086 var item = new Roo.bootstrap.panel.TabItem({
41090 closable : closable,
41093 this.addTabItem(item);
41095 item.setContent(content);
41101 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41102 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41103 * @return {Roo.TabPanelItem}
41105 getTab : function(id){
41106 return this.items[id];
41110 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41111 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41113 hideTab : function(id){
41114 var t = this.items[id];
41117 this.hiddenCount++;
41118 this.autoSizeTabs();
41123 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41124 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41126 unhideTab : function(id){
41127 var t = this.items[id];
41129 t.setHidden(false);
41130 this.hiddenCount--;
41131 this.autoSizeTabs();
41136 * Adds an existing {@link Roo.TabPanelItem}.
41137 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41139 addTabItem : function(item)
41141 this.items[item.id] = item;
41142 this.items.push(item);
41143 this.autoSizeTabs();
41144 // if(this.resizeTabs){
41145 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41146 // this.autoSizeTabs();
41148 // item.autoSize();
41153 * Removes a {@link Roo.TabPanelItem}.
41154 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41156 removeTab : function(id){
41157 var items = this.items;
41158 var tab = items[id];
41159 if(!tab) { return; }
41160 var index = items.indexOf(tab);
41161 if(this.active == tab && items.length > 1){
41162 var newTab = this.getNextAvailable(index);
41167 this.stripEl.dom.removeChild(tab.pnode.dom);
41168 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41169 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41171 items.splice(index, 1);
41172 delete this.items[tab.id];
41173 tab.fireEvent("close", tab);
41174 tab.purgeListeners();
41175 this.autoSizeTabs();
41178 getNextAvailable : function(start){
41179 var items = this.items;
41181 // look for a next tab that will slide over to
41182 // replace the one being removed
41183 while(index < items.length){
41184 var item = items[++index];
41185 if(item && !item.isHidden()){
41189 // if one isn't found select the previous tab (on the left)
41192 var item = items[--index];
41193 if(item && !item.isHidden()){
41201 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41202 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41204 disableTab : function(id){
41205 var tab = this.items[id];
41206 if(tab && this.active != tab){
41212 * Enables a {@link Roo.TabPanelItem} that is disabled.
41213 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41215 enableTab : function(id){
41216 var tab = this.items[id];
41221 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41222 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41223 * @return {Roo.TabPanelItem} The TabPanelItem.
41225 activate : function(id)
41227 //Roo.log('activite:' + id);
41229 var tab = this.items[id];
41233 if(tab == this.active || tab.disabled){
41237 this.fireEvent("beforetabchange", this, e, tab);
41238 if(e.cancel !== true && !tab.disabled){
41240 this.active.hide();
41242 this.active = this.items[id];
41243 this.active.show();
41244 this.fireEvent("tabchange", this, this.active);
41250 * Gets the active {@link Roo.TabPanelItem}.
41251 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41253 getActiveTab : function(){
41254 return this.active;
41258 * Updates the tab body element to fit the height of the container element
41259 * for overflow scrolling
41260 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41262 syncHeight : function(targetHeight){
41263 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41264 var bm = this.bodyEl.getMargins();
41265 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41266 this.bodyEl.setHeight(newHeight);
41270 onResize : function(){
41271 if(this.monitorResize){
41272 this.autoSizeTabs();
41277 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41279 beginUpdate : function(){
41280 this.updating = true;
41284 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41286 endUpdate : function(){
41287 this.updating = false;
41288 this.autoSizeTabs();
41292 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41294 autoSizeTabs : function()
41296 var count = this.items.length;
41297 var vcount = count - this.hiddenCount;
41300 this.stripEl.hide();
41302 this.stripEl.show();
41305 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41310 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41311 var availWidth = Math.floor(w / vcount);
41312 var b = this.stripBody;
41313 if(b.getWidth() > w){
41314 var tabs = this.items;
41315 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41316 if(availWidth < this.minTabWidth){
41317 /*if(!this.sleft){ // incomplete scrolling code
41318 this.createScrollButtons();
41321 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41324 if(this.currentTabWidth < this.preferredTabWidth){
41325 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41331 * Returns the number of tabs in this TabPanel.
41334 getCount : function(){
41335 return this.items.length;
41339 * Resizes all the tabs to the passed width
41340 * @param {Number} The new width
41342 setTabWidth : function(width){
41343 this.currentTabWidth = width;
41344 for(var i = 0, len = this.items.length; i < len; i++) {
41345 if(!this.items[i].isHidden()) {
41346 this.items[i].setWidth(width);
41352 * Destroys this TabPanel
41353 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41355 destroy : function(removeEl){
41356 Roo.EventManager.removeResizeListener(this.onResize, this);
41357 for(var i = 0, len = this.items.length; i < len; i++){
41358 this.items[i].purgeListeners();
41360 if(removeEl === true){
41361 this.el.update("");
41366 createStrip : function(container)
41368 var strip = document.createElement("nav");
41369 strip.className = Roo.bootstrap.version == 4 ?
41370 "navbar-light bg-light" :
41371 "navbar navbar-default"; //"x-tabs-wrap";
41372 container.appendChild(strip);
41376 createStripList : function(strip)
41378 // div wrapper for retard IE
41379 // returns the "tr" element.
41380 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41381 //'<div class="x-tabs-strip-wrap">'+
41382 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41383 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41384 return strip.firstChild; //.firstChild.firstChild.firstChild;
41386 createBody : function(container)
41388 var body = document.createElement("div");
41389 Roo.id(body, "tab-body");
41390 //Roo.fly(body).addClass("x-tabs-body");
41391 Roo.fly(body).addClass("tab-content");
41392 container.appendChild(body);
41395 createItemBody :function(bodyEl, id){
41396 var body = Roo.getDom(id);
41398 body = document.createElement("div");
41401 //Roo.fly(body).addClass("x-tabs-item-body");
41402 Roo.fly(body).addClass("tab-pane");
41403 bodyEl.insertBefore(body, bodyEl.firstChild);
41407 createStripElements : function(stripEl, text, closable, tpl)
41409 var td = document.createElement("li"); // was td..
41410 td.className = 'nav-item';
41412 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41415 stripEl.appendChild(td);
41417 td.className = "x-tabs-closable";
41418 if(!this.closeTpl){
41419 this.closeTpl = new Roo.Template(
41420 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41421 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41422 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41425 var el = this.closeTpl.overwrite(td, {"text": text});
41426 var close = el.getElementsByTagName("div")[0];
41427 var inner = el.getElementsByTagName("em")[0];
41428 return {"el": el, "close": close, "inner": inner};
41431 // not sure what this is..
41432 // if(!this.tabTpl){
41433 //this.tabTpl = 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></em></span></a>'
41437 // this.tabTpl = new Roo.Template(
41438 // '<a href="#">' +
41439 // '<span unselectable="on"' +
41440 // (this.disableTooltips ? '' : ' title="{text}"') +
41441 // ' >{text}</span></a>'
41447 var template = tpl || this.tabTpl || false;
41450 template = new Roo.Template(
41451 Roo.bootstrap.version == 4 ?
41453 '<a class="nav-link" href="#" unselectable="on"' +
41454 (this.disableTooltips ? '' : ' title="{text}"') +
41457 '<a class="nav-link" href="#">' +
41458 '<span unselectable="on"' +
41459 (this.disableTooltips ? '' : ' title="{text}"') +
41460 ' >{text}</span></a>'
41465 switch (typeof(template)) {
41469 template = new Roo.Template(template);
41475 var el = template.overwrite(td, {"text": text});
41477 var inner = el.getElementsByTagName("span")[0];
41479 return {"el": el, "inner": inner};
41487 * @class Roo.TabPanelItem
41488 * @extends Roo.util.Observable
41489 * Represents an individual item (tab plus body) in a TabPanel.
41490 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41491 * @param {String} id The id of this TabPanelItem
41492 * @param {String} text The text for the tab of this TabPanelItem
41493 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41495 Roo.bootstrap.panel.TabItem = function(config){
41497 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41498 * @type Roo.TabPanel
41500 this.tabPanel = config.panel;
41502 * The id for this TabPanelItem
41505 this.id = config.id;
41507 this.disabled = false;
41509 this.text = config.text;
41511 this.loaded = false;
41512 this.closable = config.closable;
41515 * The body element for this TabPanelItem.
41516 * @type Roo.Element
41518 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41519 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41520 this.bodyEl.setStyle("display", "block");
41521 this.bodyEl.setStyle("zoom", "1");
41522 //this.hideAction();
41524 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41526 this.el = Roo.get(els.el);
41527 this.inner = Roo.get(els.inner, true);
41528 this.textEl = Roo.bootstrap.version == 4 ?
41529 this.el : Roo.get(this.el.dom.firstChild, true);
41531 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41532 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41535 // this.el.on("mousedown", this.onTabMouseDown, this);
41536 this.el.on("click", this.onTabClick, this);
41538 if(config.closable){
41539 var c = Roo.get(els.close, true);
41540 c.dom.title = this.closeText;
41541 c.addClassOnOver("close-over");
41542 c.on("click", this.closeClick, this);
41548 * Fires when this tab becomes the active tab.
41549 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41550 * @param {Roo.TabPanelItem} this
41554 * @event beforeclose
41555 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41556 * @param {Roo.TabPanelItem} this
41557 * @param {Object} e Set cancel to true on this object to cancel the close.
41559 "beforeclose": true,
41562 * Fires when this tab is closed.
41563 * @param {Roo.TabPanelItem} this
41567 * @event deactivate
41568 * Fires when this tab is no longer the active tab.
41569 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41570 * @param {Roo.TabPanelItem} this
41572 "deactivate" : true
41574 this.hidden = false;
41576 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41579 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41581 purgeListeners : function(){
41582 Roo.util.Observable.prototype.purgeListeners.call(this);
41583 this.el.removeAllListeners();
41586 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41589 this.status_node.addClass("active");
41592 this.tabPanel.stripWrap.repaint();
41594 this.fireEvent("activate", this.tabPanel, this);
41598 * Returns true if this tab is the active tab.
41599 * @return {Boolean}
41601 isActive : function(){
41602 return this.tabPanel.getActiveTab() == this;
41606 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41609 this.status_node.removeClass("active");
41611 this.fireEvent("deactivate", this.tabPanel, this);
41614 hideAction : function(){
41615 this.bodyEl.hide();
41616 this.bodyEl.setStyle("position", "absolute");
41617 this.bodyEl.setLeft("-20000px");
41618 this.bodyEl.setTop("-20000px");
41621 showAction : function(){
41622 this.bodyEl.setStyle("position", "relative");
41623 this.bodyEl.setTop("");
41624 this.bodyEl.setLeft("");
41625 this.bodyEl.show();
41629 * Set the tooltip for the tab.
41630 * @param {String} tooltip The tab's tooltip
41632 setTooltip : function(text){
41633 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41634 this.textEl.dom.qtip = text;
41635 this.textEl.dom.removeAttribute('title');
41637 this.textEl.dom.title = text;
41641 onTabClick : function(e){
41642 e.preventDefault();
41643 this.tabPanel.activate(this.id);
41646 onTabMouseDown : function(e){
41647 e.preventDefault();
41648 this.tabPanel.activate(this.id);
41651 getWidth : function(){
41652 return this.inner.getWidth();
41655 setWidth : function(width){
41656 var iwidth = width - this.linode.getPadding("lr");
41657 this.inner.setWidth(iwidth);
41658 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41659 this.linode.setWidth(width);
41663 * Show or hide the tab
41664 * @param {Boolean} hidden True to hide or false to show.
41666 setHidden : function(hidden){
41667 this.hidden = hidden;
41668 this.linode.setStyle("display", hidden ? "none" : "");
41672 * Returns true if this tab is "hidden"
41673 * @return {Boolean}
41675 isHidden : function(){
41676 return this.hidden;
41680 * Returns the text for this tab
41683 getText : function(){
41687 autoSize : function(){
41688 //this.el.beginMeasure();
41689 this.textEl.setWidth(1);
41691 * #2804 [new] Tabs in Roojs
41692 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41694 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41695 //this.el.endMeasure();
41699 * Sets the text for the tab (Note: this also sets the tooltip text)
41700 * @param {String} text The tab's text and tooltip
41702 setText : function(text){
41704 this.textEl.update(text);
41705 this.setTooltip(text);
41706 //if(!this.tabPanel.resizeTabs){
41707 // this.autoSize();
41711 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41713 activate : function(){
41714 this.tabPanel.activate(this.id);
41718 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41720 disable : function(){
41721 if(this.tabPanel.active != this){
41722 this.disabled = true;
41723 this.status_node.addClass("disabled");
41728 * Enables this TabPanelItem if it was previously disabled.
41730 enable : function(){
41731 this.disabled = false;
41732 this.status_node.removeClass("disabled");
41736 * Sets the content for this TabPanelItem.
41737 * @param {String} content The content
41738 * @param {Boolean} loadScripts true to look for and load scripts
41740 setContent : function(content, loadScripts){
41741 this.bodyEl.update(content, loadScripts);
41745 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41746 * @return {Roo.UpdateManager} The UpdateManager
41748 getUpdateManager : function(){
41749 return this.bodyEl.getUpdateManager();
41753 * Set a URL to be used to load the content for this TabPanelItem.
41754 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41755 * @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)
41756 * @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)
41757 * @return {Roo.UpdateManager} The UpdateManager
41759 setUrl : function(url, params, loadOnce){
41760 if(this.refreshDelegate){
41761 this.un('activate', this.refreshDelegate);
41763 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41764 this.on("activate", this.refreshDelegate);
41765 return this.bodyEl.getUpdateManager();
41769 _handleRefresh : function(url, params, loadOnce){
41770 if(!loadOnce || !this.loaded){
41771 var updater = this.bodyEl.getUpdateManager();
41772 updater.update(url, params, this._setLoaded.createDelegate(this));
41777 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41778 * Will fail silently if the setUrl method has not been called.
41779 * This does not activate the panel, just updates its content.
41781 refresh : function(){
41782 if(this.refreshDelegate){
41783 this.loaded = false;
41784 this.refreshDelegate();
41789 _setLoaded : function(){
41790 this.loaded = true;
41794 closeClick : function(e){
41797 this.fireEvent("beforeclose", this, o);
41798 if(o.cancel !== true){
41799 this.tabPanel.removeTab(this.id);
41803 * The text displayed in the tooltip for the close icon.
41806 closeText : "Close this tab"
41809 * This script refer to:
41810 * Title: International Telephone Input
41811 * Author: Jack O'Connor
41812 * Code version: v12.1.12
41813 * Availability: https://github.com/jackocnr/intl-tel-input.git
41816 Roo.bootstrap.PhoneInputData = function() {
41819 "Afghanistan (افغانستان)",
41824 "Albania (Shqipëri)",
41829 "Algeria (الجزائر)",
41854 "Antigua and Barbuda",
41864 "Armenia (Հայաստան)",
41880 "Austria (Österreich)",
41885 "Azerbaijan (Azərbaycan)",
41895 "Bahrain (البحرين)",
41900 "Bangladesh (বাংলাদেশ)",
41910 "Belarus (Беларусь)",
41915 "Belgium (België)",
41945 "Bosnia and Herzegovina (Босна и Херцеговина)",
41960 "British Indian Ocean Territory",
41965 "British Virgin Islands",
41975 "Bulgaria (България)",
41985 "Burundi (Uburundi)",
41990 "Cambodia (កម្ពុជា)",
41995 "Cameroon (Cameroun)",
42004 ["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"]
42007 "Cape Verde (Kabu Verdi)",
42012 "Caribbean Netherlands",
42023 "Central African Republic (République centrafricaine)",
42043 "Christmas Island",
42049 "Cocos (Keeling) Islands",
42060 "Comoros (جزر القمر)",
42065 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42070 "Congo (Republic) (Congo-Brazzaville)",
42090 "Croatia (Hrvatska)",
42111 "Czech Republic (Česká republika)",
42116 "Denmark (Danmark)",
42131 "Dominican Republic (República Dominicana)",
42135 ["809", "829", "849"]
42153 "Equatorial Guinea (Guinea Ecuatorial)",
42173 "Falkland Islands (Islas Malvinas)",
42178 "Faroe Islands (Føroyar)",
42199 "French Guiana (Guyane française)",
42204 "French Polynesia (Polynésie française)",
42219 "Georgia (საქართველო)",
42224 "Germany (Deutschland)",
42244 "Greenland (Kalaallit Nunaat)",
42281 "Guinea-Bissau (Guiné Bissau)",
42306 "Hungary (Magyarország)",
42311 "Iceland (Ísland)",
42331 "Iraq (العراق)",
42347 "Israel (ישראל)",
42374 "Jordan (الأردن)",
42379 "Kazakhstan (Казахстан)",
42400 "Kuwait (الكويت)",
42405 "Kyrgyzstan (Кыргызстан)",
42415 "Latvia (Latvija)",
42420 "Lebanon (لبنان)",
42435 "Libya (ليبيا)",
42445 "Lithuania (Lietuva)",
42460 "Macedonia (FYROM) (Македонија)",
42465 "Madagascar (Madagasikara)",
42495 "Marshall Islands",
42505 "Mauritania (موريتانيا)",
42510 "Mauritius (Moris)",
42531 "Moldova (Republica Moldova)",
42541 "Mongolia (Монгол)",
42546 "Montenegro (Crna Gora)",
42556 "Morocco (المغرب)",
42562 "Mozambique (Moçambique)",
42567 "Myanmar (Burma) (မြန်မာ)",
42572 "Namibia (Namibië)",
42587 "Netherlands (Nederland)",
42592 "New Caledonia (Nouvelle-Calédonie)",
42627 "North Korea (조선 민주주의 인민 공화국)",
42632 "Northern Mariana Islands",
42648 "Pakistan (پاکستان)",
42658 "Palestine (فلسطين)",
42668 "Papua New Guinea",
42710 "Réunion (La Réunion)",
42716 "Romania (România)",
42732 "Saint Barthélemy",
42743 "Saint Kitts and Nevis",
42753 "Saint Martin (Saint-Martin (partie française))",
42759 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42764 "Saint Vincent and the Grenadines",
42779 "São Tomé and Príncipe (São Tomé e Príncipe)",
42784 "Saudi Arabia (المملكة العربية السعودية)",
42789 "Senegal (Sénégal)",
42819 "Slovakia (Slovensko)",
42824 "Slovenia (Slovenija)",
42834 "Somalia (Soomaaliya)",
42844 "South Korea (대한민국)",
42849 "South Sudan (جنوب السودان)",
42859 "Sri Lanka (ශ්රී ලංකාව)",
42864 "Sudan (السودان)",
42874 "Svalbard and Jan Mayen",
42885 "Sweden (Sverige)",
42890 "Switzerland (Schweiz)",
42895 "Syria (سوريا)",
42940 "Trinidad and Tobago",
42945 "Tunisia (تونس)",
42950 "Turkey (Türkiye)",
42960 "Turks and Caicos Islands",
42970 "U.S. Virgin Islands",
42980 "Ukraine (Україна)",
42985 "United Arab Emirates (الإمارات العربية المتحدة)",
43007 "Uzbekistan (Oʻzbekiston)",
43017 "Vatican City (Città del Vaticano)",
43028 "Vietnam (Việt Nam)",
43033 "Wallis and Futuna (Wallis-et-Futuna)",
43038 "Western Sahara (الصحراء الغربية)",
43044 "Yemen (اليمن)",
43068 * This script refer to:
43069 * Title: International Telephone Input
43070 * Author: Jack O'Connor
43071 * Code version: v12.1.12
43072 * Availability: https://github.com/jackocnr/intl-tel-input.git
43076 * @class Roo.bootstrap.PhoneInput
43077 * @extends Roo.bootstrap.TriggerField
43078 * An input with International dial-code selection
43080 * @cfg {String} defaultDialCode default '+852'
43081 * @cfg {Array} preferedCountries default []
43084 * Create a new PhoneInput.
43085 * @param {Object} config Configuration options
43088 Roo.bootstrap.PhoneInput = function(config) {
43089 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43092 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43094 listWidth: undefined,
43096 selectedClass: 'active',
43098 invalidClass : "has-warning",
43100 validClass: 'has-success',
43102 allowed: '0123456789',
43107 * @cfg {String} defaultDialCode The default dial code when initializing the input
43109 defaultDialCode: '+852',
43112 * @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
43114 preferedCountries: false,
43116 getAutoCreate : function()
43118 var data = Roo.bootstrap.PhoneInputData();
43119 var align = this.labelAlign || this.parentLabelAlign();
43122 this.allCountries = [];
43123 this.dialCodeMapping = [];
43125 for (var i = 0; i < data.length; i++) {
43127 this.allCountries[i] = {
43131 priority: c[3] || 0,
43132 areaCodes: c[4] || null
43134 this.dialCodeMapping[c[2]] = {
43137 priority: c[3] || 0,
43138 areaCodes: c[4] || null
43150 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43151 maxlength: this.max_length,
43152 cls : 'form-control tel-input',
43153 autocomplete: 'new-password'
43156 var hiddenInput = {
43159 cls: 'hidden-tel-input'
43163 hiddenInput.name = this.name;
43166 if (this.disabled) {
43167 input.disabled = true;
43170 var flag_container = {
43187 cls: this.hasFeedback ? 'has-feedback' : '',
43193 cls: 'dial-code-holder',
43200 cls: 'roo-select2-container input-group',
43207 if (this.fieldLabel.length) {
43210 tooltip: 'This field is required'
43216 cls: 'control-label',
43222 html: this.fieldLabel
43225 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43231 if(this.indicatorpos == 'right') {
43232 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43239 if(align == 'left') {
43247 if(this.labelWidth > 12){
43248 label.style = "width: " + this.labelWidth + 'px';
43250 if(this.labelWidth < 13 && this.labelmd == 0){
43251 this.labelmd = this.labelWidth;
43253 if(this.labellg > 0){
43254 label.cls += ' col-lg-' + this.labellg;
43255 input.cls += ' col-lg-' + (12 - this.labellg);
43257 if(this.labelmd > 0){
43258 label.cls += ' col-md-' + this.labelmd;
43259 container.cls += ' col-md-' + (12 - this.labelmd);
43261 if(this.labelsm > 0){
43262 label.cls += ' col-sm-' + this.labelsm;
43263 container.cls += ' col-sm-' + (12 - this.labelsm);
43265 if(this.labelxs > 0){
43266 label.cls += ' col-xs-' + this.labelxs;
43267 container.cls += ' col-xs-' + (12 - this.labelxs);
43277 var settings = this;
43279 ['xs','sm','md','lg'].map(function(size){
43280 if (settings[size]) {
43281 cfg.cls += ' col-' + size + '-' + settings[size];
43285 this.store = new Roo.data.Store({
43286 proxy : new Roo.data.MemoryProxy({}),
43287 reader : new Roo.data.JsonReader({
43298 'name' : 'dialCode',
43302 'name' : 'priority',
43306 'name' : 'areaCodes',
43313 if(!this.preferedCountries) {
43314 this.preferedCountries = [
43321 var p = this.preferedCountries.reverse();
43324 for (var i = 0; i < p.length; i++) {
43325 for (var j = 0; j < this.allCountries.length; j++) {
43326 if(this.allCountries[j].iso2 == p[i]) {
43327 var t = this.allCountries[j];
43328 this.allCountries.splice(j,1);
43329 this.allCountries.unshift(t);
43335 this.store.proxy.data = {
43337 data: this.allCountries
43343 initEvents : function()
43346 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43348 this.indicator = this.indicatorEl();
43349 this.flag = this.flagEl();
43350 this.dialCodeHolder = this.dialCodeHolderEl();
43352 this.trigger = this.el.select('div.flag-box',true).first();
43353 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43358 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43359 _this.list.setWidth(lw);
43362 this.list.on('mouseover', this.onViewOver, this);
43363 this.list.on('mousemove', this.onViewMove, this);
43364 this.inputEl().on("keyup", this.onKeyUp, this);
43365 this.inputEl().on("keypress", this.onKeyPress, this);
43367 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43369 this.view = new Roo.View(this.list, this.tpl, {
43370 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43373 this.view.on('click', this.onViewClick, this);
43374 this.setValue(this.defaultDialCode);
43377 onTriggerClick : function(e)
43379 Roo.log('trigger click');
43384 if(this.isExpanded()){
43386 this.hasFocus = false;
43388 this.store.load({});
43389 this.hasFocus = true;
43394 isExpanded : function()
43396 return this.list.isVisible();
43399 collapse : function()
43401 if(!this.isExpanded()){
43405 Roo.get(document).un('mousedown', this.collapseIf, this);
43406 Roo.get(document).un('mousewheel', this.collapseIf, this);
43407 this.fireEvent('collapse', this);
43411 expand : function()
43415 if(this.isExpanded() || !this.hasFocus){
43419 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43420 this.list.setWidth(lw);
43423 this.restrictHeight();
43425 Roo.get(document).on('mousedown', this.collapseIf, this);
43426 Roo.get(document).on('mousewheel', this.collapseIf, this);
43428 this.fireEvent('expand', this);
43431 restrictHeight : function()
43433 this.list.alignTo(this.inputEl(), this.listAlign);
43434 this.list.alignTo(this.inputEl(), this.listAlign);
43437 onViewOver : function(e, t)
43439 if(this.inKeyMode){
43442 var item = this.view.findItemFromChild(t);
43445 var index = this.view.indexOf(item);
43446 this.select(index, false);
43451 onViewClick : function(view, doFocus, el, e)
43453 var index = this.view.getSelectedIndexes()[0];
43455 var r = this.store.getAt(index);
43458 this.onSelect(r, index);
43460 if(doFocus !== false && !this.blockFocus){
43461 this.inputEl().focus();
43465 onViewMove : function(e, t)
43467 this.inKeyMode = false;
43470 select : function(index, scrollIntoView)
43472 this.selectedIndex = index;
43473 this.view.select(index);
43474 if(scrollIntoView !== false){
43475 var el = this.view.getNode(index);
43477 this.list.scrollChildIntoView(el, false);
43482 createList : function()
43484 this.list = Roo.get(document.body).createChild({
43486 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43487 style: 'display:none'
43490 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43493 collapseIf : function(e)
43495 var in_combo = e.within(this.el);
43496 var in_list = e.within(this.list);
43497 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43499 if (in_combo || in_list || is_list) {
43505 onSelect : function(record, index)
43507 if(this.fireEvent('beforeselect', this, record, index) !== false){
43509 this.setFlagClass(record.data.iso2);
43510 this.setDialCode(record.data.dialCode);
43511 this.hasFocus = false;
43513 this.fireEvent('select', this, record, index);
43517 flagEl : function()
43519 var flag = this.el.select('div.flag',true).first();
43526 dialCodeHolderEl : function()
43528 var d = this.el.select('input.dial-code-holder',true).first();
43535 setDialCode : function(v)
43537 this.dialCodeHolder.dom.value = '+'+v;
43540 setFlagClass : function(n)
43542 this.flag.dom.className = 'flag '+n;
43545 getValue : function()
43547 var v = this.inputEl().getValue();
43548 if(this.dialCodeHolder) {
43549 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43554 setValue : function(v)
43556 var d = this.getDialCode(v);
43558 //invalid dial code
43559 if(v.length == 0 || !d || d.length == 0) {
43561 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43562 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43568 this.setFlagClass(this.dialCodeMapping[d].iso2);
43569 this.setDialCode(d);
43570 this.inputEl().dom.value = v.replace('+'+d,'');
43571 this.hiddenEl().dom.value = this.getValue();
43576 getDialCode : function(v)
43580 if (v.length == 0) {
43581 return this.dialCodeHolder.dom.value;
43585 if (v.charAt(0) != "+") {
43588 var numericChars = "";
43589 for (var i = 1; i < v.length; i++) {
43590 var c = v.charAt(i);
43593 if (this.dialCodeMapping[numericChars]) {
43594 dialCode = v.substr(1, i);
43596 if (numericChars.length == 4) {
43606 this.setValue(this.defaultDialCode);
43610 hiddenEl : function()
43612 return this.el.select('input.hidden-tel-input',true).first();
43615 // after setting val
43616 onKeyUp : function(e){
43617 this.setValue(this.getValue());
43620 onKeyPress : function(e){
43621 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43628 * @class Roo.bootstrap.MoneyField
43629 * @extends Roo.bootstrap.ComboBox
43630 * Bootstrap MoneyField class
43633 * Create a new MoneyField.
43634 * @param {Object} config Configuration options
43637 Roo.bootstrap.MoneyField = function(config) {
43639 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43643 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43646 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43648 allowDecimals : true,
43650 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43652 decimalSeparator : ".",
43654 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43656 decimalPrecision : 0,
43658 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43660 allowNegative : true,
43662 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43666 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43668 minValue : Number.NEGATIVE_INFINITY,
43670 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43672 maxValue : Number.MAX_VALUE,
43674 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43676 minText : "The minimum value for this field is {0}",
43678 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43680 maxText : "The maximum value for this field is {0}",
43682 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43683 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43685 nanText : "{0} is not a valid number",
43687 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43691 * @cfg {String} defaults currency of the MoneyField
43692 * value should be in lkey
43694 defaultCurrency : false,
43696 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43698 thousandsDelimiter : false,
43700 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43711 getAutoCreate : function()
43713 var align = this.labelAlign || this.parentLabelAlign();
43725 cls : 'form-control roo-money-amount-input',
43726 autocomplete: 'new-password'
43729 var hiddenInput = {
43733 cls: 'hidden-number-input'
43736 if(this.max_length) {
43737 input.maxlength = this.max_length;
43741 hiddenInput.name = this.name;
43744 if (this.disabled) {
43745 input.disabled = true;
43748 var clg = 12 - this.inputlg;
43749 var cmd = 12 - this.inputmd;
43750 var csm = 12 - this.inputsm;
43751 var cxs = 12 - this.inputxs;
43755 cls : 'row roo-money-field',
43759 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43763 cls: 'roo-select2-container input-group',
43767 cls : 'form-control roo-money-currency-input',
43768 autocomplete: 'new-password',
43770 name : this.currencyName
43774 cls : 'input-group-addon',
43788 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43792 cls: this.hasFeedback ? 'has-feedback' : '',
43803 if (this.fieldLabel.length) {
43806 tooltip: 'This field is required'
43812 cls: 'control-label',
43818 html: this.fieldLabel
43821 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43827 if(this.indicatorpos == 'right') {
43828 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43835 if(align == 'left') {
43843 if(this.labelWidth > 12){
43844 label.style = "width: " + this.labelWidth + 'px';
43846 if(this.labelWidth < 13 && this.labelmd == 0){
43847 this.labelmd = this.labelWidth;
43849 if(this.labellg > 0){
43850 label.cls += ' col-lg-' + this.labellg;
43851 input.cls += ' col-lg-' + (12 - this.labellg);
43853 if(this.labelmd > 0){
43854 label.cls += ' col-md-' + this.labelmd;
43855 container.cls += ' col-md-' + (12 - this.labelmd);
43857 if(this.labelsm > 0){
43858 label.cls += ' col-sm-' + this.labelsm;
43859 container.cls += ' col-sm-' + (12 - this.labelsm);
43861 if(this.labelxs > 0){
43862 label.cls += ' col-xs-' + this.labelxs;
43863 container.cls += ' col-xs-' + (12 - this.labelxs);
43874 var settings = this;
43876 ['xs','sm','md','lg'].map(function(size){
43877 if (settings[size]) {
43878 cfg.cls += ' col-' + size + '-' + settings[size];
43885 initEvents : function()
43887 this.indicator = this.indicatorEl();
43889 this.initCurrencyEvent();
43891 this.initNumberEvent();
43894 initCurrencyEvent : function()
43897 throw "can not find store for combo";
43900 this.store = Roo.factory(this.store, Roo.data);
43901 this.store.parent = this;
43905 this.triggerEl = this.el.select('.input-group-addon', true).first();
43907 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43912 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43913 _this.list.setWidth(lw);
43916 this.list.on('mouseover', this.onViewOver, this);
43917 this.list.on('mousemove', this.onViewMove, this);
43918 this.list.on('scroll', this.onViewScroll, this);
43921 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43924 this.view = new Roo.View(this.list, this.tpl, {
43925 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43928 this.view.on('click', this.onViewClick, this);
43930 this.store.on('beforeload', this.onBeforeLoad, this);
43931 this.store.on('load', this.onLoad, this);
43932 this.store.on('loadexception', this.onLoadException, this);
43934 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43935 "up" : function(e){
43936 this.inKeyMode = true;
43940 "down" : function(e){
43941 if(!this.isExpanded()){
43942 this.onTriggerClick();
43944 this.inKeyMode = true;
43949 "enter" : function(e){
43952 if(this.fireEvent("specialkey", this, e)){
43953 this.onViewClick(false);
43959 "esc" : function(e){
43963 "tab" : function(e){
43966 if(this.fireEvent("specialkey", this, e)){
43967 this.onViewClick(false);
43975 doRelay : function(foo, bar, hname){
43976 if(hname == 'down' || this.scope.isExpanded()){
43977 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43985 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43989 initNumberEvent : function(e)
43991 this.inputEl().on("keydown" , this.fireKey, this);
43992 this.inputEl().on("focus", this.onFocus, this);
43993 this.inputEl().on("blur", this.onBlur, this);
43995 this.inputEl().relayEvent('keyup', this);
43997 if(this.indicator){
43998 this.indicator.addClass('invisible');
44001 this.originalValue = this.getValue();
44003 if(this.validationEvent == 'keyup'){
44004 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44005 this.inputEl().on('keyup', this.filterValidation, this);
44007 else if(this.validationEvent !== false){
44008 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44011 if(this.selectOnFocus){
44012 this.on("focus", this.preFocus, this);
44015 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44016 this.inputEl().on("keypress", this.filterKeys, this);
44018 this.inputEl().relayEvent('keypress', this);
44021 var allowed = "0123456789";
44023 if(this.allowDecimals){
44024 allowed += this.decimalSeparator;
44027 if(this.allowNegative){
44031 if(this.thousandsDelimiter) {
44035 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44037 var keyPress = function(e){
44039 var k = e.getKey();
44041 var c = e.getCharCode();
44044 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44045 allowed.indexOf(String.fromCharCode(c)) === -1
44051 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44055 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44060 this.inputEl().on("keypress", keyPress, this);
44064 onTriggerClick : function(e)
44071 this.loadNext = false;
44073 if(this.isExpanded()){
44078 this.hasFocus = true;
44080 if(this.triggerAction == 'all') {
44081 this.doQuery(this.allQuery, true);
44085 this.doQuery(this.getRawValue());
44088 getCurrency : function()
44090 var v = this.currencyEl().getValue();
44095 restrictHeight : function()
44097 this.list.alignTo(this.currencyEl(), this.listAlign);
44098 this.list.alignTo(this.currencyEl(), this.listAlign);
44101 onViewClick : function(view, doFocus, el, e)
44103 var index = this.view.getSelectedIndexes()[0];
44105 var r = this.store.getAt(index);
44108 this.onSelect(r, index);
44112 onSelect : function(record, index){
44114 if(this.fireEvent('beforeselect', this, record, index) !== false){
44116 this.setFromCurrencyData(index > -1 ? record.data : false);
44120 this.fireEvent('select', this, record, index);
44124 setFromCurrencyData : function(o)
44128 this.lastCurrency = o;
44130 if (this.currencyField) {
44131 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44133 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44136 this.lastSelectionText = currency;
44138 //setting default currency
44139 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44140 this.setCurrency(this.defaultCurrency);
44144 this.setCurrency(currency);
44147 setFromData : function(o)
44151 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44153 this.setFromCurrencyData(c);
44158 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44160 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44163 this.setValue(value);
44167 setCurrency : function(v)
44169 this.currencyValue = v;
44172 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44177 setValue : function(v)
44179 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44185 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44187 this.inputEl().dom.value = (v == '') ? '' :
44188 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44190 if(!this.allowZero && v === '0') {
44191 this.hiddenEl().dom.value = '';
44192 this.inputEl().dom.value = '';
44199 getRawValue : function()
44201 var v = this.inputEl().getValue();
44206 getValue : function()
44208 return this.fixPrecision(this.parseValue(this.getRawValue()));
44211 parseValue : function(value)
44213 if(this.thousandsDelimiter) {
44215 r = new RegExp(",", "g");
44216 value = value.replace(r, "");
44219 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44220 return isNaN(value) ? '' : value;
44224 fixPrecision : function(value)
44226 if(this.thousandsDelimiter) {
44228 r = new RegExp(",", "g");
44229 value = value.replace(r, "");
44232 var nan = isNaN(value);
44234 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44235 return nan ? '' : value;
44237 return parseFloat(value).toFixed(this.decimalPrecision);
44240 decimalPrecisionFcn : function(v)
44242 return Math.floor(v);
44245 validateValue : function(value)
44247 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44251 var num = this.parseValue(value);
44254 this.markInvalid(String.format(this.nanText, value));
44258 if(num < this.minValue){
44259 this.markInvalid(String.format(this.minText, this.minValue));
44263 if(num > this.maxValue){
44264 this.markInvalid(String.format(this.maxText, this.maxValue));
44271 validate : function()
44273 if(this.disabled || this.allowBlank){
44278 var currency = this.getCurrency();
44280 if(this.validateValue(this.getRawValue()) && currency.length){
44285 this.markInvalid();
44289 getName: function()
44294 beforeBlur : function()
44300 var v = this.parseValue(this.getRawValue());
44307 onBlur : function()
44311 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44312 //this.el.removeClass(this.focusClass);
44315 this.hasFocus = false;
44317 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44321 var v = this.getValue();
44323 if(String(v) !== String(this.startValue)){
44324 this.fireEvent('change', this, v, this.startValue);
44327 this.fireEvent("blur", this);
44330 inputEl : function()
44332 return this.el.select('.roo-money-amount-input', true).first();
44335 currencyEl : function()
44337 return this.el.select('.roo-money-currency-input', true).first();
44340 hiddenEl : function()
44342 return this.el.select('input.hidden-number-input',true).first();
44346 * @class Roo.bootstrap.BezierSignature
44347 * @extends Roo.bootstrap.Component
44348 * Bootstrap BezierSignature class
44349 * This script refer to:
44350 * Title: Signature Pad
44352 * Availability: https://github.com/szimek/signature_pad
44355 * Create a new BezierSignature
44356 * @param {Object} config The config object
44359 Roo.bootstrap.BezierSignature = function(config){
44360 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44366 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44373 mouse_btn_down: true,
44376 * @cfg {int} canvas height
44378 canvas_height: '200px',
44381 * @cfg {float|function} Radius of a single dot.
44386 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44391 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44396 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44401 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44406 * @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.
44408 bg_color: 'rgba(0, 0, 0, 0)',
44411 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44413 dot_color: 'black',
44416 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44418 velocity_filter_weight: 0.7,
44421 * @cfg {function} Callback when stroke begin.
44426 * @cfg {function} Callback when stroke end.
44430 getAutoCreate : function()
44432 var cls = 'roo-signature column';
44435 cls += ' ' + this.cls;
44445 for(var i = 0; i < col_sizes.length; i++) {
44446 if(this[col_sizes[i]]) {
44447 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44457 cls: 'roo-signature-body',
44461 cls: 'roo-signature-body-canvas',
44462 height: this.canvas_height,
44463 width: this.canvas_width
44470 style: 'display: none'
44478 initEvents: function()
44480 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44482 var canvas = this.canvasEl();
44484 // mouse && touch event swapping...
44485 canvas.dom.style.touchAction = 'none';
44486 canvas.dom.style.msTouchAction = 'none';
44488 this.mouse_btn_down = false;
44489 canvas.on('mousedown', this._handleMouseDown, this);
44490 canvas.on('mousemove', this._handleMouseMove, this);
44491 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44493 if (window.PointerEvent) {
44494 canvas.on('pointerdown', this._handleMouseDown, this);
44495 canvas.on('pointermove', this._handleMouseMove, this);
44496 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44499 if ('ontouchstart' in window) {
44500 canvas.on('touchstart', this._handleTouchStart, this);
44501 canvas.on('touchmove', this._handleTouchMove, this);
44502 canvas.on('touchend', this._handleTouchEnd, this);
44505 Roo.EventManager.onWindowResize(this.resize, this, true);
44507 // file input event
44508 this.fileEl().on('change', this.uploadImage, this);
44515 resize: function(){
44517 var canvas = this.canvasEl().dom;
44518 var ctx = this.canvasElCtx();
44519 var img_data = false;
44521 if(canvas.width > 0) {
44522 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44524 // setting canvas width will clean img data
44527 var style = window.getComputedStyle ?
44528 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44530 var padding_left = parseInt(style.paddingLeft) || 0;
44531 var padding_right = parseInt(style.paddingRight) || 0;
44533 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44536 ctx.putImageData(img_data, 0, 0);
44540 _handleMouseDown: function(e)
44542 if (e.browserEvent.which === 1) {
44543 this.mouse_btn_down = true;
44544 this.strokeBegin(e);
44548 _handleMouseMove: function (e)
44550 if (this.mouse_btn_down) {
44551 this.strokeMoveUpdate(e);
44555 _handleMouseUp: function (e)
44557 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44558 this.mouse_btn_down = false;
44563 _handleTouchStart: function (e) {
44565 e.preventDefault();
44566 if (e.browserEvent.targetTouches.length === 1) {
44567 // var touch = e.browserEvent.changedTouches[0];
44568 // this.strokeBegin(touch);
44570 this.strokeBegin(e); // assume e catching the correct xy...
44574 _handleTouchMove: function (e) {
44575 e.preventDefault();
44576 // var touch = event.targetTouches[0];
44577 // _this._strokeMoveUpdate(touch);
44578 this.strokeMoveUpdate(e);
44581 _handleTouchEnd: function (e) {
44582 var wasCanvasTouched = e.target === this.canvasEl().dom;
44583 if (wasCanvasTouched) {
44584 e.preventDefault();
44585 // var touch = event.changedTouches[0];
44586 // _this._strokeEnd(touch);
44591 reset: function () {
44592 this._lastPoints = [];
44593 this._lastVelocity = 0;
44594 this._lastWidth = (this.min_width + this.max_width) / 2;
44595 this.canvasElCtx().fillStyle = this.dot_color;
44598 strokeMoveUpdate: function(e)
44600 this.strokeUpdate(e);
44602 if (this.throttle) {
44603 this.throttleStroke(this.strokeUpdate, this.throttle);
44606 this.strokeUpdate(e);
44610 strokeBegin: function(e)
44612 var newPointGroup = {
44613 color: this.dot_color,
44617 if (typeof this.onBegin === 'function') {
44621 this.curve_data.push(newPointGroup);
44623 this.strokeUpdate(e);
44626 strokeUpdate: function(e)
44628 var rect = this.canvasEl().dom.getBoundingClientRect();
44629 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44630 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44631 var lastPoints = lastPointGroup.points;
44632 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44633 var isLastPointTooClose = lastPoint
44634 ? point.distanceTo(lastPoint) <= this.min_distance
44636 var color = lastPointGroup.color;
44637 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44638 var curve = this.addPoint(point);
44640 this.drawDot({color: color, point: point});
44643 this.drawCurve({color: color, curve: curve});
44653 strokeEnd: function(e)
44655 this.strokeUpdate(e);
44656 if (typeof this.onEnd === 'function') {
44661 addPoint: function (point) {
44662 var _lastPoints = this._lastPoints;
44663 _lastPoints.push(point);
44664 if (_lastPoints.length > 2) {
44665 if (_lastPoints.length === 3) {
44666 _lastPoints.unshift(_lastPoints[0]);
44668 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44669 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44670 _lastPoints.shift();
44676 calculateCurveWidths: function (startPoint, endPoint) {
44677 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44678 (1 - this.velocity_filter_weight) * this._lastVelocity;
44680 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44683 start: this._lastWidth
44686 this._lastVelocity = velocity;
44687 this._lastWidth = newWidth;
44691 drawDot: function (_a) {
44692 var color = _a.color, point = _a.point;
44693 var ctx = this.canvasElCtx();
44694 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44696 this.drawCurveSegment(point.x, point.y, width);
44698 ctx.fillStyle = color;
44702 drawCurve: function (_a) {
44703 var color = _a.color, curve = _a.curve;
44704 var ctx = this.canvasElCtx();
44705 var widthDelta = curve.endWidth - curve.startWidth;
44706 var drawSteps = Math.floor(curve.length()) * 2;
44708 ctx.fillStyle = color;
44709 for (var i = 0; i < drawSteps; i += 1) {
44710 var t = i / drawSteps;
44716 var x = uuu * curve.startPoint.x;
44717 x += 3 * uu * t * curve.control1.x;
44718 x += 3 * u * tt * curve.control2.x;
44719 x += ttt * curve.endPoint.x;
44720 var y = uuu * curve.startPoint.y;
44721 y += 3 * uu * t * curve.control1.y;
44722 y += 3 * u * tt * curve.control2.y;
44723 y += ttt * curve.endPoint.y;
44724 var width = curve.startWidth + ttt * widthDelta;
44725 this.drawCurveSegment(x, y, width);
44731 drawCurveSegment: function (x, y, width) {
44732 var ctx = this.canvasElCtx();
44734 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44735 this.is_empty = false;
44740 var ctx = this.canvasElCtx();
44741 var canvas = this.canvasEl().dom;
44742 ctx.fillStyle = this.bg_color;
44743 ctx.clearRect(0, 0, canvas.width, canvas.height);
44744 ctx.fillRect(0, 0, canvas.width, canvas.height);
44745 this.curve_data = [];
44747 this.is_empty = true;
44752 return this.el.select('input',true).first();
44755 canvasEl: function()
44757 return this.el.select('canvas',true).first();
44760 canvasElCtx: function()
44762 return this.el.select('canvas',true).first().dom.getContext('2d');
44765 getImage: function(type)
44767 if(this.is_empty) {
44772 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44775 drawFromImage: function(img_src)
44777 var img = new Image();
44779 img.onload = function(){
44780 this.canvasElCtx().drawImage(img, 0, 0);
44785 this.is_empty = false;
44788 selectImage: function()
44790 this.fileEl().dom.click();
44793 uploadImage: function(e)
44795 var reader = new FileReader();
44797 reader.onload = function(e){
44798 var img = new Image();
44799 img.onload = function(){
44801 this.canvasElCtx().drawImage(img, 0, 0);
44803 img.src = e.target.result;
44806 reader.readAsDataURL(e.target.files[0]);
44809 // Bezier Point Constructor
44810 Point: (function () {
44811 function Point(x, y, time) {
44814 this.time = time || Date.now();
44816 Point.prototype.distanceTo = function (start) {
44817 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44819 Point.prototype.equals = function (other) {
44820 return this.x === other.x && this.y === other.y && this.time === other.time;
44822 Point.prototype.velocityFrom = function (start) {
44823 return this.time !== start.time
44824 ? this.distanceTo(start) / (this.time - start.time)
44831 // Bezier Constructor
44832 Bezier: (function () {
44833 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44834 this.startPoint = startPoint;
44835 this.control2 = control2;
44836 this.control1 = control1;
44837 this.endPoint = endPoint;
44838 this.startWidth = startWidth;
44839 this.endWidth = endWidth;
44841 Bezier.fromPoints = function (points, widths, scope) {
44842 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44843 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44844 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44846 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44847 var dx1 = s1.x - s2.x;
44848 var dy1 = s1.y - s2.y;
44849 var dx2 = s2.x - s3.x;
44850 var dy2 = s2.y - s3.y;
44851 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44852 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44853 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44854 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44855 var dxm = m1.x - m2.x;
44856 var dym = m1.y - m2.y;
44857 var k = l2 / (l1 + l2);
44858 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44859 var tx = s2.x - cm.x;
44860 var ty = s2.y - cm.y;
44862 c1: new scope.Point(m1.x + tx, m1.y + ty),
44863 c2: new scope.Point(m2.x + tx, m2.y + ty)
44866 Bezier.prototype.length = function () {
44871 for (var i = 0; i <= steps; i += 1) {
44873 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44874 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44876 var xdiff = cx - px;
44877 var ydiff = cy - py;
44878 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44885 Bezier.prototype.point = function (t, start, c1, c2, end) {
44886 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44887 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44888 + (3.0 * c2 * (1.0 - t) * t * t)
44889 + (end * t * t * t);
44894 throttleStroke: function(fn, wait) {
44895 if (wait === void 0) { wait = 250; }
44897 var timeout = null;
44901 var later = function () {
44902 previous = Date.now();
44904 result = fn.apply(storedContext, storedArgs);
44906 storedContext = null;
44910 return function wrapper() {
44912 for (var _i = 0; _i < arguments.length; _i++) {
44913 args[_i] = arguments[_i];
44915 var now = Date.now();
44916 var remaining = wait - (now - previous);
44917 storedContext = this;
44919 if (remaining <= 0 || remaining > wait) {
44921 clearTimeout(timeout);
44925 result = fn.apply(storedContext, storedArgs);
44927 storedContext = null;
44931 else if (!timeout) {
44932 timeout = window.setTimeout(later, remaining);