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 if (!this.viewDate) {
21958 this.viewDate = new Date();
21960 this.viewDate.setUTCDate(1);
21961 if (className.indexOf('month') > -1) {
21962 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21964 var year = parseInt(html, 10) || 0;
21965 this.viewDate.setUTCFullYear(year);
21969 if(this.singleMode){
21970 this.setValue(this.formatDate(this.viewDate));
21981 //Roo.log(className);
21982 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21983 var day = parseInt(html, 10) || 1;
21984 var year = (this.viewDate || new Date()).getUTCFullYear(),
21985 month = (this.viewDate || new Date()).getUTCMonth();
21987 if (className.indexOf('old') > -1) {
21994 } else if (className.indexOf('new') > -1) {
22002 //Roo.log([year,month,day]);
22003 this.date = this.UTCDate(year, month, day,0,0,0,0);
22004 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22006 //Roo.log(this.formatDate(this.date));
22007 this.setValue(this.formatDate(this.date));
22014 setStartDate: function(startDate)
22016 this.startDate = startDate || -Infinity;
22017 if (this.startDate !== -Infinity) {
22018 this.startDate = this.parseDate(this.startDate);
22021 this.updateNavArrows();
22024 setEndDate: function(endDate)
22026 this.endDate = endDate || Infinity;
22027 if (this.endDate !== Infinity) {
22028 this.endDate = this.parseDate(this.endDate);
22031 this.updateNavArrows();
22034 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22036 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22037 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22038 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22040 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22041 return parseInt(d, 10);
22044 this.updateNavArrows();
22047 updateNavArrows: function()
22049 if(this.singleMode){
22053 var d = new Date(this.viewDate),
22054 year = d.getUTCFullYear(),
22055 month = d.getUTCMonth();
22057 Roo.each(this.picker().select('.prev', true).elements, function(v){
22059 switch (this.viewMode) {
22062 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22068 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22075 Roo.each(this.picker().select('.next', true).elements, function(v){
22077 switch (this.viewMode) {
22080 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22086 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22094 moveMonth: function(date, dir)
22099 var new_date = new Date(date.valueOf()),
22100 day = new_date.getUTCDate(),
22101 month = new_date.getUTCMonth(),
22102 mag = Math.abs(dir),
22104 dir = dir > 0 ? 1 : -1;
22107 // If going back one month, make sure month is not current month
22108 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22110 return new_date.getUTCMonth() == month;
22112 // If going forward one month, make sure month is as expected
22113 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22115 return new_date.getUTCMonth() != new_month;
22117 new_month = month + dir;
22118 new_date.setUTCMonth(new_month);
22119 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22120 if (new_month < 0 || new_month > 11) {
22121 new_month = (new_month + 12) % 12;
22124 // For magnitudes >1, move one month at a time...
22125 for (var i=0; i<mag; i++) {
22126 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22127 new_date = this.moveMonth(new_date, dir);
22129 // ...then reset the day, keeping it in the new month
22130 new_month = new_date.getUTCMonth();
22131 new_date.setUTCDate(day);
22133 return new_month != new_date.getUTCMonth();
22136 // Common date-resetting loop -- if date is beyond end of month, make it
22139 new_date.setUTCDate(--day);
22140 new_date.setUTCMonth(new_month);
22145 moveYear: function(date, dir)
22147 return this.moveMonth(date, dir*12);
22150 dateWithinRange: function(date)
22152 return date >= this.startDate && date <= this.endDate;
22158 this.picker().remove();
22161 validateValue : function(value)
22163 if(this.getVisibilityEl().hasClass('hidden')){
22167 if(value.length < 1) {
22168 if(this.allowBlank){
22174 if(value.length < this.minLength){
22177 if(value.length > this.maxLength){
22181 var vt = Roo.form.VTypes;
22182 if(!vt[this.vtype](value, this)){
22186 if(typeof this.validator == "function"){
22187 var msg = this.validator(value);
22193 if(this.regex && !this.regex.test(value)){
22197 if(typeof(this.parseDate(value)) == 'undefined'){
22201 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22205 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22215 this.date = this.viewDate = '';
22217 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22222 Roo.apply(Roo.bootstrap.DateField, {
22233 html: '<i class="fa fa-arrow-left"/>'
22243 html: '<i class="fa fa-arrow-right"/>'
22285 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22286 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22287 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22288 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22289 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22302 navFnc: 'FullYear',
22307 navFnc: 'FullYear',
22312 Roo.apply(Roo.bootstrap.DateField, {
22316 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22320 cls: 'datepicker-days',
22324 cls: 'table-condensed',
22326 Roo.bootstrap.DateField.head,
22330 Roo.bootstrap.DateField.footer
22337 cls: 'datepicker-months',
22341 cls: 'table-condensed',
22343 Roo.bootstrap.DateField.head,
22344 Roo.bootstrap.DateField.content,
22345 Roo.bootstrap.DateField.footer
22352 cls: 'datepicker-years',
22356 cls: 'table-condensed',
22358 Roo.bootstrap.DateField.head,
22359 Roo.bootstrap.DateField.content,
22360 Roo.bootstrap.DateField.footer
22379 * @class Roo.bootstrap.TimeField
22380 * @extends Roo.bootstrap.Input
22381 * Bootstrap DateField class
22385 * Create a new TimeField
22386 * @param {Object} config The config object
22389 Roo.bootstrap.TimeField = function(config){
22390 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22394 * Fires when this field show.
22395 * @param {Roo.bootstrap.DateField} thisthis
22396 * @param {Mixed} date The date value
22401 * Fires when this field hide.
22402 * @param {Roo.bootstrap.DateField} this
22403 * @param {Mixed} date The date value
22408 * Fires when select a date.
22409 * @param {Roo.bootstrap.DateField} this
22410 * @param {Mixed} date The date value
22416 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22419 * @cfg {String} format
22420 * The default time format string which can be overriden for localization support. The format must be
22421 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22425 getAutoCreate : function()
22427 this.after = '<i class="fa far fa-clock"></i>';
22428 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22432 onRender: function(ct, position)
22435 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22437 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22439 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22441 this.pop = this.picker().select('>.datepicker-time',true).first();
22442 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22444 this.picker().on('mousedown', this.onMousedown, this);
22445 this.picker().on('click', this.onClick, this);
22447 this.picker().addClass('datepicker-dropdown');
22452 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22453 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22454 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22455 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22456 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22457 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22461 fireKey: function(e){
22462 if (!this.picker().isVisible()){
22463 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22469 e.preventDefault();
22477 this.onTogglePeriod();
22480 this.onIncrementMinutes();
22483 this.onDecrementMinutes();
22492 onClick: function(e) {
22493 e.stopPropagation();
22494 e.preventDefault();
22497 picker : function()
22499 return this.pickerEl;
22502 fillTime: function()
22504 var time = this.pop.select('tbody', true).first();
22506 time.dom.innerHTML = '';
22521 cls: 'hours-up fa fas fa-chevron-up'
22541 cls: 'minutes-up fa fas fa-chevron-up'
22562 cls: 'timepicker-hour',
22577 cls: 'timepicker-minute',
22592 cls: 'btn btn-primary period',
22614 cls: 'hours-down fa fas fa-chevron-down'
22634 cls: 'minutes-down fa fas fa-chevron-down'
22652 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22659 var hours = this.time.getHours();
22660 var minutes = this.time.getMinutes();
22673 hours = hours - 12;
22677 hours = '0' + hours;
22681 minutes = '0' + minutes;
22684 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22685 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22686 this.pop.select('button', true).first().dom.innerHTML = period;
22692 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22694 var cls = ['bottom'];
22696 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22703 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22707 //this.picker().setXY(20000,20000);
22708 this.picker().addClass(cls.join('-'));
22712 Roo.each(cls, function(c){
22717 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22718 //_this.picker().setTop(_this.inputEl().getHeight());
22722 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22724 //_this.picker().setTop(0 - _this.picker().getHeight());
22729 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22733 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22741 onFocus : function()
22743 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22747 onBlur : function()
22749 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22755 this.picker().show();
22760 this.fireEvent('show', this, this.date);
22765 this.picker().hide();
22768 this.fireEvent('hide', this, this.date);
22771 setTime : function()
22774 this.setValue(this.time.format(this.format));
22776 this.fireEvent('select', this, this.date);
22781 onMousedown: function(e){
22782 e.stopPropagation();
22783 e.preventDefault();
22786 onIncrementHours: function()
22788 Roo.log('onIncrementHours');
22789 this.time = this.time.add(Date.HOUR, 1);
22794 onDecrementHours: function()
22796 Roo.log('onDecrementHours');
22797 this.time = this.time.add(Date.HOUR, -1);
22801 onIncrementMinutes: function()
22803 Roo.log('onIncrementMinutes');
22804 this.time = this.time.add(Date.MINUTE, 1);
22808 onDecrementMinutes: function()
22810 Roo.log('onDecrementMinutes');
22811 this.time = this.time.add(Date.MINUTE, -1);
22815 onTogglePeriod: function()
22817 Roo.log('onTogglePeriod');
22818 this.time = this.time.add(Date.HOUR, 12);
22826 Roo.apply(Roo.bootstrap.TimeField, {
22830 cls: 'datepicker dropdown-menu',
22834 cls: 'datepicker-time',
22838 cls: 'table-condensed',
22867 cls: 'btn btn-info ok',
22895 * @class Roo.bootstrap.MonthField
22896 * @extends Roo.bootstrap.Input
22897 * Bootstrap MonthField class
22899 * @cfg {String} language default en
22902 * Create a new MonthField
22903 * @param {Object} config The config object
22906 Roo.bootstrap.MonthField = function(config){
22907 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22912 * Fires when this field show.
22913 * @param {Roo.bootstrap.MonthField} this
22914 * @param {Mixed} date The date value
22919 * Fires when this field hide.
22920 * @param {Roo.bootstrap.MonthField} this
22921 * @param {Mixed} date The date value
22926 * Fires when select a date.
22927 * @param {Roo.bootstrap.MonthField} this
22928 * @param {String} oldvalue The old value
22929 * @param {String} newvalue The new value
22935 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22937 onRender: function(ct, position)
22940 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22942 this.language = this.language || 'en';
22943 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22944 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22946 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22947 this.isInline = false;
22948 this.isInput = true;
22949 this.component = this.el.select('.add-on', true).first() || false;
22950 this.component = (this.component && this.component.length === 0) ? false : this.component;
22951 this.hasInput = this.component && this.inputEL().length;
22953 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22955 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22957 this.picker().on('mousedown', this.onMousedown, this);
22958 this.picker().on('click', this.onClick, this);
22960 this.picker().addClass('datepicker-dropdown');
22962 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22963 v.setStyle('width', '189px');
22970 if(this.isInline) {
22976 setValue: function(v, suppressEvent)
22978 var o = this.getValue();
22980 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22984 if(suppressEvent !== true){
22985 this.fireEvent('select', this, o, v);
22990 getValue: function()
22995 onClick: function(e)
22997 e.stopPropagation();
22998 e.preventDefault();
23000 var target = e.getTarget();
23002 if(target.nodeName.toLowerCase() === 'i'){
23003 target = Roo.get(target).dom.parentNode;
23006 var nodeName = target.nodeName;
23007 var className = target.className;
23008 var html = target.innerHTML;
23010 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23014 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23016 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23022 picker : function()
23024 return this.pickerEl;
23027 fillMonths: function()
23030 var months = this.picker().select('>.datepicker-months td', true).first();
23032 months.dom.innerHTML = '';
23038 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23041 months.createChild(month);
23050 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23051 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23054 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23055 e.removeClass('active');
23057 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23058 e.addClass('active');
23065 if(this.isInline) {
23069 this.picker().removeClass(['bottom', 'top']);
23071 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23073 * place to the top of element!
23077 this.picker().addClass('top');
23078 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23083 this.picker().addClass('bottom');
23085 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23088 onFocus : function()
23090 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23094 onBlur : function()
23096 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23098 var d = this.inputEl().getValue();
23107 this.picker().show();
23108 this.picker().select('>.datepicker-months', true).first().show();
23112 this.fireEvent('show', this, this.date);
23117 if(this.isInline) {
23120 this.picker().hide();
23121 this.fireEvent('hide', this, this.date);
23125 onMousedown: function(e)
23127 e.stopPropagation();
23128 e.preventDefault();
23133 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23137 fireKey: function(e)
23139 if (!this.picker().isVisible()){
23140 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23151 e.preventDefault();
23155 dir = e.keyCode == 37 ? -1 : 1;
23157 this.vIndex = this.vIndex + dir;
23159 if(this.vIndex < 0){
23163 if(this.vIndex > 11){
23167 if(isNaN(this.vIndex)){
23171 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23177 dir = e.keyCode == 38 ? -1 : 1;
23179 this.vIndex = this.vIndex + dir * 4;
23181 if(this.vIndex < 0){
23185 if(this.vIndex > 11){
23189 if(isNaN(this.vIndex)){
23193 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23198 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23199 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23203 e.preventDefault();
23206 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23207 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23223 this.picker().remove();
23228 Roo.apply(Roo.bootstrap.MonthField, {
23247 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23248 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23253 Roo.apply(Roo.bootstrap.MonthField, {
23257 cls: 'datepicker dropdown-menu roo-dynamic',
23261 cls: 'datepicker-months',
23265 cls: 'table-condensed',
23267 Roo.bootstrap.DateField.content
23287 * @class Roo.bootstrap.CheckBox
23288 * @extends Roo.bootstrap.Input
23289 * Bootstrap CheckBox class
23291 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23292 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23293 * @cfg {String} boxLabel The text that appears beside the checkbox
23294 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23295 * @cfg {Boolean} checked initnal the element
23296 * @cfg {Boolean} inline inline the element (default false)
23297 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23298 * @cfg {String} tooltip label tooltip
23301 * Create a new CheckBox
23302 * @param {Object} config The config object
23305 Roo.bootstrap.CheckBox = function(config){
23306 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23311 * Fires when the element is checked or unchecked.
23312 * @param {Roo.bootstrap.CheckBox} this This input
23313 * @param {Boolean} checked The new checked value
23318 * Fires when the element is click.
23319 * @param {Roo.bootstrap.CheckBox} this This input
23326 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23328 inputType: 'checkbox',
23337 // checkbox success does not make any sense really..
23342 getAutoCreate : function()
23344 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23350 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23353 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23359 type : this.inputType,
23360 value : this.inputValue,
23361 cls : 'roo-' + this.inputType, //'form-box',
23362 placeholder : this.placeholder || ''
23366 if(this.inputType != 'radio'){
23370 cls : 'roo-hidden-value',
23371 value : this.checked ? this.inputValue : this.valueOff
23376 if (this.weight) { // Validity check?
23377 cfg.cls += " " + this.inputType + "-" + this.weight;
23380 if (this.disabled) {
23381 input.disabled=true;
23385 input.checked = this.checked;
23390 input.name = this.name;
23392 if(this.inputType != 'radio'){
23393 hidden.name = this.name;
23394 input.name = '_hidden_' + this.name;
23399 input.cls += ' input-' + this.size;
23404 ['xs','sm','md','lg'].map(function(size){
23405 if (settings[size]) {
23406 cfg.cls += ' col-' + size + '-' + settings[size];
23410 var inputblock = input;
23412 if (this.before || this.after) {
23415 cls : 'input-group',
23420 inputblock.cn.push({
23422 cls : 'input-group-addon',
23427 inputblock.cn.push(input);
23429 if(this.inputType != 'radio'){
23430 inputblock.cn.push(hidden);
23434 inputblock.cn.push({
23436 cls : 'input-group-addon',
23442 var boxLabelCfg = false;
23448 //'for': id, // box label is handled by onclick - so no for...
23450 html: this.boxLabel
23453 boxLabelCfg.tooltip = this.tooltip;
23459 if (align ==='left' && this.fieldLabel.length) {
23460 // Roo.log("left and has label");
23465 cls : 'control-label',
23466 html : this.fieldLabel
23477 cfg.cn[1].cn.push(boxLabelCfg);
23480 if(this.labelWidth > 12){
23481 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23484 if(this.labelWidth < 13 && this.labelmd == 0){
23485 this.labelmd = this.labelWidth;
23488 if(this.labellg > 0){
23489 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23490 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23493 if(this.labelmd > 0){
23494 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23495 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23498 if(this.labelsm > 0){
23499 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23500 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23503 if(this.labelxs > 0){
23504 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23505 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23508 } else if ( this.fieldLabel.length) {
23509 // Roo.log(" label");
23513 tag: this.boxLabel ? 'span' : 'label',
23515 cls: 'control-label box-input-label',
23516 //cls : 'input-group-addon',
23517 html : this.fieldLabel
23524 cfg.cn.push(boxLabelCfg);
23529 // Roo.log(" no label && no align");
23530 cfg.cn = [ inputblock ] ;
23532 cfg.cn.push(boxLabelCfg);
23540 if(this.inputType != 'radio'){
23541 cfg.cn.push(hidden);
23549 * return the real input element.
23551 inputEl: function ()
23553 return this.el.select('input.roo-' + this.inputType,true).first();
23555 hiddenEl: function ()
23557 return this.el.select('input.roo-hidden-value',true).first();
23560 labelEl: function()
23562 return this.el.select('label.control-label',true).first();
23564 /* depricated... */
23568 return this.labelEl();
23571 boxLabelEl: function()
23573 return this.el.select('label.box-label',true).first();
23576 initEvents : function()
23578 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23580 this.inputEl().on('click', this.onClick, this);
23582 if (this.boxLabel) {
23583 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23586 this.startValue = this.getValue();
23589 Roo.bootstrap.CheckBox.register(this);
23593 onClick : function(e)
23595 if(this.fireEvent('click', this, e) !== false){
23596 this.setChecked(!this.checked);
23601 setChecked : function(state,suppressEvent)
23603 this.startValue = this.getValue();
23605 if(this.inputType == 'radio'){
23607 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23608 e.dom.checked = false;
23611 this.inputEl().dom.checked = true;
23613 this.inputEl().dom.value = this.inputValue;
23615 if(suppressEvent !== true){
23616 this.fireEvent('check', this, true);
23624 this.checked = state;
23626 this.inputEl().dom.checked = state;
23629 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23631 if(suppressEvent !== true){
23632 this.fireEvent('check', this, state);
23638 getValue : function()
23640 if(this.inputType == 'radio'){
23641 return this.getGroupValue();
23644 return this.hiddenEl().dom.value;
23648 getGroupValue : function()
23650 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23654 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23657 setValue : function(v,suppressEvent)
23659 if(this.inputType == 'radio'){
23660 this.setGroupValue(v, suppressEvent);
23664 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23669 setGroupValue : function(v, suppressEvent)
23671 this.startValue = this.getValue();
23673 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23674 e.dom.checked = false;
23676 if(e.dom.value == v){
23677 e.dom.checked = true;
23681 if(suppressEvent !== true){
23682 this.fireEvent('check', this, true);
23690 validate : function()
23692 if(this.getVisibilityEl().hasClass('hidden')){
23698 (this.inputType == 'radio' && this.validateRadio()) ||
23699 (this.inputType == 'checkbox' && this.validateCheckbox())
23705 this.markInvalid();
23709 validateRadio : function()
23711 if(this.getVisibilityEl().hasClass('hidden')){
23715 if(this.allowBlank){
23721 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23722 if(!e.dom.checked){
23734 validateCheckbox : function()
23737 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23738 //return (this.getValue() == this.inputValue) ? true : false;
23741 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23749 for(var i in group){
23750 if(group[i].el.isVisible(true)){
23758 for(var i in group){
23763 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23770 * Mark this field as valid
23772 markValid : function()
23776 this.fireEvent('valid', this);
23778 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23781 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23788 if(this.inputType == 'radio'){
23789 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23790 var fg = e.findParent('.form-group', false, true);
23791 if (Roo.bootstrap.version == 3) {
23792 fg.removeClass([_this.invalidClass, _this.validClass]);
23793 fg.addClass(_this.validClass);
23795 fg.removeClass(['is-valid', 'is-invalid']);
23796 fg.addClass('is-valid');
23804 var fg = this.el.findParent('.form-group', false, true);
23805 if (Roo.bootstrap.version == 3) {
23806 fg.removeClass([this.invalidClass, this.validClass]);
23807 fg.addClass(this.validClass);
23809 fg.removeClass(['is-valid', 'is-invalid']);
23810 fg.addClass('is-valid');
23815 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23821 for(var i in group){
23822 var fg = group[i].el.findParent('.form-group', false, true);
23823 if (Roo.bootstrap.version == 3) {
23824 fg.removeClass([this.invalidClass, this.validClass]);
23825 fg.addClass(this.validClass);
23827 fg.removeClass(['is-valid', 'is-invalid']);
23828 fg.addClass('is-valid');
23834 * Mark this field as invalid
23835 * @param {String} msg The validation message
23837 markInvalid : function(msg)
23839 if(this.allowBlank){
23845 this.fireEvent('invalid', this, msg);
23847 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23850 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23854 label.markInvalid();
23857 if(this.inputType == 'radio'){
23859 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23860 var fg = e.findParent('.form-group', false, true);
23861 if (Roo.bootstrap.version == 3) {
23862 fg.removeClass([_this.invalidClass, _this.validClass]);
23863 fg.addClass(_this.invalidClass);
23865 fg.removeClass(['is-invalid', 'is-valid']);
23866 fg.addClass('is-invalid');
23874 var fg = this.el.findParent('.form-group', false, true);
23875 if (Roo.bootstrap.version == 3) {
23876 fg.removeClass([_this.invalidClass, _this.validClass]);
23877 fg.addClass(_this.invalidClass);
23879 fg.removeClass(['is-invalid', 'is-valid']);
23880 fg.addClass('is-invalid');
23885 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23891 for(var i in group){
23892 var fg = group[i].el.findParent('.form-group', false, true);
23893 if (Roo.bootstrap.version == 3) {
23894 fg.removeClass([_this.invalidClass, _this.validClass]);
23895 fg.addClass(_this.invalidClass);
23897 fg.removeClass(['is-invalid', 'is-valid']);
23898 fg.addClass('is-invalid');
23904 clearInvalid : function()
23906 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23908 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23910 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23912 if (label && label.iconEl) {
23913 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23914 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23918 disable : function()
23920 if(this.inputType != 'radio'){
23921 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23928 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23929 _this.getActionEl().addClass(this.disabledClass);
23930 e.dom.disabled = true;
23934 this.disabled = true;
23935 this.fireEvent("disable", this);
23939 enable : function()
23941 if(this.inputType != 'radio'){
23942 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23949 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23950 _this.getActionEl().removeClass(this.disabledClass);
23951 e.dom.disabled = false;
23955 this.disabled = false;
23956 this.fireEvent("enable", this);
23960 setBoxLabel : function(v)
23965 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23971 Roo.apply(Roo.bootstrap.CheckBox, {
23976 * register a CheckBox Group
23977 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23979 register : function(checkbox)
23981 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23982 this.groups[checkbox.groupId] = {};
23985 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23989 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23993 * fetch a CheckBox Group based on the group ID
23994 * @param {string} the group ID
23995 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23997 get: function(groupId) {
23998 if (typeof(this.groups[groupId]) == 'undefined') {
24002 return this.groups[groupId] ;
24015 * @class Roo.bootstrap.Radio
24016 * @extends Roo.bootstrap.Component
24017 * Bootstrap Radio class
24018 * @cfg {String} boxLabel - the label associated
24019 * @cfg {String} value - the value of radio
24022 * Create a new Radio
24023 * @param {Object} config The config object
24025 Roo.bootstrap.Radio = function(config){
24026 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24030 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24036 getAutoCreate : function()
24040 cls : 'form-group radio',
24045 html : this.boxLabel
24053 initEvents : function()
24055 this.parent().register(this);
24057 this.el.on('click', this.onClick, this);
24061 onClick : function(e)
24063 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24064 this.setChecked(true);
24068 setChecked : function(state, suppressEvent)
24070 this.parent().setValue(this.value, suppressEvent);
24074 setBoxLabel : function(v)
24079 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24094 * @class Roo.bootstrap.SecurePass
24095 * @extends Roo.bootstrap.Input
24096 * Bootstrap SecurePass class
24100 * Create a new SecurePass
24101 * @param {Object} config The config object
24104 Roo.bootstrap.SecurePass = function (config) {
24105 // these go here, so the translation tool can replace them..
24107 PwdEmpty: "Please type a password, and then retype it to confirm.",
24108 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24109 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24110 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24111 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24112 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24113 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24114 TooWeak: "Your password is Too Weak."
24116 this.meterLabel = "Password strength:";
24117 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24118 this.meterClass = [
24119 "roo-password-meter-tooweak",
24120 "roo-password-meter-weak",
24121 "roo-password-meter-medium",
24122 "roo-password-meter-strong",
24123 "roo-password-meter-grey"
24128 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24131 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24133 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24135 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24136 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24137 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24138 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24139 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24140 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24141 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24151 * @cfg {String/Object} Label for the strength meter (defaults to
24152 * 'Password strength:')
24157 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24158 * ['Weak', 'Medium', 'Strong'])
24161 pwdStrengths: false,
24174 initEvents: function ()
24176 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24178 if (this.el.is('input[type=password]') && Roo.isSafari) {
24179 this.el.on('keydown', this.SafariOnKeyDown, this);
24182 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24185 onRender: function (ct, position)
24187 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24188 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24189 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24191 this.trigger.createChild({
24196 cls: 'roo-password-meter-grey col-xs-12',
24199 //width: this.meterWidth + 'px'
24203 cls: 'roo-password-meter-text'
24209 if (this.hideTrigger) {
24210 this.trigger.setDisplayed(false);
24212 this.setSize(this.width || '', this.height || '');
24215 onDestroy: function ()
24217 if (this.trigger) {
24218 this.trigger.removeAllListeners();
24219 this.trigger.remove();
24222 this.wrap.remove();
24224 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24227 checkStrength: function ()
24229 var pwd = this.inputEl().getValue();
24230 if (pwd == this._lastPwd) {
24235 if (this.ClientSideStrongPassword(pwd)) {
24237 } else if (this.ClientSideMediumPassword(pwd)) {
24239 } else if (this.ClientSideWeakPassword(pwd)) {
24245 Roo.log('strength1: ' + strength);
24247 //var pm = this.trigger.child('div/div/div').dom;
24248 var pm = this.trigger.child('div/div');
24249 pm.removeClass(this.meterClass);
24250 pm.addClass(this.meterClass[strength]);
24253 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24255 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24257 this._lastPwd = pwd;
24261 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24263 this._lastPwd = '';
24265 var pm = this.trigger.child('div/div');
24266 pm.removeClass(this.meterClass);
24267 pm.addClass('roo-password-meter-grey');
24270 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24273 this.inputEl().dom.type='password';
24276 validateValue: function (value)
24278 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24281 if (value.length == 0) {
24282 if (this.allowBlank) {
24283 this.clearInvalid();
24287 this.markInvalid(this.errors.PwdEmpty);
24288 this.errorMsg = this.errors.PwdEmpty;
24296 if (!value.match(/[\x21-\x7e]+/)) {
24297 this.markInvalid(this.errors.PwdBadChar);
24298 this.errorMsg = this.errors.PwdBadChar;
24301 if (value.length < 6) {
24302 this.markInvalid(this.errors.PwdShort);
24303 this.errorMsg = this.errors.PwdShort;
24306 if (value.length > 16) {
24307 this.markInvalid(this.errors.PwdLong);
24308 this.errorMsg = this.errors.PwdLong;
24312 if (this.ClientSideStrongPassword(value)) {
24314 } else if (this.ClientSideMediumPassword(value)) {
24316 } else if (this.ClientSideWeakPassword(value)) {
24323 if (strength < 2) {
24324 //this.markInvalid(this.errors.TooWeak);
24325 this.errorMsg = this.errors.TooWeak;
24330 console.log('strength2: ' + strength);
24332 //var pm = this.trigger.child('div/div/div').dom;
24334 var pm = this.trigger.child('div/div');
24335 pm.removeClass(this.meterClass);
24336 pm.addClass(this.meterClass[strength]);
24338 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24340 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24342 this.errorMsg = '';
24346 CharacterSetChecks: function (type)
24349 this.fResult = false;
24352 isctype: function (character, type)
24355 case this.kCapitalLetter:
24356 if (character >= 'A' && character <= 'Z') {
24361 case this.kSmallLetter:
24362 if (character >= 'a' && character <= 'z') {
24368 if (character >= '0' && character <= '9') {
24373 case this.kPunctuation:
24374 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24385 IsLongEnough: function (pwd, size)
24387 return !(pwd == null || isNaN(size) || pwd.length < size);
24390 SpansEnoughCharacterSets: function (word, nb)
24392 if (!this.IsLongEnough(word, nb))
24397 var characterSetChecks = new Array(
24398 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24399 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24402 for (var index = 0; index < word.length; ++index) {
24403 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24404 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24405 characterSetChecks[nCharSet].fResult = true;
24412 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24413 if (characterSetChecks[nCharSet].fResult) {
24418 if (nCharSets < nb) {
24424 ClientSideStrongPassword: function (pwd)
24426 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24429 ClientSideMediumPassword: function (pwd)
24431 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24434 ClientSideWeakPassword: function (pwd)
24436 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24439 })//<script type="text/javascript">
24442 * Based Ext JS Library 1.1.1
24443 * Copyright(c) 2006-2007, Ext JS, LLC.
24449 * @class Roo.HtmlEditorCore
24450 * @extends Roo.Component
24451 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24453 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24456 Roo.HtmlEditorCore = function(config){
24459 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24464 * @event initialize
24465 * Fires when the editor is fully initialized (including the iframe)
24466 * @param {Roo.HtmlEditorCore} this
24471 * Fires when the editor is first receives the focus. Any insertion must wait
24472 * until after this event.
24473 * @param {Roo.HtmlEditorCore} this
24477 * @event beforesync
24478 * Fires before the textarea is updated with content from the editor iframe. Return false
24479 * to cancel the sync.
24480 * @param {Roo.HtmlEditorCore} this
24481 * @param {String} html
24485 * @event beforepush
24486 * Fires before the iframe editor is updated with content from the textarea. Return false
24487 * to cancel the push.
24488 * @param {Roo.HtmlEditorCore} this
24489 * @param {String} html
24494 * Fires when the textarea is updated with content from the editor iframe.
24495 * @param {Roo.HtmlEditorCore} this
24496 * @param {String} html
24501 * Fires when the iframe editor is updated with content from the textarea.
24502 * @param {Roo.HtmlEditorCore} this
24503 * @param {String} html
24508 * @event editorevent
24509 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24510 * @param {Roo.HtmlEditorCore} this
24516 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24518 // defaults : white / black...
24519 this.applyBlacklists();
24526 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24530 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24536 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24541 * @cfg {Number} height (in pixels)
24545 * @cfg {Number} width (in pixels)
24550 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24553 stylesheets: false,
24558 // private properties
24559 validationEvent : false,
24561 initialized : false,
24563 sourceEditMode : false,
24564 onFocus : Roo.emptyFn,
24566 hideMode:'offsets',
24570 // blacklist + whitelisted elements..
24577 * Protected method that will not generally be called directly. It
24578 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24579 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24581 getDocMarkup : function(){
24585 // inherit styels from page...??
24586 if (this.stylesheets === false) {
24588 Roo.get(document.head).select('style').each(function(node) {
24589 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24592 Roo.get(document.head).select('link').each(function(node) {
24593 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24596 } else if (!this.stylesheets.length) {
24598 st = '<style type="text/css">' +
24599 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24602 for (var i in this.stylesheets) {
24603 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24608 st += '<style type="text/css">' +
24609 'IMG { cursor: pointer } ' +
24612 var cls = 'roo-htmleditor-body';
24614 if(this.bodyCls.length){
24615 cls += ' ' + this.bodyCls;
24618 return '<html><head>' + st +
24619 //<style type="text/css">' +
24620 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24622 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24626 onRender : function(ct, position)
24629 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24630 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24633 this.el.dom.style.border = '0 none';
24634 this.el.dom.setAttribute('tabIndex', -1);
24635 this.el.addClass('x-hidden hide');
24639 if(Roo.isIE){ // fix IE 1px bogus margin
24640 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24644 this.frameId = Roo.id();
24648 var iframe = this.owner.wrap.createChild({
24650 cls: 'form-control', // bootstrap..
24652 name: this.frameId,
24653 frameBorder : 'no',
24654 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24659 this.iframe = iframe.dom;
24661 this.assignDocWin();
24663 this.doc.designMode = 'on';
24666 this.doc.write(this.getDocMarkup());
24670 var task = { // must defer to wait for browser to be ready
24672 //console.log("run task?" + this.doc.readyState);
24673 this.assignDocWin();
24674 if(this.doc.body || this.doc.readyState == 'complete'){
24676 this.doc.designMode="on";
24680 Roo.TaskMgr.stop(task);
24681 this.initEditor.defer(10, this);
24688 Roo.TaskMgr.start(task);
24693 onResize : function(w, h)
24695 Roo.log('resize: ' +w + ',' + h );
24696 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24700 if(typeof w == 'number'){
24702 this.iframe.style.width = w + 'px';
24704 if(typeof h == 'number'){
24706 this.iframe.style.height = h + 'px';
24708 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24715 * Toggles the editor between standard and source edit mode.
24716 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24718 toggleSourceEdit : function(sourceEditMode){
24720 this.sourceEditMode = sourceEditMode === true;
24722 if(this.sourceEditMode){
24724 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24727 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24728 //this.iframe.className = '';
24731 //this.setSize(this.owner.wrap.getSize());
24732 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24739 * Protected method that will not generally be called directly. If you need/want
24740 * custom HTML cleanup, this is the method you should override.
24741 * @param {String} html The HTML to be cleaned
24742 * return {String} The cleaned HTML
24744 cleanHtml : function(html){
24745 html = String(html);
24746 if(html.length > 5){
24747 if(Roo.isSafari){ // strip safari nonsense
24748 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24751 if(html == ' '){
24758 * HTML Editor -> Textarea
24759 * Protected method that will not generally be called directly. Syncs the contents
24760 * of the editor iframe with the textarea.
24762 syncValue : function(){
24763 if(this.initialized){
24764 var bd = (this.doc.body || this.doc.documentElement);
24765 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24766 var html = bd.innerHTML;
24768 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24769 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24771 html = '<div style="'+m[0]+'">' + html + '</div>';
24774 html = this.cleanHtml(html);
24775 // fix up the special chars.. normaly like back quotes in word...
24776 // however we do not want to do this with chinese..
24777 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24779 var cc = match.charCodeAt();
24781 // Get the character value, handling surrogate pairs
24782 if (match.length == 2) {
24783 // It's a surrogate pair, calculate the Unicode code point
24784 var high = match.charCodeAt(0) - 0xD800;
24785 var low = match.charCodeAt(1) - 0xDC00;
24786 cc = (high * 0x400) + low + 0x10000;
24788 (cc >= 0x4E00 && cc < 0xA000 ) ||
24789 (cc >= 0x3400 && cc < 0x4E00 ) ||
24790 (cc >= 0xf900 && cc < 0xfb00 )
24795 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24796 return "&#" + cc + ";";
24803 if(this.owner.fireEvent('beforesync', this, html) !== false){
24804 this.el.dom.value = html;
24805 this.owner.fireEvent('sync', this, html);
24811 * Protected method that will not generally be called directly. Pushes the value of the textarea
24812 * into the iframe editor.
24814 pushValue : function(){
24815 if(this.initialized){
24816 var v = this.el.dom.value.trim();
24818 // if(v.length < 1){
24822 if(this.owner.fireEvent('beforepush', this, v) !== false){
24823 var d = (this.doc.body || this.doc.documentElement);
24825 this.cleanUpPaste();
24826 this.el.dom.value = d.innerHTML;
24827 this.owner.fireEvent('push', this, v);
24833 deferFocus : function(){
24834 this.focus.defer(10, this);
24838 focus : function(){
24839 if(this.win && !this.sourceEditMode){
24846 assignDocWin: function()
24848 var iframe = this.iframe;
24851 this.doc = iframe.contentWindow.document;
24852 this.win = iframe.contentWindow;
24854 // if (!Roo.get(this.frameId)) {
24857 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24858 // this.win = Roo.get(this.frameId).dom.contentWindow;
24860 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24864 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24865 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24870 initEditor : function(){
24871 //console.log("INIT EDITOR");
24872 this.assignDocWin();
24876 this.doc.designMode="on";
24878 this.doc.write(this.getDocMarkup());
24881 var dbody = (this.doc.body || this.doc.documentElement);
24882 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24883 // this copies styles from the containing element into thsi one..
24884 // not sure why we need all of this..
24885 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24887 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24888 //ss['background-attachment'] = 'fixed'; // w3c
24889 dbody.bgProperties = 'fixed'; // ie
24890 //Roo.DomHelper.applyStyles(dbody, ss);
24891 Roo.EventManager.on(this.doc, {
24892 //'mousedown': this.onEditorEvent,
24893 'mouseup': this.onEditorEvent,
24894 'dblclick': this.onEditorEvent,
24895 'click': this.onEditorEvent,
24896 'keyup': this.onEditorEvent,
24901 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24903 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24904 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24906 this.initialized = true;
24908 this.owner.fireEvent('initialize', this);
24913 onDestroy : function(){
24919 //for (var i =0; i < this.toolbars.length;i++) {
24920 // // fixme - ask toolbars for heights?
24921 // this.toolbars[i].onDestroy();
24924 //this.wrap.dom.innerHTML = '';
24925 //this.wrap.remove();
24930 onFirstFocus : function(){
24932 this.assignDocWin();
24935 this.activated = true;
24938 if(Roo.isGecko){ // prevent silly gecko errors
24940 var s = this.win.getSelection();
24941 if(!s.focusNode || s.focusNode.nodeType != 3){
24942 var r = s.getRangeAt(0);
24943 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24948 this.execCmd('useCSS', true);
24949 this.execCmd('styleWithCSS', false);
24952 this.owner.fireEvent('activate', this);
24956 adjustFont: function(btn){
24957 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24958 //if(Roo.isSafari){ // safari
24961 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24962 if(Roo.isSafari){ // safari
24963 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24964 v = (v < 10) ? 10 : v;
24965 v = (v > 48) ? 48 : v;
24966 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24971 v = Math.max(1, v+adjust);
24973 this.execCmd('FontSize', v );
24976 onEditorEvent : function(e)
24978 this.owner.fireEvent('editorevent', this, e);
24979 // this.updateToolbar();
24980 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24983 insertTag : function(tg)
24985 // could be a bit smarter... -> wrap the current selected tRoo..
24986 if (tg.toLowerCase() == 'span' ||
24987 tg.toLowerCase() == 'code' ||
24988 tg.toLowerCase() == 'sup' ||
24989 tg.toLowerCase() == 'sub'
24992 range = this.createRange(this.getSelection());
24993 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24994 wrappingNode.appendChild(range.extractContents());
24995 range.insertNode(wrappingNode);
25002 this.execCmd("formatblock", tg);
25006 insertText : function(txt)
25010 var range = this.createRange();
25011 range.deleteContents();
25012 //alert(Sender.getAttribute('label'));
25014 range.insertNode(this.doc.createTextNode(txt));
25020 * Executes a Midas editor command on the editor document and performs necessary focus and
25021 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25022 * @param {String} cmd The Midas command
25023 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25025 relayCmd : function(cmd, value){
25027 this.execCmd(cmd, value);
25028 this.owner.fireEvent('editorevent', this);
25029 //this.updateToolbar();
25030 this.owner.deferFocus();
25034 * Executes a Midas editor command directly on the editor document.
25035 * For visual commands, you should use {@link #relayCmd} instead.
25036 * <b>This should only be called after the editor is initialized.</b>
25037 * @param {String} cmd The Midas command
25038 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25040 execCmd : function(cmd, value){
25041 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25048 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25050 * @param {String} text | dom node..
25052 insertAtCursor : function(text)
25055 if(!this.activated){
25061 var r = this.doc.selection.createRange();
25072 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25076 // from jquery ui (MIT licenced)
25078 var win = this.win;
25080 if (win.getSelection && win.getSelection().getRangeAt) {
25081 range = win.getSelection().getRangeAt(0);
25082 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25083 range.insertNode(node);
25084 } else if (win.document.selection && win.document.selection.createRange) {
25085 // no firefox support
25086 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25087 win.document.selection.createRange().pasteHTML(txt);
25089 // no firefox support
25090 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25091 this.execCmd('InsertHTML', txt);
25100 mozKeyPress : function(e){
25102 var c = e.getCharCode(), cmd;
25105 c = String.fromCharCode(c).toLowerCase();
25119 this.cleanUpPaste.defer(100, this);
25127 e.preventDefault();
25135 fixKeys : function(){ // load time branching for fastest keydown performance
25137 return function(e){
25138 var k = e.getKey(), r;
25141 r = this.doc.selection.createRange();
25144 r.pasteHTML('    ');
25151 r = this.doc.selection.createRange();
25153 var target = r.parentElement();
25154 if(!target || target.tagName.toLowerCase() != 'li'){
25156 r.pasteHTML('<br />');
25162 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25163 this.cleanUpPaste.defer(100, this);
25169 }else if(Roo.isOpera){
25170 return function(e){
25171 var k = e.getKey();
25175 this.execCmd('InsertHTML','    ');
25178 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25179 this.cleanUpPaste.defer(100, this);
25184 }else if(Roo.isSafari){
25185 return function(e){
25186 var k = e.getKey();
25190 this.execCmd('InsertText','\t');
25194 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25195 this.cleanUpPaste.defer(100, this);
25203 getAllAncestors: function()
25205 var p = this.getSelectedNode();
25208 a.push(p); // push blank onto stack..
25209 p = this.getParentElement();
25213 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25217 a.push(this.doc.body);
25221 lastSelNode : false,
25224 getSelection : function()
25226 this.assignDocWin();
25227 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25230 getSelectedNode: function()
25232 // this may only work on Gecko!!!
25234 // should we cache this!!!!
25239 var range = this.createRange(this.getSelection()).cloneRange();
25242 var parent = range.parentElement();
25244 var testRange = range.duplicate();
25245 testRange.moveToElementText(parent);
25246 if (testRange.inRange(range)) {
25249 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25252 parent = parent.parentElement;
25257 // is ancestor a text element.
25258 var ac = range.commonAncestorContainer;
25259 if (ac.nodeType == 3) {
25260 ac = ac.parentNode;
25263 var ar = ac.childNodes;
25266 var other_nodes = [];
25267 var has_other_nodes = false;
25268 for (var i=0;i<ar.length;i++) {
25269 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25272 // fullly contained node.
25274 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25279 // probably selected..
25280 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25281 other_nodes.push(ar[i]);
25285 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25290 has_other_nodes = true;
25292 if (!nodes.length && other_nodes.length) {
25293 nodes= other_nodes;
25295 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25301 createRange: function(sel)
25303 // this has strange effects when using with
25304 // top toolbar - not sure if it's a great idea.
25305 //this.editor.contentWindow.focus();
25306 if (typeof sel != "undefined") {
25308 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25310 return this.doc.createRange();
25313 return this.doc.createRange();
25316 getParentElement: function()
25319 this.assignDocWin();
25320 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25322 var range = this.createRange(sel);
25325 var p = range.commonAncestorContainer;
25326 while (p.nodeType == 3) { // text node
25337 * Range intersection.. the hard stuff...
25341 * [ -- selected range --- ]
25345 * if end is before start or hits it. fail.
25346 * if start is after end or hits it fail.
25348 * if either hits (but other is outside. - then it's not
25354 // @see http://www.thismuchiknow.co.uk/?p=64.
25355 rangeIntersectsNode : function(range, node)
25357 var nodeRange = node.ownerDocument.createRange();
25359 nodeRange.selectNode(node);
25361 nodeRange.selectNodeContents(node);
25364 var rangeStartRange = range.cloneRange();
25365 rangeStartRange.collapse(true);
25367 var rangeEndRange = range.cloneRange();
25368 rangeEndRange.collapse(false);
25370 var nodeStartRange = nodeRange.cloneRange();
25371 nodeStartRange.collapse(true);
25373 var nodeEndRange = nodeRange.cloneRange();
25374 nodeEndRange.collapse(false);
25376 return rangeStartRange.compareBoundaryPoints(
25377 Range.START_TO_START, nodeEndRange) == -1 &&
25378 rangeEndRange.compareBoundaryPoints(
25379 Range.START_TO_START, nodeStartRange) == 1;
25383 rangeCompareNode : function(range, node)
25385 var nodeRange = node.ownerDocument.createRange();
25387 nodeRange.selectNode(node);
25389 nodeRange.selectNodeContents(node);
25393 range.collapse(true);
25395 nodeRange.collapse(true);
25397 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25398 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25400 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25402 var nodeIsBefore = ss == 1;
25403 var nodeIsAfter = ee == -1;
25405 if (nodeIsBefore && nodeIsAfter) {
25408 if (!nodeIsBefore && nodeIsAfter) {
25409 return 1; //right trailed.
25412 if (nodeIsBefore && !nodeIsAfter) {
25413 return 2; // left trailed.
25419 // private? - in a new class?
25420 cleanUpPaste : function()
25422 // cleans up the whole document..
25423 Roo.log('cleanuppaste');
25425 this.cleanUpChildren(this.doc.body);
25426 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25427 if (clean != this.doc.body.innerHTML) {
25428 this.doc.body.innerHTML = clean;
25433 cleanWordChars : function(input) {// change the chars to hex code
25434 var he = Roo.HtmlEditorCore;
25436 var output = input;
25437 Roo.each(he.swapCodes, function(sw) {
25438 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25440 output = output.replace(swapper, sw[1]);
25447 cleanUpChildren : function (n)
25449 if (!n.childNodes.length) {
25452 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25453 this.cleanUpChild(n.childNodes[i]);
25460 cleanUpChild : function (node)
25463 //console.log(node);
25464 if (node.nodeName == "#text") {
25465 // clean up silly Windows -- stuff?
25468 if (node.nodeName == "#comment") {
25469 node.parentNode.removeChild(node);
25470 // clean up silly Windows -- stuff?
25473 var lcname = node.tagName.toLowerCase();
25474 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25475 // whitelist of tags..
25477 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25479 node.parentNode.removeChild(node);
25484 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25486 // spans with no attributes - just remove them..
25487 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25488 remove_keep_children = true;
25491 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25492 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25494 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25495 // remove_keep_children = true;
25498 if (remove_keep_children) {
25499 this.cleanUpChildren(node);
25500 // inserts everything just before this node...
25501 while (node.childNodes.length) {
25502 var cn = node.childNodes[0];
25503 node.removeChild(cn);
25504 node.parentNode.insertBefore(cn, node);
25506 node.parentNode.removeChild(node);
25510 if (!node.attributes || !node.attributes.length) {
25515 this.cleanUpChildren(node);
25519 function cleanAttr(n,v)
25522 if (v.match(/^\./) || v.match(/^\//)) {
25525 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25528 if (v.match(/^#/)) {
25531 if (v.match(/^\{/)) { // allow template editing.
25534 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25535 node.removeAttribute(n);
25539 var cwhite = this.cwhite;
25540 var cblack = this.cblack;
25542 function cleanStyle(n,v)
25544 if (v.match(/expression/)) { //XSS?? should we even bother..
25545 node.removeAttribute(n);
25549 var parts = v.split(/;/);
25552 Roo.each(parts, function(p) {
25553 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25557 var l = p.split(':').shift().replace(/\s+/g,'');
25558 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25560 if ( cwhite.length && cblack.indexOf(l) > -1) {
25561 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25562 //node.removeAttribute(n);
25566 // only allow 'c whitelisted system attributes'
25567 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25568 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25569 //node.removeAttribute(n);
25579 if (clean.length) {
25580 node.setAttribute(n, clean.join(';'));
25582 node.removeAttribute(n);
25588 for (var i = node.attributes.length-1; i > -1 ; i--) {
25589 var a = node.attributes[i];
25592 if (a.name.toLowerCase().substr(0,2)=='on') {
25593 node.removeAttribute(a.name);
25596 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25597 node.removeAttribute(a.name);
25600 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25601 cleanAttr(a.name,a.value); // fixme..
25604 if (a.name == 'style') {
25605 cleanStyle(a.name,a.value);
25608 /// clean up MS crap..
25609 // tecnically this should be a list of valid class'es..
25612 if (a.name == 'class') {
25613 if (a.value.match(/^Mso/)) {
25614 node.removeAttribute('class');
25617 if (a.value.match(/^body$/)) {
25618 node.removeAttribute('class');
25629 this.cleanUpChildren(node);
25635 * Clean up MS wordisms...
25637 cleanWord : function(node)
25640 this.cleanWord(this.doc.body);
25645 node.nodeName == 'SPAN' &&
25646 !node.hasAttributes() &&
25647 node.childNodes.length == 1 &&
25648 node.firstChild.nodeName == "#text"
25650 var textNode = node.firstChild;
25651 node.removeChild(textNode);
25652 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25653 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25655 node.parentNode.insertBefore(textNode, node);
25656 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25657 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25659 node.parentNode.removeChild(node);
25662 if (node.nodeName == "#text") {
25663 // clean up silly Windows -- stuff?
25666 if (node.nodeName == "#comment") {
25667 node.parentNode.removeChild(node);
25668 // clean up silly Windows -- stuff?
25672 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25673 node.parentNode.removeChild(node);
25676 //Roo.log(node.tagName);
25677 // remove - but keep children..
25678 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25679 //Roo.log('-- removed');
25680 while (node.childNodes.length) {
25681 var cn = node.childNodes[0];
25682 node.removeChild(cn);
25683 node.parentNode.insertBefore(cn, node);
25684 // move node to parent - and clean it..
25685 this.cleanWord(cn);
25687 node.parentNode.removeChild(node);
25688 /// no need to iterate chidlren = it's got none..
25689 //this.iterateChildren(node, this.cleanWord);
25693 if (node.className.length) {
25695 var cn = node.className.split(/\W+/);
25697 Roo.each(cn, function(cls) {
25698 if (cls.match(/Mso[a-zA-Z]+/)) {
25703 node.className = cna.length ? cna.join(' ') : '';
25705 node.removeAttribute("class");
25709 if (node.hasAttribute("lang")) {
25710 node.removeAttribute("lang");
25713 if (node.hasAttribute("style")) {
25715 var styles = node.getAttribute("style").split(";");
25717 Roo.each(styles, function(s) {
25718 if (!s.match(/:/)) {
25721 var kv = s.split(":");
25722 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25725 // what ever is left... we allow.
25728 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25729 if (!nstyle.length) {
25730 node.removeAttribute('style');
25733 this.iterateChildren(node, this.cleanWord);
25739 * iterateChildren of a Node, calling fn each time, using this as the scole..
25740 * @param {DomNode} node node to iterate children of.
25741 * @param {Function} fn method of this class to call on each item.
25743 iterateChildren : function(node, fn)
25745 if (!node.childNodes.length) {
25748 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25749 fn.call(this, node.childNodes[i])
25755 * cleanTableWidths.
25757 * Quite often pasting from word etc.. results in tables with column and widths.
25758 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25761 cleanTableWidths : function(node)
25766 this.cleanTableWidths(this.doc.body);
25771 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25774 Roo.log(node.tagName);
25775 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25776 this.iterateChildren(node, this.cleanTableWidths);
25779 if (node.hasAttribute('width')) {
25780 node.removeAttribute('width');
25784 if (node.hasAttribute("style")) {
25787 var styles = node.getAttribute("style").split(";");
25789 Roo.each(styles, function(s) {
25790 if (!s.match(/:/)) {
25793 var kv = s.split(":");
25794 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25797 // what ever is left... we allow.
25800 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25801 if (!nstyle.length) {
25802 node.removeAttribute('style');
25806 this.iterateChildren(node, this.cleanTableWidths);
25814 domToHTML : function(currentElement, depth, nopadtext) {
25816 depth = depth || 0;
25817 nopadtext = nopadtext || false;
25819 if (!currentElement) {
25820 return this.domToHTML(this.doc.body);
25823 //Roo.log(currentElement);
25825 var allText = false;
25826 var nodeName = currentElement.nodeName;
25827 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25829 if (nodeName == '#text') {
25831 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25836 if (nodeName != 'BODY') {
25839 // Prints the node tagName, such as <A>, <IMG>, etc
25842 for(i = 0; i < currentElement.attributes.length;i++) {
25844 var aname = currentElement.attributes.item(i).name;
25845 if (!currentElement.attributes.item(i).value.length) {
25848 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25851 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25860 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25863 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25868 // Traverse the tree
25870 var currentElementChild = currentElement.childNodes.item(i);
25871 var allText = true;
25872 var innerHTML = '';
25874 while (currentElementChild) {
25875 // Formatting code (indent the tree so it looks nice on the screen)
25876 var nopad = nopadtext;
25877 if (lastnode == 'SPAN') {
25881 if (currentElementChild.nodeName == '#text') {
25882 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25883 toadd = nopadtext ? toadd : toadd.trim();
25884 if (!nopad && toadd.length > 80) {
25885 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25887 innerHTML += toadd;
25890 currentElementChild = currentElement.childNodes.item(i);
25896 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25898 // Recursively traverse the tree structure of the child node
25899 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25900 lastnode = currentElementChild.nodeName;
25902 currentElementChild=currentElement.childNodes.item(i);
25908 // The remaining code is mostly for formatting the tree
25909 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25914 ret+= "</"+tagName+">";
25920 applyBlacklists : function()
25922 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25923 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25927 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25928 if (b.indexOf(tag) > -1) {
25931 this.white.push(tag);
25935 Roo.each(w, function(tag) {
25936 if (b.indexOf(tag) > -1) {
25939 if (this.white.indexOf(tag) > -1) {
25942 this.white.push(tag);
25947 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25948 if (w.indexOf(tag) > -1) {
25951 this.black.push(tag);
25955 Roo.each(b, function(tag) {
25956 if (w.indexOf(tag) > -1) {
25959 if (this.black.indexOf(tag) > -1) {
25962 this.black.push(tag);
25967 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25968 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25972 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25973 if (b.indexOf(tag) > -1) {
25976 this.cwhite.push(tag);
25980 Roo.each(w, function(tag) {
25981 if (b.indexOf(tag) > -1) {
25984 if (this.cwhite.indexOf(tag) > -1) {
25987 this.cwhite.push(tag);
25992 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25993 if (w.indexOf(tag) > -1) {
25996 this.cblack.push(tag);
26000 Roo.each(b, function(tag) {
26001 if (w.indexOf(tag) > -1) {
26004 if (this.cblack.indexOf(tag) > -1) {
26007 this.cblack.push(tag);
26012 setStylesheets : function(stylesheets)
26014 if(typeof(stylesheets) == 'string'){
26015 Roo.get(this.iframe.contentDocument.head).createChild({
26017 rel : 'stylesheet',
26026 Roo.each(stylesheets, function(s) {
26031 Roo.get(_this.iframe.contentDocument.head).createChild({
26033 rel : 'stylesheet',
26042 removeStylesheets : function()
26046 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26051 setStyle : function(style)
26053 Roo.get(this.iframe.contentDocument.head).createChild({
26062 // hide stuff that is not compatible
26076 * @event specialkey
26080 * @cfg {String} fieldClass @hide
26083 * @cfg {String} focusClass @hide
26086 * @cfg {String} autoCreate @hide
26089 * @cfg {String} inputType @hide
26092 * @cfg {String} invalidClass @hide
26095 * @cfg {String} invalidText @hide
26098 * @cfg {String} msgFx @hide
26101 * @cfg {String} validateOnBlur @hide
26105 Roo.HtmlEditorCore.white = [
26106 'area', 'br', 'img', 'input', 'hr', 'wbr',
26108 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26109 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26110 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26111 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26112 'table', 'ul', 'xmp',
26114 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26117 'dir', 'menu', 'ol', 'ul', 'dl',
26123 Roo.HtmlEditorCore.black = [
26124 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26126 'base', 'basefont', 'bgsound', 'blink', 'body',
26127 'frame', 'frameset', 'head', 'html', 'ilayer',
26128 'iframe', 'layer', 'link', 'meta', 'object',
26129 'script', 'style' ,'title', 'xml' // clean later..
26131 Roo.HtmlEditorCore.clean = [
26132 'script', 'style', 'title', 'xml'
26134 Roo.HtmlEditorCore.remove = [
26139 Roo.HtmlEditorCore.ablack = [
26143 Roo.HtmlEditorCore.aclean = [
26144 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26148 Roo.HtmlEditorCore.pwhite= [
26149 'http', 'https', 'mailto'
26152 // white listed style attributes.
26153 Roo.HtmlEditorCore.cwhite= [
26154 // 'text-align', /// default is to allow most things..
26160 // black listed style attributes.
26161 Roo.HtmlEditorCore.cblack= [
26162 // 'font-size' -- this can be set by the project
26166 Roo.HtmlEditorCore.swapCodes =[
26167 [ 8211, "–" ],
26168 [ 8212, "—" ],
26185 * @class Roo.bootstrap.HtmlEditor
26186 * @extends Roo.bootstrap.TextArea
26187 * Bootstrap HtmlEditor class
26190 * Create a new HtmlEditor
26191 * @param {Object} config The config object
26194 Roo.bootstrap.HtmlEditor = function(config){
26195 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26196 if (!this.toolbars) {
26197 this.toolbars = [];
26200 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26203 * @event initialize
26204 * Fires when the editor is fully initialized (including the iframe)
26205 * @param {HtmlEditor} this
26210 * Fires when the editor is first receives the focus. Any insertion must wait
26211 * until after this event.
26212 * @param {HtmlEditor} this
26216 * @event beforesync
26217 * Fires before the textarea is updated with content from the editor iframe. Return false
26218 * to cancel the sync.
26219 * @param {HtmlEditor} this
26220 * @param {String} html
26224 * @event beforepush
26225 * Fires before the iframe editor is updated with content from the textarea. Return false
26226 * to cancel the push.
26227 * @param {HtmlEditor} this
26228 * @param {String} html
26233 * Fires when the textarea is updated with content from the editor iframe.
26234 * @param {HtmlEditor} this
26235 * @param {String} html
26240 * Fires when the iframe editor is updated with content from the textarea.
26241 * @param {HtmlEditor} this
26242 * @param {String} html
26246 * @event editmodechange
26247 * Fires when the editor switches edit modes
26248 * @param {HtmlEditor} this
26249 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26251 editmodechange: true,
26253 * @event editorevent
26254 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26255 * @param {HtmlEditor} this
26259 * @event firstfocus
26260 * Fires when on first focus - needed by toolbars..
26261 * @param {HtmlEditor} this
26266 * Auto save the htmlEditor value as a file into Events
26267 * @param {HtmlEditor} this
26271 * @event savedpreview
26272 * preview the saved version of htmlEditor
26273 * @param {HtmlEditor} this
26280 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26284 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26289 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26294 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26299 * @cfg {Number} height (in pixels)
26303 * @cfg {Number} width (in pixels)
26308 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26311 stylesheets: false,
26316 // private properties
26317 validationEvent : false,
26319 initialized : false,
26322 onFocus : Roo.emptyFn,
26324 hideMode:'offsets',
26326 tbContainer : false,
26330 toolbarContainer :function() {
26331 return this.wrap.select('.x-html-editor-tb',true).first();
26335 * Protected method that will not generally be called directly. It
26336 * is called when the editor creates its toolbar. Override this method if you need to
26337 * add custom toolbar buttons.
26338 * @param {HtmlEditor} editor
26340 createToolbar : function(){
26341 Roo.log('renewing');
26342 Roo.log("create toolbars");
26344 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26345 this.toolbars[0].render(this.toolbarContainer());
26349 // if (!editor.toolbars || !editor.toolbars.length) {
26350 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26353 // for (var i =0 ; i < editor.toolbars.length;i++) {
26354 // editor.toolbars[i] = Roo.factory(
26355 // typeof(editor.toolbars[i]) == 'string' ?
26356 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26357 // Roo.bootstrap.HtmlEditor);
26358 // editor.toolbars[i].init(editor);
26364 onRender : function(ct, position)
26366 // Roo.log("Call onRender: " + this.xtype);
26368 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26370 this.wrap = this.inputEl().wrap({
26371 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26374 this.editorcore.onRender(ct, position);
26376 if (this.resizable) {
26377 this.resizeEl = new Roo.Resizable(this.wrap, {
26381 minHeight : this.height,
26382 height: this.height,
26383 handles : this.resizable,
26386 resize : function(r, w, h) {
26387 _t.onResize(w,h); // -something
26393 this.createToolbar(this);
26396 if(!this.width && this.resizable){
26397 this.setSize(this.wrap.getSize());
26399 if (this.resizeEl) {
26400 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26401 // should trigger onReize..
26407 onResize : function(w, h)
26409 Roo.log('resize: ' +w + ',' + h );
26410 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26414 if(this.inputEl() ){
26415 if(typeof w == 'number'){
26416 var aw = w - this.wrap.getFrameWidth('lr');
26417 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26420 if(typeof h == 'number'){
26421 var tbh = -11; // fixme it needs to tool bar size!
26422 for (var i =0; i < this.toolbars.length;i++) {
26423 // fixme - ask toolbars for heights?
26424 tbh += this.toolbars[i].el.getHeight();
26425 //if (this.toolbars[i].footer) {
26426 // tbh += this.toolbars[i].footer.el.getHeight();
26434 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26435 ah -= 5; // knock a few pixes off for look..
26436 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26440 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26441 this.editorcore.onResize(ew,eh);
26446 * Toggles the editor between standard and source edit mode.
26447 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26449 toggleSourceEdit : function(sourceEditMode)
26451 this.editorcore.toggleSourceEdit(sourceEditMode);
26453 if(this.editorcore.sourceEditMode){
26454 Roo.log('editor - showing textarea');
26457 // Roo.log(this.syncValue());
26459 this.inputEl().removeClass(['hide', 'x-hidden']);
26460 this.inputEl().dom.removeAttribute('tabIndex');
26461 this.inputEl().focus();
26463 Roo.log('editor - hiding textarea');
26465 // Roo.log(this.pushValue());
26468 this.inputEl().addClass(['hide', 'x-hidden']);
26469 this.inputEl().dom.setAttribute('tabIndex', -1);
26470 //this.deferFocus();
26473 if(this.resizable){
26474 this.setSize(this.wrap.getSize());
26477 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26480 // private (for BoxComponent)
26481 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26483 // private (for BoxComponent)
26484 getResizeEl : function(){
26488 // private (for BoxComponent)
26489 getPositionEl : function(){
26494 initEvents : function(){
26495 this.originalValue = this.getValue();
26499 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26502 // markInvalid : Roo.emptyFn,
26504 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26507 // clearInvalid : Roo.emptyFn,
26509 setValue : function(v){
26510 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26511 this.editorcore.pushValue();
26516 deferFocus : function(){
26517 this.focus.defer(10, this);
26521 focus : function(){
26522 this.editorcore.focus();
26528 onDestroy : function(){
26534 for (var i =0; i < this.toolbars.length;i++) {
26535 // fixme - ask toolbars for heights?
26536 this.toolbars[i].onDestroy();
26539 this.wrap.dom.innerHTML = '';
26540 this.wrap.remove();
26545 onFirstFocus : function(){
26546 //Roo.log("onFirstFocus");
26547 this.editorcore.onFirstFocus();
26548 for (var i =0; i < this.toolbars.length;i++) {
26549 this.toolbars[i].onFirstFocus();
26555 syncValue : function()
26557 this.editorcore.syncValue();
26560 pushValue : function()
26562 this.editorcore.pushValue();
26566 // hide stuff that is not compatible
26580 * @event specialkey
26584 * @cfg {String} fieldClass @hide
26587 * @cfg {String} focusClass @hide
26590 * @cfg {String} autoCreate @hide
26593 * @cfg {String} inputType @hide
26597 * @cfg {String} invalidText @hide
26600 * @cfg {String} msgFx @hide
26603 * @cfg {String} validateOnBlur @hide
26612 Roo.namespace('Roo.bootstrap.htmleditor');
26614 * @class Roo.bootstrap.HtmlEditorToolbar1
26620 new Roo.bootstrap.HtmlEditor({
26623 new Roo.bootstrap.HtmlEditorToolbar1({
26624 disable : { fonts: 1 , format: 1, ..., ... , ...],
26630 * @cfg {Object} disable List of elements to disable..
26631 * @cfg {Array} btns List of additional buttons.
26635 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26638 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26641 Roo.apply(this, config);
26643 // default disabled, based on 'good practice'..
26644 this.disable = this.disable || {};
26645 Roo.applyIf(this.disable, {
26648 specialElements : true
26650 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26652 this.editor = config.editor;
26653 this.editorcore = config.editor.editorcore;
26655 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26657 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26658 // dont call parent... till later.
26660 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26665 editorcore : false,
26670 "h1","h2","h3","h4","h5","h6",
26672 "abbr", "acronym", "address", "cite", "samp", "var",
26676 onRender : function(ct, position)
26678 // Roo.log("Call onRender: " + this.xtype);
26680 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26682 this.el.dom.style.marginBottom = '0';
26684 var editorcore = this.editorcore;
26685 var editor= this.editor;
26688 var btn = function(id,cmd , toggle, handler, html){
26690 var event = toggle ? 'toggle' : 'click';
26695 xns: Roo.bootstrap,
26699 enableToggle:toggle !== false,
26701 pressed : toggle ? false : null,
26704 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26705 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26711 // var cb_box = function...
26716 xns: Roo.bootstrap,
26721 xns: Roo.bootstrap,
26725 Roo.each(this.formats, function(f) {
26726 style.menu.items.push({
26728 xns: Roo.bootstrap,
26729 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26734 editorcore.insertTag(this.tagname);
26741 children.push(style);
26743 btn('bold',false,true);
26744 btn('italic',false,true);
26745 btn('align-left', 'justifyleft',true);
26746 btn('align-center', 'justifycenter',true);
26747 btn('align-right' , 'justifyright',true);
26748 btn('link', false, false, function(btn) {
26749 //Roo.log("create link?");
26750 var url = prompt(this.createLinkText, this.defaultLinkValue);
26751 if(url && url != 'http:/'+'/'){
26752 this.editorcore.relayCmd('createlink', url);
26755 btn('list','insertunorderedlist',true);
26756 btn('pencil', false,true, function(btn){
26758 this.toggleSourceEdit(btn.pressed);
26761 if (this.editor.btns.length > 0) {
26762 for (var i = 0; i<this.editor.btns.length; i++) {
26763 children.push(this.editor.btns[i]);
26771 xns: Roo.bootstrap,
26776 xns: Roo.bootstrap,
26781 cog.menu.items.push({
26783 xns: Roo.bootstrap,
26784 html : Clean styles,
26789 editorcore.insertTag(this.tagname);
26798 this.xtype = 'NavSimplebar';
26800 for(var i=0;i< children.length;i++) {
26802 this.buttons.add(this.addxtypeChild(children[i]));
26806 editor.on('editorevent', this.updateToolbar, this);
26808 onBtnClick : function(id)
26810 this.editorcore.relayCmd(id);
26811 this.editorcore.focus();
26815 * Protected method that will not generally be called directly. It triggers
26816 * a toolbar update by reading the markup state of the current selection in the editor.
26818 updateToolbar: function(){
26820 if(!this.editorcore.activated){
26821 this.editor.onFirstFocus(); // is this neeed?
26825 var btns = this.buttons;
26826 var doc = this.editorcore.doc;
26827 btns.get('bold').setActive(doc.queryCommandState('bold'));
26828 btns.get('italic').setActive(doc.queryCommandState('italic'));
26829 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26831 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26832 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26833 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26835 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26836 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26839 var ans = this.editorcore.getAllAncestors();
26840 if (this.formatCombo) {
26843 var store = this.formatCombo.store;
26844 this.formatCombo.setValue("");
26845 for (var i =0; i < ans.length;i++) {
26846 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26848 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26856 // hides menus... - so this cant be on a menu...
26857 Roo.bootstrap.MenuMgr.hideAll();
26859 Roo.bootstrap.MenuMgr.hideAll();
26860 //this.editorsyncValue();
26862 onFirstFocus: function() {
26863 this.buttons.each(function(item){
26867 toggleSourceEdit : function(sourceEditMode){
26870 if(sourceEditMode){
26871 Roo.log("disabling buttons");
26872 this.buttons.each( function(item){
26873 if(item.cmd != 'pencil'){
26879 Roo.log("enabling buttons");
26880 if(this.editorcore.initialized){
26881 this.buttons.each( function(item){
26887 Roo.log("calling toggole on editor");
26888 // tell the editor that it's been pressed..
26889 this.editor.toggleSourceEdit(sourceEditMode);
26903 * @class Roo.bootstrap.Markdown
26904 * @extends Roo.bootstrap.TextArea
26905 * Bootstrap Showdown editable area
26906 * @cfg {string} content
26909 * Create a new Showdown
26912 Roo.bootstrap.Markdown = function(config){
26913 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26917 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26921 initEvents : function()
26924 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26925 this.markdownEl = this.el.createChild({
26926 cls : 'roo-markdown-area'
26928 this.inputEl().addClass('d-none');
26929 if (this.getValue() == '') {
26930 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26933 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26935 this.markdownEl.on('click', this.toggleTextEdit, this);
26936 this.on('blur', this.toggleTextEdit, this);
26937 this.on('specialkey', this.resizeTextArea, this);
26940 toggleTextEdit : function()
26942 var sh = this.markdownEl.getHeight();
26943 this.inputEl().addClass('d-none');
26944 this.markdownEl.addClass('d-none');
26945 if (!this.editing) {
26947 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26948 this.inputEl().removeClass('d-none');
26949 this.inputEl().focus();
26950 this.editing = true;
26953 // show showdown...
26954 this.updateMarkdown();
26955 this.markdownEl.removeClass('d-none');
26956 this.editing = false;
26959 updateMarkdown : function()
26961 if (this.getValue() == '') {
26962 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26966 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26969 resizeTextArea: function () {
26972 Roo.log([sh, this.getValue().split("\n").length * 30]);
26973 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26975 setValue : function(val)
26977 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26978 if (!this.editing) {
26979 this.updateMarkdown();
26985 if (!this.editing) {
26986 this.toggleTextEdit();
26994 * @class Roo.bootstrap.Table.AbstractSelectionModel
26995 * @extends Roo.util.Observable
26996 * Abstract base class for grid SelectionModels. It provides the interface that should be
26997 * implemented by descendant classes. This class should not be directly instantiated.
27000 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27001 this.locked = false;
27002 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27006 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27007 /** @ignore Called by the grid automatically. Do not call directly. */
27008 init : function(grid){
27014 * Locks the selections.
27017 this.locked = true;
27021 * Unlocks the selections.
27023 unlock : function(){
27024 this.locked = false;
27028 * Returns true if the selections are locked.
27029 * @return {Boolean}
27031 isLocked : function(){
27032 return this.locked;
27036 initEvents : function ()
27042 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27043 * @class Roo.bootstrap.Table.RowSelectionModel
27044 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27045 * It supports multiple selections and keyboard selection/navigation.
27047 * @param {Object} config
27050 Roo.bootstrap.Table.RowSelectionModel = function(config){
27051 Roo.apply(this, config);
27052 this.selections = new Roo.util.MixedCollection(false, function(o){
27057 this.lastActive = false;
27061 * @event selectionchange
27062 * Fires when the selection changes
27063 * @param {SelectionModel} this
27065 "selectionchange" : true,
27067 * @event afterselectionchange
27068 * Fires after the selection changes (eg. by key press or clicking)
27069 * @param {SelectionModel} this
27071 "afterselectionchange" : true,
27073 * @event beforerowselect
27074 * Fires when a row is selected being selected, return false to cancel.
27075 * @param {SelectionModel} this
27076 * @param {Number} rowIndex The selected index
27077 * @param {Boolean} keepExisting False if other selections will be cleared
27079 "beforerowselect" : true,
27082 * Fires when a row is selected.
27083 * @param {SelectionModel} this
27084 * @param {Number} rowIndex The selected index
27085 * @param {Roo.data.Record} r The record
27087 "rowselect" : true,
27089 * @event rowdeselect
27090 * Fires when a row is deselected.
27091 * @param {SelectionModel} this
27092 * @param {Number} rowIndex The selected index
27094 "rowdeselect" : true
27096 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27097 this.locked = false;
27100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27102 * @cfg {Boolean} singleSelect
27103 * True to allow selection of only one row at a time (defaults to false)
27105 singleSelect : false,
27108 initEvents : function()
27111 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27112 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27113 //}else{ // allow click to work like normal
27114 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27116 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27117 this.grid.on("rowclick", this.handleMouseDown, this);
27119 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27120 "up" : function(e){
27122 this.selectPrevious(e.shiftKey);
27123 }else if(this.last !== false && this.lastActive !== false){
27124 var last = this.last;
27125 this.selectRange(this.last, this.lastActive-1);
27126 this.grid.getView().focusRow(this.lastActive);
27127 if(last !== false){
27131 this.selectFirstRow();
27133 this.fireEvent("afterselectionchange", this);
27135 "down" : function(e){
27137 this.selectNext(e.shiftKey);
27138 }else if(this.last !== false && this.lastActive !== false){
27139 var last = this.last;
27140 this.selectRange(this.last, this.lastActive+1);
27141 this.grid.getView().focusRow(this.lastActive);
27142 if(last !== false){
27146 this.selectFirstRow();
27148 this.fireEvent("afterselectionchange", this);
27152 this.grid.store.on('load', function(){
27153 this.selections.clear();
27156 var view = this.grid.view;
27157 view.on("refresh", this.onRefresh, this);
27158 view.on("rowupdated", this.onRowUpdated, this);
27159 view.on("rowremoved", this.onRemove, this);
27164 onRefresh : function()
27166 var ds = this.grid.store, i, v = this.grid.view;
27167 var s = this.selections;
27168 s.each(function(r){
27169 if((i = ds.indexOfId(r.id)) != -1){
27178 onRemove : function(v, index, r){
27179 this.selections.remove(r);
27183 onRowUpdated : function(v, index, r){
27184 if(this.isSelected(r)){
27185 v.onRowSelect(index);
27191 * @param {Array} records The records to select
27192 * @param {Boolean} keepExisting (optional) True to keep existing selections
27194 selectRecords : function(records, keepExisting)
27197 this.clearSelections();
27199 var ds = this.grid.store;
27200 for(var i = 0, len = records.length; i < len; i++){
27201 this.selectRow(ds.indexOf(records[i]), true);
27206 * Gets the number of selected rows.
27209 getCount : function(){
27210 return this.selections.length;
27214 * Selects the first row in the grid.
27216 selectFirstRow : function(){
27221 * Select the last row.
27222 * @param {Boolean} keepExisting (optional) True to keep existing selections
27224 selectLastRow : function(keepExisting){
27225 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27226 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27230 * Selects the row immediately following the last selected row.
27231 * @param {Boolean} keepExisting (optional) True to keep existing selections
27233 selectNext : function(keepExisting)
27235 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27236 this.selectRow(this.last+1, keepExisting);
27237 this.grid.getView().focusRow(this.last);
27242 * Selects the row that precedes the last selected row.
27243 * @param {Boolean} keepExisting (optional) True to keep existing selections
27245 selectPrevious : function(keepExisting){
27247 this.selectRow(this.last-1, keepExisting);
27248 this.grid.getView().focusRow(this.last);
27253 * Returns the selected records
27254 * @return {Array} Array of selected records
27256 getSelections : function(){
27257 return [].concat(this.selections.items);
27261 * Returns the first selected record.
27264 getSelected : function(){
27265 return this.selections.itemAt(0);
27270 * Clears all selections.
27272 clearSelections : function(fast)
27278 var ds = this.grid.store;
27279 var s = this.selections;
27280 s.each(function(r){
27281 this.deselectRow(ds.indexOfId(r.id));
27285 this.selections.clear();
27292 * Selects all rows.
27294 selectAll : function(){
27298 this.selections.clear();
27299 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27300 this.selectRow(i, true);
27305 * Returns True if there is a selection.
27306 * @return {Boolean}
27308 hasSelection : function(){
27309 return this.selections.length > 0;
27313 * Returns True if the specified row is selected.
27314 * @param {Number/Record} record The record or index of the record to check
27315 * @return {Boolean}
27317 isSelected : function(index){
27318 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27319 return (r && this.selections.key(r.id) ? true : false);
27323 * Returns True if the specified record id is selected.
27324 * @param {String} id The id of record to check
27325 * @return {Boolean}
27327 isIdSelected : function(id){
27328 return (this.selections.key(id) ? true : false);
27333 handleMouseDBClick : function(e, t){
27337 handleMouseDown : function(e, t)
27339 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27340 if(this.isLocked() || rowIndex < 0 ){
27343 if(e.shiftKey && this.last !== false){
27344 var last = this.last;
27345 this.selectRange(last, rowIndex, e.ctrlKey);
27346 this.last = last; // reset the last
27350 var isSelected = this.isSelected(rowIndex);
27351 //Roo.log("select row:" + rowIndex);
27353 this.deselectRow(rowIndex);
27355 this.selectRow(rowIndex, true);
27359 if(e.button !== 0 && isSelected){
27360 alert('rowIndex 2: ' + rowIndex);
27361 view.focusRow(rowIndex);
27362 }else if(e.ctrlKey && isSelected){
27363 this.deselectRow(rowIndex);
27364 }else if(!isSelected){
27365 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27366 view.focusRow(rowIndex);
27370 this.fireEvent("afterselectionchange", this);
27373 handleDragableRowClick : function(grid, rowIndex, e)
27375 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27376 this.selectRow(rowIndex, false);
27377 grid.view.focusRow(rowIndex);
27378 this.fireEvent("afterselectionchange", this);
27383 * Selects multiple rows.
27384 * @param {Array} rows Array of the indexes of the row to select
27385 * @param {Boolean} keepExisting (optional) True to keep existing selections
27387 selectRows : function(rows, keepExisting){
27389 this.clearSelections();
27391 for(var i = 0, len = rows.length; i < len; i++){
27392 this.selectRow(rows[i], true);
27397 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27398 * @param {Number} startRow The index of the first row in the range
27399 * @param {Number} endRow The index of the last row in the range
27400 * @param {Boolean} keepExisting (optional) True to retain existing selections
27402 selectRange : function(startRow, endRow, keepExisting){
27407 this.clearSelections();
27409 if(startRow <= endRow){
27410 for(var i = startRow; i <= endRow; i++){
27411 this.selectRow(i, true);
27414 for(var i = startRow; i >= endRow; i--){
27415 this.selectRow(i, true);
27421 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27422 * @param {Number} startRow The index of the first row in the range
27423 * @param {Number} endRow The index of the last row in the range
27425 deselectRange : function(startRow, endRow, preventViewNotify){
27429 for(var i = startRow; i <= endRow; i++){
27430 this.deselectRow(i, preventViewNotify);
27436 * @param {Number} row The index of the row to select
27437 * @param {Boolean} keepExisting (optional) True to keep existing selections
27439 selectRow : function(index, keepExisting, preventViewNotify)
27441 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27444 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27445 if(!keepExisting || this.singleSelect){
27446 this.clearSelections();
27449 var r = this.grid.store.getAt(index);
27450 //console.log('selectRow - record id :' + r.id);
27452 this.selections.add(r);
27453 this.last = this.lastActive = index;
27454 if(!preventViewNotify){
27455 var proxy = new Roo.Element(
27456 this.grid.getRowDom(index)
27458 proxy.addClass('bg-info info');
27460 this.fireEvent("rowselect", this, index, r);
27461 this.fireEvent("selectionchange", this);
27467 * @param {Number} row The index of the row to deselect
27469 deselectRow : function(index, preventViewNotify)
27474 if(this.last == index){
27477 if(this.lastActive == index){
27478 this.lastActive = false;
27481 var r = this.grid.store.getAt(index);
27486 this.selections.remove(r);
27487 //.console.log('deselectRow - record id :' + r.id);
27488 if(!preventViewNotify){
27490 var proxy = new Roo.Element(
27491 this.grid.getRowDom(index)
27493 proxy.removeClass('bg-info info');
27495 this.fireEvent("rowdeselect", this, index);
27496 this.fireEvent("selectionchange", this);
27500 restoreLast : function(){
27502 this.last = this._last;
27507 acceptsNav : function(row, col, cm){
27508 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27512 onEditorKey : function(field, e){
27513 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27518 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27520 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27522 }else if(k == e.ENTER && !e.ctrlKey){
27526 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27528 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27530 }else if(k == e.ESC){
27534 g.startEditing(newCell[0], newCell[1]);
27540 * Ext JS Library 1.1.1
27541 * Copyright(c) 2006-2007, Ext JS, LLC.
27543 * Originally Released Under LGPL - original licence link has changed is not relivant.
27546 * <script type="text/javascript">
27550 * @class Roo.bootstrap.PagingToolbar
27551 * @extends Roo.bootstrap.NavSimplebar
27552 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27554 * Create a new PagingToolbar
27555 * @param {Object} config The config object
27556 * @param {Roo.data.Store} store
27558 Roo.bootstrap.PagingToolbar = function(config)
27560 // old args format still supported... - xtype is prefered..
27561 // created from xtype...
27563 this.ds = config.dataSource;
27565 if (config.store && !this.ds) {
27566 this.store= Roo.factory(config.store, Roo.data);
27567 this.ds = this.store;
27568 this.ds.xmodule = this.xmodule || false;
27571 this.toolbarItems = [];
27572 if (config.items) {
27573 this.toolbarItems = config.items;
27576 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27581 this.bind(this.ds);
27584 if (Roo.bootstrap.version == 4) {
27585 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27587 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27594 * @cfg {Roo.data.Store} dataSource
27595 * The underlying data store providing the paged data
27598 * @cfg {String/HTMLElement/Element} container
27599 * container The id or element that will contain the toolbar
27602 * @cfg {Boolean} displayInfo
27603 * True to display the displayMsg (defaults to false)
27606 * @cfg {Number} pageSize
27607 * The number of records to display per page (defaults to 20)
27611 * @cfg {String} displayMsg
27612 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27614 displayMsg : 'Displaying {0} - {1} of {2}',
27616 * @cfg {String} emptyMsg
27617 * The message to display when no records are found (defaults to "No data to display")
27619 emptyMsg : 'No data to display',
27621 * Customizable piece of the default paging text (defaults to "Page")
27624 beforePageText : "Page",
27626 * Customizable piece of the default paging text (defaults to "of %0")
27629 afterPageText : "of {0}",
27631 * Customizable piece of the default paging text (defaults to "First Page")
27634 firstText : "First Page",
27636 * Customizable piece of the default paging text (defaults to "Previous Page")
27639 prevText : "Previous Page",
27641 * Customizable piece of the default paging text (defaults to "Next Page")
27644 nextText : "Next Page",
27646 * Customizable piece of the default paging text (defaults to "Last Page")
27649 lastText : "Last Page",
27651 * Customizable piece of the default paging text (defaults to "Refresh")
27654 refreshText : "Refresh",
27658 onRender : function(ct, position)
27660 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27661 this.navgroup.parentId = this.id;
27662 this.navgroup.onRender(this.el, null);
27663 // add the buttons to the navgroup
27665 if(this.displayInfo){
27666 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27667 this.displayEl = this.el.select('.x-paging-info', true).first();
27668 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27669 // this.displayEl = navel.el.select('span',true).first();
27675 Roo.each(_this.buttons, function(e){ // this might need to use render????
27676 Roo.factory(e).render(_this.el);
27680 Roo.each(_this.toolbarItems, function(e) {
27681 _this.navgroup.addItem(e);
27685 this.first = this.navgroup.addItem({
27686 tooltip: this.firstText,
27687 cls: "prev btn-outline-secondary",
27688 html : ' <i class="fa fa-step-backward"></i>',
27690 preventDefault: true,
27691 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27694 this.prev = this.navgroup.addItem({
27695 tooltip: this.prevText,
27696 cls: "prev btn-outline-secondary",
27697 html : ' <i class="fa fa-backward"></i>',
27699 preventDefault: true,
27700 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27702 //this.addSeparator();
27705 var field = this.navgroup.addItem( {
27707 cls : 'x-paging-position btn-outline-secondary',
27709 html : this.beforePageText +
27710 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27711 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27714 this.field = field.el.select('input', true).first();
27715 this.field.on("keydown", this.onPagingKeydown, this);
27716 this.field.on("focus", function(){this.dom.select();});
27719 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27720 //this.field.setHeight(18);
27721 //this.addSeparator();
27722 this.next = this.navgroup.addItem({
27723 tooltip: this.nextText,
27724 cls: "next btn-outline-secondary",
27725 html : ' <i class="fa fa-forward"></i>',
27727 preventDefault: true,
27728 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27730 this.last = this.navgroup.addItem({
27731 tooltip: this.lastText,
27732 html : ' <i class="fa fa-step-forward"></i>',
27733 cls: "next btn-outline-secondary",
27735 preventDefault: true,
27736 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27738 //this.addSeparator();
27739 this.loading = this.navgroup.addItem({
27740 tooltip: this.refreshText,
27741 cls: "btn-outline-secondary",
27742 html : ' <i class="fa fa-refresh"></i>',
27743 preventDefault: true,
27744 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27750 updateInfo : function(){
27751 if(this.displayEl){
27752 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27753 var msg = count == 0 ?
27757 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27759 this.displayEl.update(msg);
27764 onLoad : function(ds, r, o)
27766 this.cursor = o.params && o.params.start ? o.params.start : 0;
27768 var d = this.getPageData(),
27773 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27774 this.field.dom.value = ap;
27775 this.first.setDisabled(ap == 1);
27776 this.prev.setDisabled(ap == 1);
27777 this.next.setDisabled(ap == ps);
27778 this.last.setDisabled(ap == ps);
27779 this.loading.enable();
27784 getPageData : function(){
27785 var total = this.ds.getTotalCount();
27788 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27789 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27794 onLoadError : function(){
27795 this.loading.enable();
27799 onPagingKeydown : function(e){
27800 var k = e.getKey();
27801 var d = this.getPageData();
27803 var v = this.field.dom.value, pageNum;
27804 if(!v || isNaN(pageNum = parseInt(v, 10))){
27805 this.field.dom.value = d.activePage;
27808 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27809 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27812 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))
27814 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27815 this.field.dom.value = pageNum;
27816 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27819 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27821 var v = this.field.dom.value, pageNum;
27822 var increment = (e.shiftKey) ? 10 : 1;
27823 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27826 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27827 this.field.dom.value = d.activePage;
27830 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27832 this.field.dom.value = parseInt(v, 10) + increment;
27833 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27834 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27841 beforeLoad : function(){
27843 this.loading.disable();
27848 onClick : function(which){
27857 ds.load({params:{start: 0, limit: this.pageSize}});
27860 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27863 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27866 var total = ds.getTotalCount();
27867 var extra = total % this.pageSize;
27868 var lastStart = extra ? (total - extra) : total-this.pageSize;
27869 ds.load({params:{start: lastStart, limit: this.pageSize}});
27872 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27878 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27879 * @param {Roo.data.Store} store The data store to unbind
27881 unbind : function(ds){
27882 ds.un("beforeload", this.beforeLoad, this);
27883 ds.un("load", this.onLoad, this);
27884 ds.un("loadexception", this.onLoadError, this);
27885 ds.un("remove", this.updateInfo, this);
27886 ds.un("add", this.updateInfo, this);
27887 this.ds = undefined;
27891 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27892 * @param {Roo.data.Store} store The data store to bind
27894 bind : function(ds){
27895 ds.on("beforeload", this.beforeLoad, this);
27896 ds.on("load", this.onLoad, this);
27897 ds.on("loadexception", this.onLoadError, this);
27898 ds.on("remove", this.updateInfo, this);
27899 ds.on("add", this.updateInfo, this);
27910 * @class Roo.bootstrap.MessageBar
27911 * @extends Roo.bootstrap.Component
27912 * Bootstrap MessageBar class
27913 * @cfg {String} html contents of the MessageBar
27914 * @cfg {String} weight (info | success | warning | danger) default info
27915 * @cfg {String} beforeClass insert the bar before the given class
27916 * @cfg {Boolean} closable (true | false) default false
27917 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27920 * Create a new Element
27921 * @param {Object} config The config object
27924 Roo.bootstrap.MessageBar = function(config){
27925 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27934 beforeClass: 'bootstrap-sticky-wrap',
27936 getAutoCreate : function(){
27940 cls: 'alert alert-dismissable alert-' + this.weight,
27945 html: this.html || ''
27951 cfg.cls += ' alert-messages-fixed';
27965 onRender : function(ct, position)
27967 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27970 var cfg = Roo.apply({}, this.getAutoCreate());
27974 cfg.cls += ' ' + this.cls;
27977 cfg.style = this.style;
27979 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27981 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27984 this.el.select('>button.close').on('click', this.hide, this);
27990 if (!this.rendered) {
27996 this.fireEvent('show', this);
28002 if (!this.rendered) {
28008 this.fireEvent('hide', this);
28011 update : function()
28013 // var e = this.el.dom.firstChild;
28015 // if(this.closable){
28016 // e = e.nextSibling;
28019 // e.data = this.html || '';
28021 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28037 * @class Roo.bootstrap.Graph
28038 * @extends Roo.bootstrap.Component
28039 * Bootstrap Graph class
28043 @cfg {String} graphtype bar | vbar | pie
28044 @cfg {number} g_x coodinator | centre x (pie)
28045 @cfg {number} g_y coodinator | centre y (pie)
28046 @cfg {number} g_r radius (pie)
28047 @cfg {number} g_height height of the chart (respected by all elements in the set)
28048 @cfg {number} g_width width of the chart (respected by all elements in the set)
28049 @cfg {Object} title The title of the chart
28052 -opts (object) options for the chart
28054 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28055 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28057 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.
28058 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28060 o stretch (boolean)
28062 -opts (object) options for the pie
28065 o startAngle (number)
28066 o endAngle (number)
28070 * Create a new Input
28071 * @param {Object} config The config object
28074 Roo.bootstrap.Graph = function(config){
28075 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28081 * The img click event for the img.
28082 * @param {Roo.EventObject} e
28088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28099 //g_colors: this.colors,
28106 getAutoCreate : function(){
28117 onRender : function(ct,position){
28120 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28122 if (typeof(Raphael) == 'undefined') {
28123 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28127 this.raphael = Raphael(this.el.dom);
28129 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28130 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28131 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28132 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28134 r.text(160, 10, "Single Series Chart").attr(txtattr);
28135 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28136 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28137 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28139 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28140 r.barchart(330, 10, 300, 220, data1);
28141 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28142 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28145 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28146 // r.barchart(30, 30, 560, 250, xdata, {
28147 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28148 // axis : "0 0 1 1",
28149 // axisxlabels : xdata
28150 // //yvalues : cols,
28153 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28155 // this.load(null,xdata,{
28156 // axis : "0 0 1 1",
28157 // axisxlabels : xdata
28162 load : function(graphtype,xdata,opts)
28164 this.raphael.clear();
28166 graphtype = this.graphtype;
28171 var r = this.raphael,
28172 fin = function () {
28173 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28175 fout = function () {
28176 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28178 pfin = function() {
28179 this.sector.stop();
28180 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28183 this.label[0].stop();
28184 this.label[0].attr({ r: 7.5 });
28185 this.label[1].attr({ "font-weight": 800 });
28188 pfout = function() {
28189 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28192 this.label[0].animate({ r: 5 }, 500, "bounce");
28193 this.label[1].attr({ "font-weight": 400 });
28199 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28202 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28205 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28206 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28208 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28215 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28220 setTitle: function(o)
28225 initEvents: function() {
28228 this.el.on('click', this.onClick, this);
28232 onClick : function(e)
28234 Roo.log('img onclick');
28235 this.fireEvent('click', this, e);
28247 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28250 * @class Roo.bootstrap.dash.NumberBox
28251 * @extends Roo.bootstrap.Component
28252 * Bootstrap NumberBox class
28253 * @cfg {String} headline Box headline
28254 * @cfg {String} content Box content
28255 * @cfg {String} icon Box icon
28256 * @cfg {String} footer Footer text
28257 * @cfg {String} fhref Footer href
28260 * Create a new NumberBox
28261 * @param {Object} config The config object
28265 Roo.bootstrap.dash.NumberBox = function(config){
28266 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28270 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28279 getAutoCreate : function(){
28283 cls : 'small-box ',
28291 cls : 'roo-headline',
28292 html : this.headline
28296 cls : 'roo-content',
28297 html : this.content
28311 cls : 'ion ' + this.icon
28320 cls : 'small-box-footer',
28321 href : this.fhref || '#',
28325 cfg.cn.push(footer);
28332 onRender : function(ct,position){
28333 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28340 setHeadline: function (value)
28342 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28345 setFooter: function (value, href)
28347 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28350 this.el.select('a.small-box-footer',true).first().attr('href', href);
28355 setContent: function (value)
28357 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28360 initEvents: function()
28374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28377 * @class Roo.bootstrap.dash.TabBox
28378 * @extends Roo.bootstrap.Component
28379 * Bootstrap TabBox class
28380 * @cfg {String} title Title of the TabBox
28381 * @cfg {String} icon Icon of the TabBox
28382 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28383 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28386 * Create a new TabBox
28387 * @param {Object} config The config object
28391 Roo.bootstrap.dash.TabBox = function(config){
28392 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28397 * When a pane is added
28398 * @param {Roo.bootstrap.dash.TabPane} pane
28402 * @event activatepane
28403 * When a pane is activated
28404 * @param {Roo.bootstrap.dash.TabPane} pane
28406 "activatepane" : true
28414 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28419 tabScrollable : false,
28421 getChildContainer : function()
28423 return this.el.select('.tab-content', true).first();
28426 getAutoCreate : function(){
28430 cls: 'pull-left header',
28438 cls: 'fa ' + this.icon
28444 cls: 'nav nav-tabs pull-right',
28450 if(this.tabScrollable){
28457 cls: 'nav nav-tabs pull-right',
28468 cls: 'nav-tabs-custom',
28473 cls: 'tab-content no-padding',
28481 initEvents : function()
28483 //Roo.log('add add pane handler');
28484 this.on('addpane', this.onAddPane, this);
28487 * Updates the box title
28488 * @param {String} html to set the title to.
28490 setTitle : function(value)
28492 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28494 onAddPane : function(pane)
28496 this.panes.push(pane);
28497 //Roo.log('addpane');
28499 // tabs are rendere left to right..
28500 if(!this.showtabs){
28504 var ctr = this.el.select('.nav-tabs', true).first();
28507 var existing = ctr.select('.nav-tab',true);
28508 var qty = existing.getCount();;
28511 var tab = ctr.createChild({
28513 cls : 'nav-tab' + (qty ? '' : ' active'),
28521 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28524 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28526 pane.el.addClass('active');
28531 onTabClick : function(ev,un,ob,pane)
28533 //Roo.log('tab - prev default');
28534 ev.preventDefault();
28537 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28538 pane.tab.addClass('active');
28539 //Roo.log(pane.title);
28540 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28541 // technically we should have a deactivate event.. but maybe add later.
28542 // and it should not de-activate the selected tab...
28543 this.fireEvent('activatepane', pane);
28544 pane.el.addClass('active');
28545 pane.fireEvent('activate');
28550 getActivePane : function()
28553 Roo.each(this.panes, function(p) {
28554 if(p.el.hasClass('active')){
28575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28577 * @class Roo.bootstrap.TabPane
28578 * @extends Roo.bootstrap.Component
28579 * Bootstrap TabPane class
28580 * @cfg {Boolean} active (false | true) Default false
28581 * @cfg {String} title title of panel
28585 * Create a new TabPane
28586 * @param {Object} config The config object
28589 Roo.bootstrap.dash.TabPane = function(config){
28590 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28596 * When a pane is activated
28597 * @param {Roo.bootstrap.dash.TabPane} pane
28604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28609 // the tabBox that this is attached to.
28612 getAutoCreate : function()
28620 cfg.cls += ' active';
28625 initEvents : function()
28627 //Roo.log('trigger add pane handler');
28628 this.parent().fireEvent('addpane', this)
28632 * Updates the tab title
28633 * @param {String} html to set the title to.
28635 setTitle: function(str)
28641 this.tab.select('a', true).first().dom.innerHTML = str;
28658 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28661 * @class Roo.bootstrap.menu.Menu
28662 * @extends Roo.bootstrap.Component
28663 * Bootstrap Menu class - container for Menu
28664 * @cfg {String} html Text of the menu
28665 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28666 * @cfg {String} icon Font awesome icon
28667 * @cfg {String} pos Menu align to (top | bottom) default bottom
28671 * Create a new Menu
28672 * @param {Object} config The config object
28676 Roo.bootstrap.menu.Menu = function(config){
28677 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28681 * @event beforeshow
28682 * Fires before this menu is displayed
28683 * @param {Roo.bootstrap.menu.Menu} this
28687 * @event beforehide
28688 * Fires before this menu is hidden
28689 * @param {Roo.bootstrap.menu.Menu} this
28694 * Fires after this menu is displayed
28695 * @param {Roo.bootstrap.menu.Menu} this
28700 * Fires after this menu is hidden
28701 * @param {Roo.bootstrap.menu.Menu} this
28706 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28707 * @param {Roo.bootstrap.menu.Menu} this
28708 * @param {Roo.EventObject} e
28715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28719 weight : 'default',
28724 getChildContainer : function() {
28725 if(this.isSubMenu){
28729 return this.el.select('ul.dropdown-menu', true).first();
28732 getAutoCreate : function()
28737 cls : 'roo-menu-text',
28745 cls : 'fa ' + this.icon
28756 cls : 'dropdown-button btn btn-' + this.weight,
28761 cls : 'dropdown-toggle btn btn-' + this.weight,
28771 cls : 'dropdown-menu'
28777 if(this.pos == 'top'){
28778 cfg.cls += ' dropup';
28781 if(this.isSubMenu){
28784 cls : 'dropdown-menu'
28791 onRender : function(ct, position)
28793 this.isSubMenu = ct.hasClass('dropdown-submenu');
28795 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28798 initEvents : function()
28800 if(this.isSubMenu){
28804 this.hidden = true;
28806 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28807 this.triggerEl.on('click', this.onTriggerPress, this);
28809 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28810 this.buttonEl.on('click', this.onClick, this);
28816 if(this.isSubMenu){
28820 return this.el.select('ul.dropdown-menu', true).first();
28823 onClick : function(e)
28825 this.fireEvent("click", this, e);
28828 onTriggerPress : function(e)
28830 if (this.isVisible()) {
28837 isVisible : function(){
28838 return !this.hidden;
28843 this.fireEvent("beforeshow", this);
28845 this.hidden = false;
28846 this.el.addClass('open');
28848 Roo.get(document).on("mouseup", this.onMouseUp, this);
28850 this.fireEvent("show", this);
28857 this.fireEvent("beforehide", this);
28859 this.hidden = true;
28860 this.el.removeClass('open');
28862 Roo.get(document).un("mouseup", this.onMouseUp);
28864 this.fireEvent("hide", this);
28867 onMouseUp : function()
28881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28884 * @class Roo.bootstrap.menu.Item
28885 * @extends Roo.bootstrap.Component
28886 * Bootstrap MenuItem class
28887 * @cfg {Boolean} submenu (true | false) default false
28888 * @cfg {String} html text of the item
28889 * @cfg {String} href the link
28890 * @cfg {Boolean} disable (true | false) default false
28891 * @cfg {Boolean} preventDefault (true | false) default true
28892 * @cfg {String} icon Font awesome icon
28893 * @cfg {String} pos Submenu align to (left | right) default right
28897 * Create a new Item
28898 * @param {Object} config The config object
28902 Roo.bootstrap.menu.Item = function(config){
28903 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28907 * Fires when the mouse is hovering over this menu
28908 * @param {Roo.bootstrap.menu.Item} this
28909 * @param {Roo.EventObject} e
28914 * Fires when the mouse exits this menu
28915 * @param {Roo.bootstrap.menu.Item} this
28916 * @param {Roo.EventObject} e
28922 * The raw click event for the entire grid.
28923 * @param {Roo.EventObject} e
28929 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28934 preventDefault: true,
28939 getAutoCreate : function()
28944 cls : 'roo-menu-item-text',
28952 cls : 'fa ' + this.icon
28961 href : this.href || '#',
28968 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28972 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28974 if(this.pos == 'left'){
28975 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28982 initEvents : function()
28984 this.el.on('mouseover', this.onMouseOver, this);
28985 this.el.on('mouseout', this.onMouseOut, this);
28987 this.el.select('a', true).first().on('click', this.onClick, this);
28991 onClick : function(e)
28993 if(this.preventDefault){
28994 e.preventDefault();
28997 this.fireEvent("click", this, e);
29000 onMouseOver : function(e)
29002 if(this.submenu && this.pos == 'left'){
29003 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29006 this.fireEvent("mouseover", this, e);
29009 onMouseOut : function(e)
29011 this.fireEvent("mouseout", this, e);
29023 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29026 * @class Roo.bootstrap.menu.Separator
29027 * @extends Roo.bootstrap.Component
29028 * Bootstrap Separator class
29031 * Create a new Separator
29032 * @param {Object} config The config object
29036 Roo.bootstrap.menu.Separator = function(config){
29037 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29040 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29042 getAutoCreate : function(){
29045 cls: 'dropdown-divider divider'
29063 * @class Roo.bootstrap.Tooltip
29064 * Bootstrap Tooltip class
29065 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29066 * to determine which dom element triggers the tooltip.
29068 * It needs to add support for additional attributes like tooltip-position
29071 * Create a new Toolti
29072 * @param {Object} config The config object
29075 Roo.bootstrap.Tooltip = function(config){
29076 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29078 this.alignment = Roo.bootstrap.Tooltip.alignment;
29080 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29081 this.alignment = config.alignment;
29086 Roo.apply(Roo.bootstrap.Tooltip, {
29088 * @function init initialize tooltip monitoring.
29092 currentTip : false,
29093 currentRegion : false,
29099 Roo.get(document).on('mouseover', this.enter ,this);
29100 Roo.get(document).on('mouseout', this.leave, this);
29103 this.currentTip = new Roo.bootstrap.Tooltip();
29106 enter : function(ev)
29108 var dom = ev.getTarget();
29110 //Roo.log(['enter',dom]);
29111 var el = Roo.fly(dom);
29112 if (this.currentEl) {
29114 //Roo.log(this.currentEl);
29115 //Roo.log(this.currentEl.contains(dom));
29116 if (this.currentEl == el) {
29119 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29125 if (this.currentTip.el) {
29126 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29130 if(!el || el.dom == document){
29136 if (!el.attr('tooltip')) {
29137 pel = el.findParent("[tooltip]");
29139 bindEl = Roo.get(pel);
29145 // you can not look for children, as if el is the body.. then everythign is the child..
29146 if (!pel && !el.attr('tooltip')) { //
29147 if (!el.select("[tooltip]").elements.length) {
29150 // is the mouse over this child...?
29151 bindEl = el.select("[tooltip]").first();
29152 var xy = ev.getXY();
29153 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29154 //Roo.log("not in region.");
29157 //Roo.log("child element over..");
29160 this.currentEl = el;
29161 this.currentTip.bind(bindEl);
29162 this.currentRegion = Roo.lib.Region.getRegion(dom);
29163 this.currentTip.enter();
29166 leave : function(ev)
29168 var dom = ev.getTarget();
29169 //Roo.log(['leave',dom]);
29170 if (!this.currentEl) {
29175 if (dom != this.currentEl.dom) {
29178 var xy = ev.getXY();
29179 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29182 // only activate leave if mouse cursor is outside... bounding box..
29187 if (this.currentTip) {
29188 this.currentTip.leave();
29190 //Roo.log('clear currentEl');
29191 this.currentEl = false;
29196 'left' : ['r-l', [-2,0], 'right'],
29197 'right' : ['l-r', [2,0], 'left'],
29198 'bottom' : ['t-b', [0,2], 'top'],
29199 'top' : [ 'b-t', [0,-2], 'bottom']
29205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29210 delay : null, // can be { show : 300 , hide: 500}
29214 hoverState : null, //???
29216 placement : 'bottom',
29220 getAutoCreate : function(){
29227 cls : 'tooltip-arrow arrow'
29230 cls : 'tooltip-inner'
29237 bind : function(el)
29242 initEvents : function()
29244 this.arrowEl = this.el.select('.arrow', true).first();
29245 this.innerEl = this.el.select('.tooltip-inner', true).first();
29248 enter : function () {
29250 if (this.timeout != null) {
29251 clearTimeout(this.timeout);
29254 this.hoverState = 'in';
29255 //Roo.log("enter - show");
29256 if (!this.delay || !this.delay.show) {
29261 this.timeout = setTimeout(function () {
29262 if (_t.hoverState == 'in') {
29265 }, this.delay.show);
29269 clearTimeout(this.timeout);
29271 this.hoverState = 'out';
29272 if (!this.delay || !this.delay.hide) {
29278 this.timeout = setTimeout(function () {
29279 //Roo.log("leave - timeout");
29281 if (_t.hoverState == 'out') {
29283 Roo.bootstrap.Tooltip.currentEl = false;
29288 show : function (msg)
29291 this.render(document.body);
29294 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29296 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29298 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29300 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29301 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29303 var placement = typeof this.placement == 'function' ?
29304 this.placement.call(this, this.el, on_el) :
29307 var autoToken = /\s?auto?\s?/i;
29308 var autoPlace = autoToken.test(placement);
29310 placement = placement.replace(autoToken, '') || 'top';
29314 //this.el.setXY([0,0]);
29316 //this.el.dom.style.display='block';
29318 //this.el.appendTo(on_el);
29320 var p = this.getPosition();
29321 var box = this.el.getBox();
29327 var align = this.alignment[placement];
29329 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29331 if(placement == 'top' || placement == 'bottom'){
29333 placement = 'right';
29336 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29337 placement = 'left';
29340 var scroll = Roo.select('body', true).first().getScroll();
29342 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29346 align = this.alignment[placement];
29348 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29352 var elems = document.getElementsByTagName('div');
29353 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29354 for (var i = 0; i < elems.length; i++) {
29355 var zindex = Number.parseInt(
29356 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29359 if (zindex > highest) {
29366 this.el.dom.style.zIndex = highest;
29368 this.el.alignTo(this.bindEl, align[0],align[1]);
29369 //var arrow = this.el.select('.arrow',true).first();
29370 //arrow.set(align[2],
29372 this.el.addClass(placement);
29373 this.el.addClass("bs-tooltip-"+ placement);
29375 this.el.addClass('in fade show');
29377 this.hoverState = null;
29379 if (this.el.hasClass('fade')) {
29394 //this.el.setXY([0,0]);
29395 this.el.removeClass(['show', 'in']);
29411 * @class Roo.bootstrap.LocationPicker
29412 * @extends Roo.bootstrap.Component
29413 * Bootstrap LocationPicker class
29414 * @cfg {Number} latitude Position when init default 0
29415 * @cfg {Number} longitude Position when init default 0
29416 * @cfg {Number} zoom default 15
29417 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29418 * @cfg {Boolean} mapTypeControl default false
29419 * @cfg {Boolean} disableDoubleClickZoom default false
29420 * @cfg {Boolean} scrollwheel default true
29421 * @cfg {Boolean} streetViewControl default false
29422 * @cfg {Number} radius default 0
29423 * @cfg {String} locationName
29424 * @cfg {Boolean} draggable default true
29425 * @cfg {Boolean} enableAutocomplete default false
29426 * @cfg {Boolean} enableReverseGeocode default true
29427 * @cfg {String} markerTitle
29430 * Create a new LocationPicker
29431 * @param {Object} config The config object
29435 Roo.bootstrap.LocationPicker = function(config){
29437 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29442 * Fires when the picker initialized.
29443 * @param {Roo.bootstrap.LocationPicker} this
29444 * @param {Google Location} location
29448 * @event positionchanged
29449 * Fires when the picker position changed.
29450 * @param {Roo.bootstrap.LocationPicker} this
29451 * @param {Google Location} location
29453 positionchanged : true,
29456 * Fires when the map resize.
29457 * @param {Roo.bootstrap.LocationPicker} this
29462 * Fires when the map show.
29463 * @param {Roo.bootstrap.LocationPicker} this
29468 * Fires when the map hide.
29469 * @param {Roo.bootstrap.LocationPicker} this
29474 * Fires when click the map.
29475 * @param {Roo.bootstrap.LocationPicker} this
29476 * @param {Map event} e
29480 * @event mapRightClick
29481 * Fires when right click the map.
29482 * @param {Roo.bootstrap.LocationPicker} this
29483 * @param {Map event} e
29485 mapRightClick : true,
29487 * @event markerClick
29488 * Fires when click the marker.
29489 * @param {Roo.bootstrap.LocationPicker} this
29490 * @param {Map event} e
29492 markerClick : true,
29494 * @event markerRightClick
29495 * Fires when right click the marker.
29496 * @param {Roo.bootstrap.LocationPicker} this
29497 * @param {Map event} e
29499 markerRightClick : true,
29501 * @event OverlayViewDraw
29502 * Fires when OverlayView Draw
29503 * @param {Roo.bootstrap.LocationPicker} this
29505 OverlayViewDraw : true,
29507 * @event OverlayViewOnAdd
29508 * Fires when OverlayView Draw
29509 * @param {Roo.bootstrap.LocationPicker} this
29511 OverlayViewOnAdd : true,
29513 * @event OverlayViewOnRemove
29514 * Fires when OverlayView Draw
29515 * @param {Roo.bootstrap.LocationPicker} this
29517 OverlayViewOnRemove : true,
29519 * @event OverlayViewShow
29520 * Fires when OverlayView Draw
29521 * @param {Roo.bootstrap.LocationPicker} this
29522 * @param {Pixel} cpx
29524 OverlayViewShow : true,
29526 * @event OverlayViewHide
29527 * Fires when OverlayView Draw
29528 * @param {Roo.bootstrap.LocationPicker} this
29530 OverlayViewHide : true,
29532 * @event loadexception
29533 * Fires when load google lib failed.
29534 * @param {Roo.bootstrap.LocationPicker} this
29536 loadexception : true
29541 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29543 gMapContext: false,
29549 mapTypeControl: false,
29550 disableDoubleClickZoom: false,
29552 streetViewControl: false,
29556 enableAutocomplete: false,
29557 enableReverseGeocode: true,
29560 getAutoCreate: function()
29565 cls: 'roo-location-picker'
29571 initEvents: function(ct, position)
29573 if(!this.el.getWidth() || this.isApplied()){
29577 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29582 initial: function()
29584 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29585 this.fireEvent('loadexception', this);
29589 if(!this.mapTypeId){
29590 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29593 this.gMapContext = this.GMapContext();
29595 this.initOverlayView();
29597 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29601 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29602 _this.setPosition(_this.gMapContext.marker.position);
29605 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29606 _this.fireEvent('mapClick', this, event);
29610 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29611 _this.fireEvent('mapRightClick', this, event);
29615 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29616 _this.fireEvent('markerClick', this, event);
29620 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29621 _this.fireEvent('markerRightClick', this, event);
29625 this.setPosition(this.gMapContext.location);
29627 this.fireEvent('initial', this, this.gMapContext.location);
29630 initOverlayView: function()
29634 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29638 _this.fireEvent('OverlayViewDraw', _this);
29643 _this.fireEvent('OverlayViewOnAdd', _this);
29646 onRemove: function()
29648 _this.fireEvent('OverlayViewOnRemove', _this);
29651 show: function(cpx)
29653 _this.fireEvent('OverlayViewShow', _this, cpx);
29658 _this.fireEvent('OverlayViewHide', _this);
29664 fromLatLngToContainerPixel: function(event)
29666 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29669 isApplied: function()
29671 return this.getGmapContext() == false ? false : true;
29674 getGmapContext: function()
29676 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29679 GMapContext: function()
29681 var position = new google.maps.LatLng(this.latitude, this.longitude);
29683 var _map = new google.maps.Map(this.el.dom, {
29686 mapTypeId: this.mapTypeId,
29687 mapTypeControl: this.mapTypeControl,
29688 disableDoubleClickZoom: this.disableDoubleClickZoom,
29689 scrollwheel: this.scrollwheel,
29690 streetViewControl: this.streetViewControl,
29691 locationName: this.locationName,
29692 draggable: this.draggable,
29693 enableAutocomplete: this.enableAutocomplete,
29694 enableReverseGeocode: this.enableReverseGeocode
29697 var _marker = new google.maps.Marker({
29698 position: position,
29700 title: this.markerTitle,
29701 draggable: this.draggable
29708 location: position,
29709 radius: this.radius,
29710 locationName: this.locationName,
29711 addressComponents: {
29712 formatted_address: null,
29713 addressLine1: null,
29714 addressLine2: null,
29716 streetNumber: null,
29720 stateOrProvince: null
29723 domContainer: this.el.dom,
29724 geodecoder: new google.maps.Geocoder()
29728 drawCircle: function(center, radius, options)
29730 if (this.gMapContext.circle != null) {
29731 this.gMapContext.circle.setMap(null);
29735 options = Roo.apply({}, options, {
29736 strokeColor: "#0000FF",
29737 strokeOpacity: .35,
29739 fillColor: "#0000FF",
29743 options.map = this.gMapContext.map;
29744 options.radius = radius;
29745 options.center = center;
29746 this.gMapContext.circle = new google.maps.Circle(options);
29747 return this.gMapContext.circle;
29753 setPosition: function(location)
29755 this.gMapContext.location = location;
29756 this.gMapContext.marker.setPosition(location);
29757 this.gMapContext.map.panTo(location);
29758 this.drawCircle(location, this.gMapContext.radius, {});
29762 if (this.gMapContext.settings.enableReverseGeocode) {
29763 this.gMapContext.geodecoder.geocode({
29764 latLng: this.gMapContext.location
29765 }, function(results, status) {
29767 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29768 _this.gMapContext.locationName = results[0].formatted_address;
29769 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29771 _this.fireEvent('positionchanged', this, location);
29778 this.fireEvent('positionchanged', this, location);
29783 google.maps.event.trigger(this.gMapContext.map, "resize");
29785 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29787 this.fireEvent('resize', this);
29790 setPositionByLatLng: function(latitude, longitude)
29792 this.setPosition(new google.maps.LatLng(latitude, longitude));
29795 getCurrentPosition: function()
29798 latitude: this.gMapContext.location.lat(),
29799 longitude: this.gMapContext.location.lng()
29803 getAddressName: function()
29805 return this.gMapContext.locationName;
29808 getAddressComponents: function()
29810 return this.gMapContext.addressComponents;
29813 address_component_from_google_geocode: function(address_components)
29817 for (var i = 0; i < address_components.length; i++) {
29818 var component = address_components[i];
29819 if (component.types.indexOf("postal_code") >= 0) {
29820 result.postalCode = component.short_name;
29821 } else if (component.types.indexOf("street_number") >= 0) {
29822 result.streetNumber = component.short_name;
29823 } else if (component.types.indexOf("route") >= 0) {
29824 result.streetName = component.short_name;
29825 } else if (component.types.indexOf("neighborhood") >= 0) {
29826 result.city = component.short_name;
29827 } else if (component.types.indexOf("locality") >= 0) {
29828 result.city = component.short_name;
29829 } else if (component.types.indexOf("sublocality") >= 0) {
29830 result.district = component.short_name;
29831 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29832 result.stateOrProvince = component.short_name;
29833 } else if (component.types.indexOf("country") >= 0) {
29834 result.country = component.short_name;
29838 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29839 result.addressLine2 = "";
29843 setZoomLevel: function(zoom)
29845 this.gMapContext.map.setZoom(zoom);
29858 this.fireEvent('show', this);
29869 this.fireEvent('hide', this);
29874 Roo.apply(Roo.bootstrap.LocationPicker, {
29876 OverlayView : function(map, options)
29878 options = options || {};
29885 * @class Roo.bootstrap.Alert
29886 * @extends Roo.bootstrap.Component
29887 * Bootstrap Alert class - shows an alert area box
29889 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29890 Enter a valid email address
29893 * @cfg {String} title The title of alert
29894 * @cfg {String} html The content of alert
29895 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29896 * @cfg {String} fa font-awesomeicon
29897 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29898 * @cfg {Boolean} close true to show a x closer
29902 * Create a new alert
29903 * @param {Object} config The config object
29907 Roo.bootstrap.Alert = function(config){
29908 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29912 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29918 faicon: false, // BC
29922 getAutoCreate : function()
29934 style : this.close ? '' : 'display:none'
29938 cls : 'roo-alert-icon'
29943 cls : 'roo-alert-title',
29948 cls : 'roo-alert-text',
29955 cfg.cn[0].cls += ' fa ' + this.faicon;
29958 cfg.cn[0].cls += ' fa ' + this.fa;
29962 cfg.cls += ' alert-' + this.weight;
29968 initEvents: function()
29970 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29971 this.titleEl = this.el.select('.roo-alert-title',true).first();
29972 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29973 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29974 if (this.seconds > 0) {
29975 this.hide.defer(this.seconds, this);
29979 * Set the Title Message HTML
29980 * @param {String} html
29982 setTitle : function(str)
29984 this.titleEl.dom.innerHTML = str;
29988 * Set the Body Message HTML
29989 * @param {String} html
29991 setHtml : function(str)
29993 this.htmlEl.dom.innerHTML = str;
29996 * Set the Weight of the alert
29997 * @param {String} (success|info|warning|danger) weight
30000 setWeight : function(weight)
30003 this.el.removeClass('alert-' + this.weight);
30006 this.weight = weight;
30008 this.el.addClass('alert-' + this.weight);
30011 * Set the Icon of the alert
30012 * @param {String} see fontawsome names (name without the 'fa-' bit)
30014 setIcon : function(icon)
30017 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30020 this.faicon = icon;
30022 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30047 * @class Roo.bootstrap.UploadCropbox
30048 * @extends Roo.bootstrap.Component
30049 * Bootstrap UploadCropbox class
30050 * @cfg {String} emptyText show when image has been loaded
30051 * @cfg {String} rotateNotify show when image too small to rotate
30052 * @cfg {Number} errorTimeout default 3000
30053 * @cfg {Number} minWidth default 300
30054 * @cfg {Number} minHeight default 300
30055 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30056 * @cfg {Boolean} isDocument (true|false) default false
30057 * @cfg {String} url action url
30058 * @cfg {String} paramName default 'imageUpload'
30059 * @cfg {String} method default POST
30060 * @cfg {Boolean} loadMask (true|false) default true
30061 * @cfg {Boolean} loadingText default 'Loading...'
30064 * Create a new UploadCropbox
30065 * @param {Object} config The config object
30068 Roo.bootstrap.UploadCropbox = function(config){
30069 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30073 * @event beforeselectfile
30074 * Fire before select file
30075 * @param {Roo.bootstrap.UploadCropbox} this
30077 "beforeselectfile" : true,
30080 * Fire after initEvent
30081 * @param {Roo.bootstrap.UploadCropbox} this
30086 * Fire after initEvent
30087 * @param {Roo.bootstrap.UploadCropbox} this
30088 * @param {String} data
30093 * Fire when preparing the file data
30094 * @param {Roo.bootstrap.UploadCropbox} this
30095 * @param {Object} file
30100 * Fire when get exception
30101 * @param {Roo.bootstrap.UploadCropbox} this
30102 * @param {XMLHttpRequest} xhr
30104 "exception" : true,
30106 * @event beforeloadcanvas
30107 * Fire before load the canvas
30108 * @param {Roo.bootstrap.UploadCropbox} this
30109 * @param {String} src
30111 "beforeloadcanvas" : true,
30114 * Fire when trash image
30115 * @param {Roo.bootstrap.UploadCropbox} this
30120 * Fire when download the image
30121 * @param {Roo.bootstrap.UploadCropbox} this
30125 * @event footerbuttonclick
30126 * Fire when footerbuttonclick
30127 * @param {Roo.bootstrap.UploadCropbox} this
30128 * @param {String} type
30130 "footerbuttonclick" : true,
30134 * @param {Roo.bootstrap.UploadCropbox} this
30139 * Fire when rotate the image
30140 * @param {Roo.bootstrap.UploadCropbox} this
30141 * @param {String} pos
30146 * Fire when inspect the file
30147 * @param {Roo.bootstrap.UploadCropbox} this
30148 * @param {Object} file
30153 * Fire when xhr upload the file
30154 * @param {Roo.bootstrap.UploadCropbox} this
30155 * @param {Object} data
30160 * Fire when arrange the file data
30161 * @param {Roo.bootstrap.UploadCropbox} this
30162 * @param {Object} formData
30167 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30170 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30172 emptyText : 'Click to upload image',
30173 rotateNotify : 'Image is too small to rotate',
30174 errorTimeout : 3000,
30188 cropType : 'image/jpeg',
30190 canvasLoaded : false,
30191 isDocument : false,
30193 paramName : 'imageUpload',
30195 loadingText : 'Loading...',
30198 getAutoCreate : function()
30202 cls : 'roo-upload-cropbox',
30206 cls : 'roo-upload-cropbox-selector',
30211 cls : 'roo-upload-cropbox-body',
30212 style : 'cursor:pointer',
30216 cls : 'roo-upload-cropbox-preview'
30220 cls : 'roo-upload-cropbox-thumb'
30224 cls : 'roo-upload-cropbox-empty-notify',
30225 html : this.emptyText
30229 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30230 html : this.rotateNotify
30236 cls : 'roo-upload-cropbox-footer',
30239 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30249 onRender : function(ct, position)
30251 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30253 if (this.buttons.length) {
30255 Roo.each(this.buttons, function(bb) {
30257 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30259 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30265 this.maskEl = this.el;
30269 initEvents : function()
30271 this.urlAPI = (window.createObjectURL && window) ||
30272 (window.URL && URL.revokeObjectURL && URL) ||
30273 (window.webkitURL && webkitURL);
30275 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30276 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30278 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30279 this.selectorEl.hide();
30281 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30282 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30284 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30285 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30286 this.thumbEl.hide();
30288 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30289 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30291 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30292 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30293 this.errorEl.hide();
30295 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30296 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30297 this.footerEl.hide();
30299 this.setThumbBoxSize();
30305 this.fireEvent('initial', this);
30312 window.addEventListener("resize", function() { _this.resize(); } );
30314 this.bodyEl.on('click', this.beforeSelectFile, this);
30317 this.bodyEl.on('touchstart', this.onTouchStart, this);
30318 this.bodyEl.on('touchmove', this.onTouchMove, this);
30319 this.bodyEl.on('touchend', this.onTouchEnd, this);
30323 this.bodyEl.on('mousedown', this.onMouseDown, this);
30324 this.bodyEl.on('mousemove', this.onMouseMove, this);
30325 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30326 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30327 Roo.get(document).on('mouseup', this.onMouseUp, this);
30330 this.selectorEl.on('change', this.onFileSelected, this);
30336 this.baseScale = 1;
30338 this.baseRotate = 1;
30339 this.dragable = false;
30340 this.pinching = false;
30343 this.cropData = false;
30344 this.notifyEl.dom.innerHTML = this.emptyText;
30346 this.selectorEl.dom.value = '';
30350 resize : function()
30352 if(this.fireEvent('resize', this) != false){
30353 this.setThumbBoxPosition();
30354 this.setCanvasPosition();
30358 onFooterButtonClick : function(e, el, o, type)
30361 case 'rotate-left' :
30362 this.onRotateLeft(e);
30364 case 'rotate-right' :
30365 this.onRotateRight(e);
30368 this.beforeSelectFile(e);
30383 this.fireEvent('footerbuttonclick', this, type);
30386 beforeSelectFile : function(e)
30388 e.preventDefault();
30390 if(this.fireEvent('beforeselectfile', this) != false){
30391 this.selectorEl.dom.click();
30395 onFileSelected : function(e)
30397 e.preventDefault();
30399 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30403 var file = this.selectorEl.dom.files[0];
30405 if(this.fireEvent('inspect', this, file) != false){
30406 this.prepare(file);
30411 trash : function(e)
30413 this.fireEvent('trash', this);
30416 download : function(e)
30418 this.fireEvent('download', this);
30421 loadCanvas : function(src)
30423 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30427 this.imageEl = document.createElement('img');
30431 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30433 this.imageEl.src = src;
30437 onLoadCanvas : function()
30439 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30440 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30442 this.bodyEl.un('click', this.beforeSelectFile, this);
30444 this.notifyEl.hide();
30445 this.thumbEl.show();
30446 this.footerEl.show();
30448 this.baseRotateLevel();
30450 if(this.isDocument){
30451 this.setThumbBoxSize();
30454 this.setThumbBoxPosition();
30456 this.baseScaleLevel();
30462 this.canvasLoaded = true;
30465 this.maskEl.unmask();
30470 setCanvasPosition : function()
30472 if(!this.canvasEl){
30476 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30477 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30479 this.previewEl.setLeft(pw);
30480 this.previewEl.setTop(ph);
30484 onMouseDown : function(e)
30488 this.dragable = true;
30489 this.pinching = false;
30491 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30492 this.dragable = false;
30496 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30497 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30501 onMouseMove : function(e)
30505 if(!this.canvasLoaded){
30509 if (!this.dragable){
30513 var minX = Math.ceil(this.thumbEl.getLeft(true));
30514 var minY = Math.ceil(this.thumbEl.getTop(true));
30516 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30517 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30519 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30520 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30522 x = x - this.mouseX;
30523 y = y - this.mouseY;
30525 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30526 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30528 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30529 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30531 this.previewEl.setLeft(bgX);
30532 this.previewEl.setTop(bgY);
30534 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30535 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30538 onMouseUp : function(e)
30542 this.dragable = false;
30545 onMouseWheel : function(e)
30549 this.startScale = this.scale;
30551 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30553 if(!this.zoomable()){
30554 this.scale = this.startScale;
30563 zoomable : function()
30565 var minScale = this.thumbEl.getWidth() / this.minWidth;
30567 if(this.minWidth < this.minHeight){
30568 minScale = this.thumbEl.getHeight() / this.minHeight;
30571 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30572 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30576 (this.rotate == 0 || this.rotate == 180) &&
30578 width > this.imageEl.OriginWidth ||
30579 height > this.imageEl.OriginHeight ||
30580 (width < this.minWidth && height < this.minHeight)
30588 (this.rotate == 90 || this.rotate == 270) &&
30590 width > this.imageEl.OriginWidth ||
30591 height > this.imageEl.OriginHeight ||
30592 (width < this.minHeight && height < this.minWidth)
30599 !this.isDocument &&
30600 (this.rotate == 0 || this.rotate == 180) &&
30602 width < this.minWidth ||
30603 width > this.imageEl.OriginWidth ||
30604 height < this.minHeight ||
30605 height > this.imageEl.OriginHeight
30612 !this.isDocument &&
30613 (this.rotate == 90 || this.rotate == 270) &&
30615 width < this.minHeight ||
30616 width > this.imageEl.OriginWidth ||
30617 height < this.minWidth ||
30618 height > this.imageEl.OriginHeight
30628 onRotateLeft : function(e)
30630 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30632 var minScale = this.thumbEl.getWidth() / this.minWidth;
30634 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30635 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30637 this.startScale = this.scale;
30639 while (this.getScaleLevel() < minScale){
30641 this.scale = this.scale + 1;
30643 if(!this.zoomable()){
30648 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30649 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30654 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30661 this.scale = this.startScale;
30663 this.onRotateFail();
30668 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30670 if(this.isDocument){
30671 this.setThumbBoxSize();
30672 this.setThumbBoxPosition();
30673 this.setCanvasPosition();
30678 this.fireEvent('rotate', this, 'left');
30682 onRotateRight : function(e)
30684 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30686 var minScale = this.thumbEl.getWidth() / this.minWidth;
30688 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30689 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30691 this.startScale = this.scale;
30693 while (this.getScaleLevel() < minScale){
30695 this.scale = this.scale + 1;
30697 if(!this.zoomable()){
30702 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30703 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30708 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30715 this.scale = this.startScale;
30717 this.onRotateFail();
30722 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30724 if(this.isDocument){
30725 this.setThumbBoxSize();
30726 this.setThumbBoxPosition();
30727 this.setCanvasPosition();
30732 this.fireEvent('rotate', this, 'right');
30735 onRotateFail : function()
30737 this.errorEl.show(true);
30741 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30746 this.previewEl.dom.innerHTML = '';
30748 var canvasEl = document.createElement("canvas");
30750 var contextEl = canvasEl.getContext("2d");
30752 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30753 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30754 var center = this.imageEl.OriginWidth / 2;
30756 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30757 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30758 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30759 center = this.imageEl.OriginHeight / 2;
30762 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30764 contextEl.translate(center, center);
30765 contextEl.rotate(this.rotate * Math.PI / 180);
30767 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30769 this.canvasEl = document.createElement("canvas");
30771 this.contextEl = this.canvasEl.getContext("2d");
30773 switch (this.rotate) {
30776 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30777 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30779 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30784 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30785 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30787 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30788 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);
30792 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30797 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30798 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30800 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30801 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);
30805 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);
30810 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30811 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30813 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30814 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30818 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);
30825 this.previewEl.appendChild(this.canvasEl);
30827 this.setCanvasPosition();
30832 if(!this.canvasLoaded){
30836 var imageCanvas = document.createElement("canvas");
30838 var imageContext = imageCanvas.getContext("2d");
30840 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30841 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30843 var center = imageCanvas.width / 2;
30845 imageContext.translate(center, center);
30847 imageContext.rotate(this.rotate * Math.PI / 180);
30849 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30851 var canvas = document.createElement("canvas");
30853 var context = canvas.getContext("2d");
30855 canvas.width = this.minWidth;
30856 canvas.height = this.minHeight;
30858 switch (this.rotate) {
30861 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30862 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30864 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30865 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30867 var targetWidth = this.minWidth - 2 * x;
30868 var targetHeight = this.minHeight - 2 * y;
30872 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30873 scale = targetWidth / width;
30876 if(x > 0 && y == 0){
30877 scale = targetHeight / height;
30880 if(x > 0 && y > 0){
30881 scale = targetWidth / width;
30883 if(width < height){
30884 scale = targetHeight / height;
30888 context.scale(scale, scale);
30890 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30891 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30893 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30894 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30896 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30901 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30902 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30904 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30905 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30907 var targetWidth = this.minWidth - 2 * x;
30908 var targetHeight = this.minHeight - 2 * y;
30912 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30913 scale = targetWidth / width;
30916 if(x > 0 && y == 0){
30917 scale = targetHeight / height;
30920 if(x > 0 && y > 0){
30921 scale = targetWidth / width;
30923 if(width < height){
30924 scale = targetHeight / height;
30928 context.scale(scale, scale);
30930 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30931 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30933 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30934 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30936 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30938 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30943 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30944 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30946 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30947 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30949 var targetWidth = this.minWidth - 2 * x;
30950 var targetHeight = this.minHeight - 2 * y;
30954 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30955 scale = targetWidth / width;
30958 if(x > 0 && y == 0){
30959 scale = targetHeight / height;
30962 if(x > 0 && y > 0){
30963 scale = targetWidth / width;
30965 if(width < height){
30966 scale = targetHeight / height;
30970 context.scale(scale, scale);
30972 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30973 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30975 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30976 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30978 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30979 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30981 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30986 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30987 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30989 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30990 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30992 var targetWidth = this.minWidth - 2 * x;
30993 var targetHeight = this.minHeight - 2 * y;
30997 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30998 scale = targetWidth / width;
31001 if(x > 0 && y == 0){
31002 scale = targetHeight / height;
31005 if(x > 0 && y > 0){
31006 scale = targetWidth / width;
31008 if(width < height){
31009 scale = targetHeight / height;
31013 context.scale(scale, scale);
31015 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31016 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31018 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31019 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31021 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31023 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31030 this.cropData = canvas.toDataURL(this.cropType);
31032 if(this.fireEvent('crop', this, this.cropData) !== false){
31033 this.process(this.file, this.cropData);
31040 setThumbBoxSize : function()
31044 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31045 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31046 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31048 this.minWidth = width;
31049 this.minHeight = height;
31051 if(this.rotate == 90 || this.rotate == 270){
31052 this.minWidth = height;
31053 this.minHeight = width;
31058 width = Math.ceil(this.minWidth * height / this.minHeight);
31060 if(this.minWidth > this.minHeight){
31062 height = Math.ceil(this.minHeight * width / this.minWidth);
31065 this.thumbEl.setStyle({
31066 width : width + 'px',
31067 height : height + 'px'
31074 setThumbBoxPosition : function()
31076 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31077 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31079 this.thumbEl.setLeft(x);
31080 this.thumbEl.setTop(y);
31084 baseRotateLevel : function()
31086 this.baseRotate = 1;
31089 typeof(this.exif) != 'undefined' &&
31090 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31091 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31093 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31096 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31100 baseScaleLevel : function()
31104 if(this.isDocument){
31106 if(this.baseRotate == 6 || this.baseRotate == 8){
31108 height = this.thumbEl.getHeight();
31109 this.baseScale = height / this.imageEl.OriginWidth;
31111 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31112 width = this.thumbEl.getWidth();
31113 this.baseScale = width / this.imageEl.OriginHeight;
31119 height = this.thumbEl.getHeight();
31120 this.baseScale = height / this.imageEl.OriginHeight;
31122 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31123 width = this.thumbEl.getWidth();
31124 this.baseScale = width / this.imageEl.OriginWidth;
31130 if(this.baseRotate == 6 || this.baseRotate == 8){
31132 width = this.thumbEl.getHeight();
31133 this.baseScale = width / this.imageEl.OriginHeight;
31135 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31136 height = this.thumbEl.getWidth();
31137 this.baseScale = height / this.imageEl.OriginHeight;
31140 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31141 height = this.thumbEl.getWidth();
31142 this.baseScale = height / this.imageEl.OriginHeight;
31144 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31145 width = this.thumbEl.getHeight();
31146 this.baseScale = width / this.imageEl.OriginWidth;
31153 width = this.thumbEl.getWidth();
31154 this.baseScale = width / this.imageEl.OriginWidth;
31156 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31157 height = this.thumbEl.getHeight();
31158 this.baseScale = height / this.imageEl.OriginHeight;
31161 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31163 height = this.thumbEl.getHeight();
31164 this.baseScale = height / this.imageEl.OriginHeight;
31166 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31167 width = this.thumbEl.getWidth();
31168 this.baseScale = width / this.imageEl.OriginWidth;
31176 getScaleLevel : function()
31178 return this.baseScale * Math.pow(1.1, this.scale);
31181 onTouchStart : function(e)
31183 if(!this.canvasLoaded){
31184 this.beforeSelectFile(e);
31188 var touches = e.browserEvent.touches;
31194 if(touches.length == 1){
31195 this.onMouseDown(e);
31199 if(touches.length != 2){
31205 for(var i = 0, finger; finger = touches[i]; i++){
31206 coords.push(finger.pageX, finger.pageY);
31209 var x = Math.pow(coords[0] - coords[2], 2);
31210 var y = Math.pow(coords[1] - coords[3], 2);
31212 this.startDistance = Math.sqrt(x + y);
31214 this.startScale = this.scale;
31216 this.pinching = true;
31217 this.dragable = false;
31221 onTouchMove : function(e)
31223 if(!this.pinching && !this.dragable){
31227 var touches = e.browserEvent.touches;
31234 this.onMouseMove(e);
31240 for(var i = 0, finger; finger = touches[i]; i++){
31241 coords.push(finger.pageX, finger.pageY);
31244 var x = Math.pow(coords[0] - coords[2], 2);
31245 var y = Math.pow(coords[1] - coords[3], 2);
31247 this.endDistance = Math.sqrt(x + y);
31249 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31251 if(!this.zoomable()){
31252 this.scale = this.startScale;
31260 onTouchEnd : function(e)
31262 this.pinching = false;
31263 this.dragable = false;
31267 process : function(file, crop)
31270 this.maskEl.mask(this.loadingText);
31273 this.xhr = new XMLHttpRequest();
31275 file.xhr = this.xhr;
31277 this.xhr.open(this.method, this.url, true);
31280 "Accept": "application/json",
31281 "Cache-Control": "no-cache",
31282 "X-Requested-With": "XMLHttpRequest"
31285 for (var headerName in headers) {
31286 var headerValue = headers[headerName];
31288 this.xhr.setRequestHeader(headerName, headerValue);
31294 this.xhr.onload = function()
31296 _this.xhrOnLoad(_this.xhr);
31299 this.xhr.onerror = function()
31301 _this.xhrOnError(_this.xhr);
31304 var formData = new FormData();
31306 formData.append('returnHTML', 'NO');
31309 formData.append('crop', crop);
31312 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31313 formData.append(this.paramName, file, file.name);
31316 if(typeof(file.filename) != 'undefined'){
31317 formData.append('filename', file.filename);
31320 if(typeof(file.mimetype) != 'undefined'){
31321 formData.append('mimetype', file.mimetype);
31324 if(this.fireEvent('arrange', this, formData) != false){
31325 this.xhr.send(formData);
31329 xhrOnLoad : function(xhr)
31332 this.maskEl.unmask();
31335 if (xhr.readyState !== 4) {
31336 this.fireEvent('exception', this, xhr);
31340 var response = Roo.decode(xhr.responseText);
31342 if(!response.success){
31343 this.fireEvent('exception', this, xhr);
31347 var response = Roo.decode(xhr.responseText);
31349 this.fireEvent('upload', this, response);
31353 xhrOnError : function()
31356 this.maskEl.unmask();
31359 Roo.log('xhr on error');
31361 var response = Roo.decode(xhr.responseText);
31367 prepare : function(file)
31370 this.maskEl.mask(this.loadingText);
31376 if(typeof(file) === 'string'){
31377 this.loadCanvas(file);
31381 if(!file || !this.urlAPI){
31386 this.cropType = file.type;
31390 if(this.fireEvent('prepare', this, this.file) != false){
31392 var reader = new FileReader();
31394 reader.onload = function (e) {
31395 if (e.target.error) {
31396 Roo.log(e.target.error);
31400 var buffer = e.target.result,
31401 dataView = new DataView(buffer),
31403 maxOffset = dataView.byteLength - 4,
31407 if (dataView.getUint16(0) === 0xffd8) {
31408 while (offset < maxOffset) {
31409 markerBytes = dataView.getUint16(offset);
31411 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31412 markerLength = dataView.getUint16(offset + 2) + 2;
31413 if (offset + markerLength > dataView.byteLength) {
31414 Roo.log('Invalid meta data: Invalid segment size.');
31418 if(markerBytes == 0xffe1){
31419 _this.parseExifData(
31426 offset += markerLength;
31436 var url = _this.urlAPI.createObjectURL(_this.file);
31438 _this.loadCanvas(url);
31443 reader.readAsArrayBuffer(this.file);
31449 parseExifData : function(dataView, offset, length)
31451 var tiffOffset = offset + 10,
31455 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31456 // No Exif data, might be XMP data instead
31460 // Check for the ASCII code for "Exif" (0x45786966):
31461 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31462 // No Exif data, might be XMP data instead
31465 if (tiffOffset + 8 > dataView.byteLength) {
31466 Roo.log('Invalid Exif data: Invalid segment size.');
31469 // Check for the two null bytes:
31470 if (dataView.getUint16(offset + 8) !== 0x0000) {
31471 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31474 // Check the byte alignment:
31475 switch (dataView.getUint16(tiffOffset)) {
31477 littleEndian = true;
31480 littleEndian = false;
31483 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31486 // Check for the TIFF tag marker (0x002A):
31487 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31488 Roo.log('Invalid Exif data: Missing TIFF marker.');
31491 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31492 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31494 this.parseExifTags(
31497 tiffOffset + dirOffset,
31502 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31507 if (dirOffset + 6 > dataView.byteLength) {
31508 Roo.log('Invalid Exif data: Invalid directory offset.');
31511 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31512 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31513 if (dirEndOffset + 4 > dataView.byteLength) {
31514 Roo.log('Invalid Exif data: Invalid directory size.');
31517 for (i = 0; i < tagsNumber; i += 1) {
31521 dirOffset + 2 + 12 * i, // tag offset
31525 // Return the offset to the next directory:
31526 return dataView.getUint32(dirEndOffset, littleEndian);
31529 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31531 var tag = dataView.getUint16(offset, littleEndian);
31533 this.exif[tag] = this.getExifValue(
31537 dataView.getUint16(offset + 2, littleEndian), // tag type
31538 dataView.getUint32(offset + 4, littleEndian), // tag length
31543 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31545 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31554 Roo.log('Invalid Exif data: Invalid tag type.');
31558 tagSize = tagType.size * length;
31559 // Determine if the value is contained in the dataOffset bytes,
31560 // or if the value at the dataOffset is a pointer to the actual data:
31561 dataOffset = tagSize > 4 ?
31562 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31563 if (dataOffset + tagSize > dataView.byteLength) {
31564 Roo.log('Invalid Exif data: Invalid data offset.');
31567 if (length === 1) {
31568 return tagType.getValue(dataView, dataOffset, littleEndian);
31571 for (i = 0; i < length; i += 1) {
31572 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31575 if (tagType.ascii) {
31577 // Concatenate the chars:
31578 for (i = 0; i < values.length; i += 1) {
31580 // Ignore the terminating NULL byte(s):
31581 if (c === '\u0000') {
31593 Roo.apply(Roo.bootstrap.UploadCropbox, {
31595 'Orientation': 0x0112
31599 1: 0, //'top-left',
31601 3: 180, //'bottom-right',
31602 // 4: 'bottom-left',
31604 6: 90, //'right-top',
31605 // 7: 'right-bottom',
31606 8: 270 //'left-bottom'
31610 // byte, 8-bit unsigned int:
31612 getValue: function (dataView, dataOffset) {
31613 return dataView.getUint8(dataOffset);
31617 // ascii, 8-bit byte:
31619 getValue: function (dataView, dataOffset) {
31620 return String.fromCharCode(dataView.getUint8(dataOffset));
31625 // short, 16 bit int:
31627 getValue: function (dataView, dataOffset, littleEndian) {
31628 return dataView.getUint16(dataOffset, littleEndian);
31632 // long, 32 bit int:
31634 getValue: function (dataView, dataOffset, littleEndian) {
31635 return dataView.getUint32(dataOffset, littleEndian);
31639 // rational = two long values, first is numerator, second is denominator:
31641 getValue: function (dataView, dataOffset, littleEndian) {
31642 return dataView.getUint32(dataOffset, littleEndian) /
31643 dataView.getUint32(dataOffset + 4, littleEndian);
31647 // slong, 32 bit signed int:
31649 getValue: function (dataView, dataOffset, littleEndian) {
31650 return dataView.getInt32(dataOffset, littleEndian);
31654 // srational, two slongs, first is numerator, second is denominator:
31656 getValue: function (dataView, dataOffset, littleEndian) {
31657 return dataView.getInt32(dataOffset, littleEndian) /
31658 dataView.getInt32(dataOffset + 4, littleEndian);
31668 cls : 'btn-group roo-upload-cropbox-rotate-left',
31669 action : 'rotate-left',
31673 cls : 'btn btn-default',
31674 html : '<i class="fa fa-undo"></i>'
31680 cls : 'btn-group roo-upload-cropbox-picture',
31681 action : 'picture',
31685 cls : 'btn btn-default',
31686 html : '<i class="fa fa-picture-o"></i>'
31692 cls : 'btn-group roo-upload-cropbox-rotate-right',
31693 action : 'rotate-right',
31697 cls : 'btn btn-default',
31698 html : '<i class="fa fa-repeat"></i>'
31706 cls : 'btn-group roo-upload-cropbox-rotate-left',
31707 action : 'rotate-left',
31711 cls : 'btn btn-default',
31712 html : '<i class="fa fa-undo"></i>'
31718 cls : 'btn-group roo-upload-cropbox-download',
31719 action : 'download',
31723 cls : 'btn btn-default',
31724 html : '<i class="fa fa-download"></i>'
31730 cls : 'btn-group roo-upload-cropbox-crop',
31735 cls : 'btn btn-default',
31736 html : '<i class="fa fa-crop"></i>'
31742 cls : 'btn-group roo-upload-cropbox-trash',
31747 cls : 'btn btn-default',
31748 html : '<i class="fa fa-trash"></i>'
31754 cls : 'btn-group roo-upload-cropbox-rotate-right',
31755 action : 'rotate-right',
31759 cls : 'btn btn-default',
31760 html : '<i class="fa fa-repeat"></i>'
31768 cls : 'btn-group roo-upload-cropbox-rotate-left',
31769 action : 'rotate-left',
31773 cls : 'btn btn-default',
31774 html : '<i class="fa fa-undo"></i>'
31780 cls : 'btn-group roo-upload-cropbox-rotate-right',
31781 action : 'rotate-right',
31785 cls : 'btn btn-default',
31786 html : '<i class="fa fa-repeat"></i>'
31799 * @class Roo.bootstrap.DocumentManager
31800 * @extends Roo.bootstrap.Component
31801 * Bootstrap DocumentManager class
31802 * @cfg {String} paramName default 'imageUpload'
31803 * @cfg {String} toolTipName default 'filename'
31804 * @cfg {String} method default POST
31805 * @cfg {String} url action url
31806 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31807 * @cfg {Boolean} multiple multiple upload default true
31808 * @cfg {Number} thumbSize default 300
31809 * @cfg {String} fieldLabel
31810 * @cfg {Number} labelWidth default 4
31811 * @cfg {String} labelAlign (left|top) default left
31812 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31813 * @cfg {Number} labellg set the width of label (1-12)
31814 * @cfg {Number} labelmd set the width of label (1-12)
31815 * @cfg {Number} labelsm set the width of label (1-12)
31816 * @cfg {Number} labelxs set the width of label (1-12)
31819 * Create a new DocumentManager
31820 * @param {Object} config The config object
31823 Roo.bootstrap.DocumentManager = function(config){
31824 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31827 this.delegates = [];
31832 * Fire when initial the DocumentManager
31833 * @param {Roo.bootstrap.DocumentManager} this
31838 * inspect selected file
31839 * @param {Roo.bootstrap.DocumentManager} this
31840 * @param {File} file
31845 * Fire when xhr load exception
31846 * @param {Roo.bootstrap.DocumentManager} this
31847 * @param {XMLHttpRequest} xhr
31849 "exception" : true,
31851 * @event afterupload
31852 * Fire when xhr load exception
31853 * @param {Roo.bootstrap.DocumentManager} this
31854 * @param {XMLHttpRequest} xhr
31856 "afterupload" : true,
31859 * prepare the form data
31860 * @param {Roo.bootstrap.DocumentManager} this
31861 * @param {Object} formData
31866 * Fire when remove the file
31867 * @param {Roo.bootstrap.DocumentManager} this
31868 * @param {Object} file
31873 * Fire after refresh the file
31874 * @param {Roo.bootstrap.DocumentManager} this
31879 * Fire after click the image
31880 * @param {Roo.bootstrap.DocumentManager} this
31881 * @param {Object} file
31886 * Fire when upload a image and editable set to true
31887 * @param {Roo.bootstrap.DocumentManager} this
31888 * @param {Object} file
31892 * @event beforeselectfile
31893 * Fire before select file
31894 * @param {Roo.bootstrap.DocumentManager} this
31896 "beforeselectfile" : true,
31899 * Fire before process file
31900 * @param {Roo.bootstrap.DocumentManager} this
31901 * @param {Object} file
31905 * @event previewrendered
31906 * Fire when preview rendered
31907 * @param {Roo.bootstrap.DocumentManager} this
31908 * @param {Object} file
31910 "previewrendered" : true,
31913 "previewResize" : true
31918 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31927 paramName : 'imageUpload',
31928 toolTipName : 'filename',
31931 labelAlign : 'left',
31941 getAutoCreate : function()
31943 var managerWidget = {
31945 cls : 'roo-document-manager',
31949 cls : 'roo-document-manager-selector',
31954 cls : 'roo-document-manager-uploader',
31958 cls : 'roo-document-manager-upload-btn',
31959 html : '<i class="fa fa-plus"></i>'
31970 cls : 'column col-md-12',
31975 if(this.fieldLabel.length){
31980 cls : 'column col-md-12',
31981 html : this.fieldLabel
31985 cls : 'column col-md-12',
31990 if(this.labelAlign == 'left'){
31995 html : this.fieldLabel
32004 if(this.labelWidth > 12){
32005 content[0].style = "width: " + this.labelWidth + 'px';
32008 if(this.labelWidth < 13 && this.labelmd == 0){
32009 this.labelmd = this.labelWidth;
32012 if(this.labellg > 0){
32013 content[0].cls += ' col-lg-' + this.labellg;
32014 content[1].cls += ' col-lg-' + (12 - this.labellg);
32017 if(this.labelmd > 0){
32018 content[0].cls += ' col-md-' + this.labelmd;
32019 content[1].cls += ' col-md-' + (12 - this.labelmd);
32022 if(this.labelsm > 0){
32023 content[0].cls += ' col-sm-' + this.labelsm;
32024 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32027 if(this.labelxs > 0){
32028 content[0].cls += ' col-xs-' + this.labelxs;
32029 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32037 cls : 'row clearfix',
32045 initEvents : function()
32047 this.managerEl = this.el.select('.roo-document-manager', true).first();
32048 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32050 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32051 this.selectorEl.hide();
32054 this.selectorEl.attr('multiple', 'multiple');
32057 this.selectorEl.on('change', this.onFileSelected, this);
32059 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32060 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32062 this.uploader.on('click', this.onUploaderClick, this);
32064 this.renderProgressDialog();
32068 window.addEventListener("resize", function() { _this.refresh(); } );
32070 this.fireEvent('initial', this);
32073 renderProgressDialog : function()
32077 this.progressDialog = new Roo.bootstrap.Modal({
32078 cls : 'roo-document-manager-progress-dialog',
32079 allow_close : false,
32090 btnclick : function() {
32091 _this.uploadCancel();
32097 this.progressDialog.render(Roo.get(document.body));
32099 this.progress = new Roo.bootstrap.Progress({
32100 cls : 'roo-document-manager-progress',
32105 this.progress.render(this.progressDialog.getChildContainer());
32107 this.progressBar = new Roo.bootstrap.ProgressBar({
32108 cls : 'roo-document-manager-progress-bar',
32111 aria_valuemax : 12,
32115 this.progressBar.render(this.progress.getChildContainer());
32118 onUploaderClick : function(e)
32120 e.preventDefault();
32122 if(this.fireEvent('beforeselectfile', this) != false){
32123 this.selectorEl.dom.click();
32128 onFileSelected : function(e)
32130 e.preventDefault();
32132 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32136 Roo.each(this.selectorEl.dom.files, function(file){
32137 if(this.fireEvent('inspect', this, file) != false){
32138 this.files.push(file);
32148 this.selectorEl.dom.value = '';
32150 if(!this.files || !this.files.length){
32154 if(this.boxes > 0 && this.files.length > this.boxes){
32155 this.files = this.files.slice(0, this.boxes);
32158 this.uploader.show();
32160 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32161 this.uploader.hide();
32170 Roo.each(this.files, function(file){
32172 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32173 var f = this.renderPreview(file);
32178 if(file.type.indexOf('image') != -1){
32179 this.delegates.push(
32181 _this.process(file);
32182 }).createDelegate(this)
32190 _this.process(file);
32191 }).createDelegate(this)
32196 this.files = files;
32198 this.delegates = this.delegates.concat(docs);
32200 if(!this.delegates.length){
32205 this.progressBar.aria_valuemax = this.delegates.length;
32212 arrange : function()
32214 if(!this.delegates.length){
32215 this.progressDialog.hide();
32220 var delegate = this.delegates.shift();
32222 this.progressDialog.show();
32224 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32226 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32231 refresh : function()
32233 this.uploader.show();
32235 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32236 this.uploader.hide();
32239 Roo.isTouch ? this.closable(false) : this.closable(true);
32241 this.fireEvent('refresh', this);
32244 onRemove : function(e, el, o)
32246 e.preventDefault();
32248 this.fireEvent('remove', this, o);
32252 remove : function(o)
32256 Roo.each(this.files, function(file){
32257 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32266 this.files = files;
32273 Roo.each(this.files, function(file){
32278 file.target.remove();
32287 onClick : function(e, el, o)
32289 e.preventDefault();
32291 this.fireEvent('click', this, o);
32295 closable : function(closable)
32297 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32299 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32311 xhrOnLoad : function(xhr)
32313 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32317 if (xhr.readyState !== 4) {
32319 this.fireEvent('exception', this, xhr);
32323 var response = Roo.decode(xhr.responseText);
32325 if(!response.success){
32327 this.fireEvent('exception', this, xhr);
32331 var file = this.renderPreview(response.data);
32333 this.files.push(file);
32337 this.fireEvent('afterupload', this, xhr);
32341 xhrOnError : function(xhr)
32343 Roo.log('xhr on error');
32345 var response = Roo.decode(xhr.responseText);
32352 process : function(file)
32354 if(this.fireEvent('process', this, file) !== false){
32355 if(this.editable && file.type.indexOf('image') != -1){
32356 this.fireEvent('edit', this, file);
32360 this.uploadStart(file, false);
32367 uploadStart : function(file, crop)
32369 this.xhr = new XMLHttpRequest();
32371 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32376 file.xhr = this.xhr;
32378 this.managerEl.createChild({
32380 cls : 'roo-document-manager-loading',
32384 tooltip : file.name,
32385 cls : 'roo-document-manager-thumb',
32386 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32392 this.xhr.open(this.method, this.url, true);
32395 "Accept": "application/json",
32396 "Cache-Control": "no-cache",
32397 "X-Requested-With": "XMLHttpRequest"
32400 for (var headerName in headers) {
32401 var headerValue = headers[headerName];
32403 this.xhr.setRequestHeader(headerName, headerValue);
32409 this.xhr.onload = function()
32411 _this.xhrOnLoad(_this.xhr);
32414 this.xhr.onerror = function()
32416 _this.xhrOnError(_this.xhr);
32419 var formData = new FormData();
32421 formData.append('returnHTML', 'NO');
32424 formData.append('crop', crop);
32427 formData.append(this.paramName, file, file.name);
32434 if(this.fireEvent('prepare', this, formData, options) != false){
32436 if(options.manually){
32440 this.xhr.send(formData);
32444 this.uploadCancel();
32447 uploadCancel : function()
32453 this.delegates = [];
32455 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32462 renderPreview : function(file)
32464 if(typeof(file.target) != 'undefined' && file.target){
32468 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32470 var previewEl = this.managerEl.createChild({
32472 cls : 'roo-document-manager-preview',
32476 tooltip : file[this.toolTipName],
32477 cls : 'roo-document-manager-thumb',
32478 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32483 html : '<i class="fa fa-times-circle"></i>'
32488 var close = previewEl.select('button.close', true).first();
32490 close.on('click', this.onRemove, this, file);
32492 file.target = previewEl;
32494 var image = previewEl.select('img', true).first();
32498 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32500 image.on('click', this.onClick, this, file);
32502 this.fireEvent('previewrendered', this, file);
32508 onPreviewLoad : function(file, image)
32510 if(typeof(file.target) == 'undefined' || !file.target){
32514 var width = image.dom.naturalWidth || image.dom.width;
32515 var height = image.dom.naturalHeight || image.dom.height;
32517 if(!this.previewResize) {
32521 if(width > height){
32522 file.target.addClass('wide');
32526 file.target.addClass('tall');
32531 uploadFromSource : function(file, crop)
32533 this.xhr = new XMLHttpRequest();
32535 this.managerEl.createChild({
32537 cls : 'roo-document-manager-loading',
32541 tooltip : file.name,
32542 cls : 'roo-document-manager-thumb',
32543 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32549 this.xhr.open(this.method, this.url, true);
32552 "Accept": "application/json",
32553 "Cache-Control": "no-cache",
32554 "X-Requested-With": "XMLHttpRequest"
32557 for (var headerName in headers) {
32558 var headerValue = headers[headerName];
32560 this.xhr.setRequestHeader(headerName, headerValue);
32566 this.xhr.onload = function()
32568 _this.xhrOnLoad(_this.xhr);
32571 this.xhr.onerror = function()
32573 _this.xhrOnError(_this.xhr);
32576 var formData = new FormData();
32578 formData.append('returnHTML', 'NO');
32580 formData.append('crop', crop);
32582 if(typeof(file.filename) != 'undefined'){
32583 formData.append('filename', file.filename);
32586 if(typeof(file.mimetype) != 'undefined'){
32587 formData.append('mimetype', file.mimetype);
32592 if(this.fireEvent('prepare', this, formData) != false){
32593 this.xhr.send(formData);
32603 * @class Roo.bootstrap.DocumentViewer
32604 * @extends Roo.bootstrap.Component
32605 * Bootstrap DocumentViewer class
32606 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32607 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32610 * Create a new DocumentViewer
32611 * @param {Object} config The config object
32614 Roo.bootstrap.DocumentViewer = function(config){
32615 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32620 * Fire after initEvent
32621 * @param {Roo.bootstrap.DocumentViewer} this
32627 * @param {Roo.bootstrap.DocumentViewer} this
32632 * Fire after download button
32633 * @param {Roo.bootstrap.DocumentViewer} this
32638 * Fire after trash button
32639 * @param {Roo.bootstrap.DocumentViewer} this
32646 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32648 showDownload : true,
32652 getAutoCreate : function()
32656 cls : 'roo-document-viewer',
32660 cls : 'roo-document-viewer-body',
32664 cls : 'roo-document-viewer-thumb',
32668 cls : 'roo-document-viewer-image'
32676 cls : 'roo-document-viewer-footer',
32679 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32683 cls : 'btn-group roo-document-viewer-download',
32687 cls : 'btn btn-default',
32688 html : '<i class="fa fa-download"></i>'
32694 cls : 'btn-group roo-document-viewer-trash',
32698 cls : 'btn btn-default',
32699 html : '<i class="fa fa-trash"></i>'
32712 initEvents : function()
32714 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32715 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32717 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32718 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32720 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32721 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32723 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32724 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32726 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32727 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32729 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32730 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32732 this.bodyEl.on('click', this.onClick, this);
32733 this.downloadBtn.on('click', this.onDownload, this);
32734 this.trashBtn.on('click', this.onTrash, this);
32736 this.downloadBtn.hide();
32737 this.trashBtn.hide();
32739 if(this.showDownload){
32740 this.downloadBtn.show();
32743 if(this.showTrash){
32744 this.trashBtn.show();
32747 if(!this.showDownload && !this.showTrash) {
32748 this.footerEl.hide();
32753 initial : function()
32755 this.fireEvent('initial', this);
32759 onClick : function(e)
32761 e.preventDefault();
32763 this.fireEvent('click', this);
32766 onDownload : function(e)
32768 e.preventDefault();
32770 this.fireEvent('download', this);
32773 onTrash : function(e)
32775 e.preventDefault();
32777 this.fireEvent('trash', this);
32789 * @class Roo.bootstrap.NavProgressBar
32790 * @extends Roo.bootstrap.Component
32791 * Bootstrap NavProgressBar class
32794 * Create a new nav progress bar
32795 * @param {Object} config The config object
32798 Roo.bootstrap.NavProgressBar = function(config){
32799 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32801 this.bullets = this.bullets || [];
32803 // Roo.bootstrap.NavProgressBar.register(this);
32807 * Fires when the active item changes
32808 * @param {Roo.bootstrap.NavProgressBar} this
32809 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32810 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32817 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32822 getAutoCreate : function()
32824 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32828 cls : 'roo-navigation-bar-group',
32832 cls : 'roo-navigation-top-bar'
32836 cls : 'roo-navigation-bullets-bar',
32840 cls : 'roo-navigation-bar'
32847 cls : 'roo-navigation-bottom-bar'
32857 initEvents: function()
32862 onRender : function(ct, position)
32864 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32866 if(this.bullets.length){
32867 Roo.each(this.bullets, function(b){
32876 addItem : function(cfg)
32878 var item = new Roo.bootstrap.NavProgressItem(cfg);
32880 item.parentId = this.id;
32881 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32884 var top = new Roo.bootstrap.Element({
32886 cls : 'roo-navigation-bar-text'
32889 var bottom = new Roo.bootstrap.Element({
32891 cls : 'roo-navigation-bar-text'
32894 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32895 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32897 var topText = new Roo.bootstrap.Element({
32899 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32902 var bottomText = new Roo.bootstrap.Element({
32904 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32907 topText.onRender(top.el, null);
32908 bottomText.onRender(bottom.el, null);
32911 item.bottomEl = bottom;
32914 this.barItems.push(item);
32919 getActive : function()
32921 var active = false;
32923 Roo.each(this.barItems, function(v){
32925 if (!v.isActive()) {
32937 setActiveItem : function(item)
32941 Roo.each(this.barItems, function(v){
32942 if (v.rid == item.rid) {
32946 if (v.isActive()) {
32947 v.setActive(false);
32952 item.setActive(true);
32954 this.fireEvent('changed', this, item, prev);
32957 getBarItem: function(rid)
32961 Roo.each(this.barItems, function(e) {
32962 if (e.rid != rid) {
32973 indexOfItem : function(item)
32977 Roo.each(this.barItems, function(v, i){
32979 if (v.rid != item.rid) {
32990 setActiveNext : function()
32992 var i = this.indexOfItem(this.getActive());
32994 if (i > this.barItems.length) {
32998 this.setActiveItem(this.barItems[i+1]);
33001 setActivePrev : function()
33003 var i = this.indexOfItem(this.getActive());
33009 this.setActiveItem(this.barItems[i-1]);
33012 format : function()
33014 if(!this.barItems.length){
33018 var width = 100 / this.barItems.length;
33020 Roo.each(this.barItems, function(i){
33021 i.el.setStyle('width', width + '%');
33022 i.topEl.el.setStyle('width', width + '%');
33023 i.bottomEl.el.setStyle('width', width + '%');
33032 * Nav Progress Item
33037 * @class Roo.bootstrap.NavProgressItem
33038 * @extends Roo.bootstrap.Component
33039 * Bootstrap NavProgressItem class
33040 * @cfg {String} rid the reference id
33041 * @cfg {Boolean} active (true|false) Is item active default false
33042 * @cfg {Boolean} disabled (true|false) Is item active default false
33043 * @cfg {String} html
33044 * @cfg {String} position (top|bottom) text position default bottom
33045 * @cfg {String} icon show icon instead of number
33048 * Create a new NavProgressItem
33049 * @param {Object} config The config object
33051 Roo.bootstrap.NavProgressItem = function(config){
33052 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33057 * The raw click event for the entire grid.
33058 * @param {Roo.bootstrap.NavProgressItem} this
33059 * @param {Roo.EventObject} e
33066 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33072 position : 'bottom',
33075 getAutoCreate : function()
33077 var iconCls = 'roo-navigation-bar-item-icon';
33079 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33083 cls: 'roo-navigation-bar-item',
33093 cfg.cls += ' active';
33096 cfg.cls += ' disabled';
33102 disable : function()
33104 this.setDisabled(true);
33107 enable : function()
33109 this.setDisabled(false);
33112 initEvents: function()
33114 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33116 this.iconEl.on('click', this.onClick, this);
33119 onClick : function(e)
33121 e.preventDefault();
33127 if(this.fireEvent('click', this, e) === false){
33131 this.parent().setActiveItem(this);
33134 isActive: function ()
33136 return this.active;
33139 setActive : function(state)
33141 if(this.active == state){
33145 this.active = state;
33148 this.el.addClass('active');
33152 this.el.removeClass('active');
33157 setDisabled : function(state)
33159 if(this.disabled == state){
33163 this.disabled = state;
33166 this.el.addClass('disabled');
33170 this.el.removeClass('disabled');
33173 tooltipEl : function()
33175 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33188 * @class Roo.bootstrap.FieldLabel
33189 * @extends Roo.bootstrap.Component
33190 * Bootstrap FieldLabel class
33191 * @cfg {String} html contents of the element
33192 * @cfg {String} tag tag of the element default label
33193 * @cfg {String} cls class of the element
33194 * @cfg {String} target label target
33195 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33196 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33197 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33198 * @cfg {String} iconTooltip default "This field is required"
33199 * @cfg {String} indicatorpos (left|right) default left
33202 * Create a new FieldLabel
33203 * @param {Object} config The config object
33206 Roo.bootstrap.FieldLabel = function(config){
33207 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33212 * Fires after the field has been marked as invalid.
33213 * @param {Roo.form.FieldLabel} this
33214 * @param {String} msg The validation message
33219 * Fires after the field has been validated with no errors.
33220 * @param {Roo.form.FieldLabel} this
33226 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33233 invalidClass : 'has-warning',
33234 validClass : 'has-success',
33235 iconTooltip : 'This field is required',
33236 indicatorpos : 'left',
33238 getAutoCreate : function(){
33241 if (!this.allowBlank) {
33247 cls : 'roo-bootstrap-field-label ' + this.cls,
33252 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33253 tooltip : this.iconTooltip
33262 if(this.indicatorpos == 'right'){
33265 cls : 'roo-bootstrap-field-label ' + this.cls,
33274 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33275 tooltip : this.iconTooltip
33284 initEvents: function()
33286 Roo.bootstrap.Element.superclass.initEvents.call(this);
33288 this.indicator = this.indicatorEl();
33290 if(this.indicator){
33291 this.indicator.removeClass('visible');
33292 this.indicator.addClass('invisible');
33295 Roo.bootstrap.FieldLabel.register(this);
33298 indicatorEl : function()
33300 var indicator = this.el.select('i.roo-required-indicator',true).first();
33311 * Mark this field as valid
33313 markValid : function()
33315 if(this.indicator){
33316 this.indicator.removeClass('visible');
33317 this.indicator.addClass('invisible');
33319 if (Roo.bootstrap.version == 3) {
33320 this.el.removeClass(this.invalidClass);
33321 this.el.addClass(this.validClass);
33323 this.el.removeClass('is-invalid');
33324 this.el.addClass('is-valid');
33328 this.fireEvent('valid', this);
33332 * Mark this field as invalid
33333 * @param {String} msg The validation message
33335 markInvalid : function(msg)
33337 if(this.indicator){
33338 this.indicator.removeClass('invisible');
33339 this.indicator.addClass('visible');
33341 if (Roo.bootstrap.version == 3) {
33342 this.el.removeClass(this.validClass);
33343 this.el.addClass(this.invalidClass);
33345 this.el.removeClass('is-valid');
33346 this.el.addClass('is-invalid');
33350 this.fireEvent('invalid', this, msg);
33356 Roo.apply(Roo.bootstrap.FieldLabel, {
33361 * register a FieldLabel Group
33362 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33364 register : function(label)
33366 if(this.groups.hasOwnProperty(label.target)){
33370 this.groups[label.target] = label;
33374 * fetch a FieldLabel Group based on the target
33375 * @param {string} target
33376 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33378 get: function(target) {
33379 if (typeof(this.groups[target]) == 'undefined') {
33383 return this.groups[target] ;
33392 * page DateSplitField.
33398 * @class Roo.bootstrap.DateSplitField
33399 * @extends Roo.bootstrap.Component
33400 * Bootstrap DateSplitField class
33401 * @cfg {string} fieldLabel - the label associated
33402 * @cfg {Number} labelWidth set the width of label (0-12)
33403 * @cfg {String} labelAlign (top|left)
33404 * @cfg {Boolean} dayAllowBlank (true|false) default false
33405 * @cfg {Boolean} monthAllowBlank (true|false) default false
33406 * @cfg {Boolean} yearAllowBlank (true|false) default false
33407 * @cfg {string} dayPlaceholder
33408 * @cfg {string} monthPlaceholder
33409 * @cfg {string} yearPlaceholder
33410 * @cfg {string} dayFormat default 'd'
33411 * @cfg {string} monthFormat default 'm'
33412 * @cfg {string} yearFormat default 'Y'
33413 * @cfg {Number} labellg set the width of label (1-12)
33414 * @cfg {Number} labelmd set the width of label (1-12)
33415 * @cfg {Number} labelsm set the width of label (1-12)
33416 * @cfg {Number} labelxs set the width of label (1-12)
33420 * Create a new DateSplitField
33421 * @param {Object} config The config object
33424 Roo.bootstrap.DateSplitField = function(config){
33425 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33431 * getting the data of years
33432 * @param {Roo.bootstrap.DateSplitField} this
33433 * @param {Object} years
33438 * getting the data of days
33439 * @param {Roo.bootstrap.DateSplitField} this
33440 * @param {Object} days
33445 * Fires after the field has been marked as invalid.
33446 * @param {Roo.form.Field} this
33447 * @param {String} msg The validation message
33452 * Fires after the field has been validated with no errors.
33453 * @param {Roo.form.Field} this
33459 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33462 labelAlign : 'top',
33464 dayAllowBlank : false,
33465 monthAllowBlank : false,
33466 yearAllowBlank : false,
33467 dayPlaceholder : '',
33468 monthPlaceholder : '',
33469 yearPlaceholder : '',
33473 isFormField : true,
33479 getAutoCreate : function()
33483 cls : 'row roo-date-split-field-group',
33488 cls : 'form-hidden-field roo-date-split-field-group-value',
33494 var labelCls = 'col-md-12';
33495 var contentCls = 'col-md-4';
33497 if(this.fieldLabel){
33501 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33505 html : this.fieldLabel
33510 if(this.labelAlign == 'left'){
33512 if(this.labelWidth > 12){
33513 label.style = "width: " + this.labelWidth + 'px';
33516 if(this.labelWidth < 13 && this.labelmd == 0){
33517 this.labelmd = this.labelWidth;
33520 if(this.labellg > 0){
33521 labelCls = ' col-lg-' + this.labellg;
33522 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33525 if(this.labelmd > 0){
33526 labelCls = ' col-md-' + this.labelmd;
33527 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33530 if(this.labelsm > 0){
33531 labelCls = ' col-sm-' + this.labelsm;
33532 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33535 if(this.labelxs > 0){
33536 labelCls = ' col-xs-' + this.labelxs;
33537 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33541 label.cls += ' ' + labelCls;
33543 cfg.cn.push(label);
33546 Roo.each(['day', 'month', 'year'], function(t){
33549 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33556 inputEl: function ()
33558 return this.el.select('.roo-date-split-field-group-value', true).first();
33561 onRender : function(ct, position)
33565 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33567 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33569 this.dayField = new Roo.bootstrap.ComboBox({
33570 allowBlank : this.dayAllowBlank,
33571 alwaysQuery : true,
33572 displayField : 'value',
33575 forceSelection : true,
33577 placeholder : this.dayPlaceholder,
33578 selectOnFocus : true,
33579 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33580 triggerAction : 'all',
33582 valueField : 'value',
33583 store : new Roo.data.SimpleStore({
33584 data : (function() {
33586 _this.fireEvent('days', _this, days);
33589 fields : [ 'value' ]
33592 select : function (_self, record, index)
33594 _this.setValue(_this.getValue());
33599 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33601 this.monthField = new Roo.bootstrap.MonthField({
33602 after : '<i class=\"fa fa-calendar\"></i>',
33603 allowBlank : this.monthAllowBlank,
33604 placeholder : this.monthPlaceholder,
33607 render : function (_self)
33609 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33610 e.preventDefault();
33614 select : function (_self, oldvalue, newvalue)
33616 _this.setValue(_this.getValue());
33621 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33623 this.yearField = new Roo.bootstrap.ComboBox({
33624 allowBlank : this.yearAllowBlank,
33625 alwaysQuery : true,
33626 displayField : 'value',
33629 forceSelection : true,
33631 placeholder : this.yearPlaceholder,
33632 selectOnFocus : true,
33633 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33634 triggerAction : 'all',
33636 valueField : 'value',
33637 store : new Roo.data.SimpleStore({
33638 data : (function() {
33640 _this.fireEvent('years', _this, years);
33643 fields : [ 'value' ]
33646 select : function (_self, record, index)
33648 _this.setValue(_this.getValue());
33653 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33656 setValue : function(v, format)
33658 this.inputEl.dom.value = v;
33660 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33662 var d = Date.parseDate(v, f);
33669 this.setDay(d.format(this.dayFormat));
33670 this.setMonth(d.format(this.monthFormat));
33671 this.setYear(d.format(this.yearFormat));
33678 setDay : function(v)
33680 this.dayField.setValue(v);
33681 this.inputEl.dom.value = this.getValue();
33686 setMonth : function(v)
33688 this.monthField.setValue(v, true);
33689 this.inputEl.dom.value = this.getValue();
33694 setYear : function(v)
33696 this.yearField.setValue(v);
33697 this.inputEl.dom.value = this.getValue();
33702 getDay : function()
33704 return this.dayField.getValue();
33707 getMonth : function()
33709 return this.monthField.getValue();
33712 getYear : function()
33714 return this.yearField.getValue();
33717 getValue : function()
33719 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33721 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33731 this.inputEl.dom.value = '';
33736 validate : function()
33738 var d = this.dayField.validate();
33739 var m = this.monthField.validate();
33740 var y = this.yearField.validate();
33745 (!this.dayAllowBlank && !d) ||
33746 (!this.monthAllowBlank && !m) ||
33747 (!this.yearAllowBlank && !y)
33752 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33761 this.markInvalid();
33766 markValid : function()
33769 var label = this.el.select('label', true).first();
33770 var icon = this.el.select('i.fa-star', true).first();
33776 this.fireEvent('valid', this);
33780 * Mark this field as invalid
33781 * @param {String} msg The validation message
33783 markInvalid : function(msg)
33786 var label = this.el.select('label', true).first();
33787 var icon = this.el.select('i.fa-star', true).first();
33789 if(label && !icon){
33790 this.el.select('.roo-date-split-field-label', true).createChild({
33792 cls : 'text-danger fa fa-lg fa-star',
33793 tooltip : 'This field is required',
33794 style : 'margin-right:5px;'
33798 this.fireEvent('invalid', this, msg);
33801 clearInvalid : function()
33803 var label = this.el.select('label', true).first();
33804 var icon = this.el.select('i.fa-star', true).first();
33810 this.fireEvent('valid', this);
33813 getName: function()
33823 * http://masonry.desandro.com
33825 * The idea is to render all the bricks based on vertical width...
33827 * The original code extends 'outlayer' - we might need to use that....
33833 * @class Roo.bootstrap.LayoutMasonry
33834 * @extends Roo.bootstrap.Component
33835 * Bootstrap Layout Masonry class
33838 * Create a new Element
33839 * @param {Object} config The config object
33842 Roo.bootstrap.LayoutMasonry = function(config){
33844 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33848 Roo.bootstrap.LayoutMasonry.register(this);
33854 * Fire after layout the items
33855 * @param {Roo.bootstrap.LayoutMasonry} this
33856 * @param {Roo.EventObject} e
33863 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33866 * @cfg {Boolean} isLayoutInstant = no animation?
33868 isLayoutInstant : false, // needed?
33871 * @cfg {Number} boxWidth width of the columns
33876 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33881 * @cfg {Number} padWidth padding below box..
33886 * @cfg {Number} gutter gutter width..
33891 * @cfg {Number} maxCols maximum number of columns
33897 * @cfg {Boolean} isAutoInitial defalut true
33899 isAutoInitial : true,
33904 * @cfg {Boolean} isHorizontal defalut false
33906 isHorizontal : false,
33908 currentSize : null,
33914 bricks: null, //CompositeElement
33918 _isLayoutInited : false,
33920 // isAlternative : false, // only use for vertical layout...
33923 * @cfg {Number} alternativePadWidth padding below box..
33925 alternativePadWidth : 50,
33927 selectedBrick : [],
33929 getAutoCreate : function(){
33931 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33935 cls: 'blog-masonary-wrapper ' + this.cls,
33937 cls : 'mas-boxes masonary'
33944 getChildContainer: function( )
33946 if (this.boxesEl) {
33947 return this.boxesEl;
33950 this.boxesEl = this.el.select('.mas-boxes').first();
33952 return this.boxesEl;
33956 initEvents : function()
33960 if(this.isAutoInitial){
33961 Roo.log('hook children rendered');
33962 this.on('childrenrendered', function() {
33963 Roo.log('children rendered');
33969 initial : function()
33971 this.selectedBrick = [];
33973 this.currentSize = this.el.getBox(true);
33975 Roo.EventManager.onWindowResize(this.resize, this);
33977 if(!this.isAutoInitial){
33985 //this.layout.defer(500,this);
33989 resize : function()
33991 var cs = this.el.getBox(true);
33994 this.currentSize.width == cs.width &&
33995 this.currentSize.x == cs.x &&
33996 this.currentSize.height == cs.height &&
33997 this.currentSize.y == cs.y
33999 Roo.log("no change in with or X or Y");
34003 this.currentSize = cs;
34009 layout : function()
34011 this._resetLayout();
34013 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34015 this.layoutItems( isInstant );
34017 this._isLayoutInited = true;
34019 this.fireEvent('layout', this);
34023 _resetLayout : function()
34025 if(this.isHorizontal){
34026 this.horizontalMeasureColumns();
34030 this.verticalMeasureColumns();
34034 verticalMeasureColumns : function()
34036 this.getContainerWidth();
34038 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34039 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34043 var boxWidth = this.boxWidth + this.padWidth;
34045 if(this.containerWidth < this.boxWidth){
34046 boxWidth = this.containerWidth
34049 var containerWidth = this.containerWidth;
34051 var cols = Math.floor(containerWidth / boxWidth);
34053 this.cols = Math.max( cols, 1 );
34055 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34057 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34059 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34061 this.colWidth = boxWidth + avail - this.padWidth;
34063 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34064 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34067 horizontalMeasureColumns : function()
34069 this.getContainerWidth();
34071 var boxWidth = this.boxWidth;
34073 if(this.containerWidth < boxWidth){
34074 boxWidth = this.containerWidth;
34077 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34079 this.el.setHeight(boxWidth);
34083 getContainerWidth : function()
34085 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34088 layoutItems : function( isInstant )
34090 Roo.log(this.bricks);
34092 var items = Roo.apply([], this.bricks);
34094 if(this.isHorizontal){
34095 this._horizontalLayoutItems( items , isInstant );
34099 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34100 // this._verticalAlternativeLayoutItems( items , isInstant );
34104 this._verticalLayoutItems( items , isInstant );
34108 _verticalLayoutItems : function ( items , isInstant)
34110 if ( !items || !items.length ) {
34115 ['xs', 'xs', 'xs', 'tall'],
34116 ['xs', 'xs', 'tall'],
34117 ['xs', 'xs', 'sm'],
34118 ['xs', 'xs', 'xs'],
34124 ['sm', 'xs', 'xs'],
34128 ['tall', 'xs', 'xs', 'xs'],
34129 ['tall', 'xs', 'xs'],
34141 Roo.each(items, function(item, k){
34143 switch (item.size) {
34144 // these layouts take up a full box,
34155 boxes.push([item]);
34178 var filterPattern = function(box, length)
34186 var pattern = box.slice(0, length);
34190 Roo.each(pattern, function(i){
34191 format.push(i.size);
34194 Roo.each(standard, function(s){
34196 if(String(s) != String(format)){
34205 if(!match && length == 1){
34210 filterPattern(box, length - 1);
34214 queue.push(pattern);
34216 box = box.slice(length, box.length);
34218 filterPattern(box, 4);
34224 Roo.each(boxes, function(box, k){
34230 if(box.length == 1){
34235 filterPattern(box, 4);
34239 this._processVerticalLayoutQueue( queue, isInstant );
34243 // _verticalAlternativeLayoutItems : function( items , isInstant )
34245 // if ( !items || !items.length ) {
34249 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34253 _horizontalLayoutItems : function ( items , isInstant)
34255 if ( !items || !items.length || items.length < 3) {
34261 var eItems = items.slice(0, 3);
34263 items = items.slice(3, items.length);
34266 ['xs', 'xs', 'xs', 'wide'],
34267 ['xs', 'xs', 'wide'],
34268 ['xs', 'xs', 'sm'],
34269 ['xs', 'xs', 'xs'],
34275 ['sm', 'xs', 'xs'],
34279 ['wide', 'xs', 'xs', 'xs'],
34280 ['wide', 'xs', 'xs'],
34293 Roo.each(items, function(item, k){
34295 switch (item.size) {
34306 boxes.push([item]);
34330 var filterPattern = function(box, length)
34338 var pattern = box.slice(0, length);
34342 Roo.each(pattern, function(i){
34343 format.push(i.size);
34346 Roo.each(standard, function(s){
34348 if(String(s) != String(format)){
34357 if(!match && length == 1){
34362 filterPattern(box, length - 1);
34366 queue.push(pattern);
34368 box = box.slice(length, box.length);
34370 filterPattern(box, 4);
34376 Roo.each(boxes, function(box, k){
34382 if(box.length == 1){
34387 filterPattern(box, 4);
34394 var pos = this.el.getBox(true);
34398 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34400 var hit_end = false;
34402 Roo.each(queue, function(box){
34406 Roo.each(box, function(b){
34408 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34418 Roo.each(box, function(b){
34420 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34423 mx = Math.max(mx, b.x);
34427 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34431 Roo.each(box, function(b){
34433 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34447 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34450 /** Sets position of item in DOM
34451 * @param {Element} item
34452 * @param {Number} x - horizontal position
34453 * @param {Number} y - vertical position
34454 * @param {Boolean} isInstant - disables transitions
34456 _processVerticalLayoutQueue : function( queue, isInstant )
34458 var pos = this.el.getBox(true);
34463 for (var i = 0; i < this.cols; i++){
34467 Roo.each(queue, function(box, k){
34469 var col = k % this.cols;
34471 Roo.each(box, function(b,kk){
34473 b.el.position('absolute');
34475 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34476 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34478 if(b.size == 'md-left' || b.size == 'md-right'){
34479 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34480 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34483 b.el.setWidth(width);
34484 b.el.setHeight(height);
34486 b.el.select('iframe',true).setSize(width,height);
34490 for (var i = 0; i < this.cols; i++){
34492 if(maxY[i] < maxY[col]){
34497 col = Math.min(col, i);
34501 x = pos.x + col * (this.colWidth + this.padWidth);
34505 var positions = [];
34507 switch (box.length){
34509 positions = this.getVerticalOneBoxColPositions(x, y, box);
34512 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34515 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34518 positions = this.getVerticalFourBoxColPositions(x, y, box);
34524 Roo.each(box, function(b,kk){
34526 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34528 var sz = b.el.getSize();
34530 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34538 for (var i = 0; i < this.cols; i++){
34539 mY = Math.max(mY, maxY[i]);
34542 this.el.setHeight(mY - pos.y);
34546 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34548 // var pos = this.el.getBox(true);
34551 // var maxX = pos.right;
34553 // var maxHeight = 0;
34555 // Roo.each(items, function(item, k){
34559 // item.el.position('absolute');
34561 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34563 // item.el.setWidth(width);
34565 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34567 // item.el.setHeight(height);
34570 // item.el.setXY([x, y], isInstant ? false : true);
34572 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34575 // y = y + height + this.alternativePadWidth;
34577 // maxHeight = maxHeight + height + this.alternativePadWidth;
34581 // this.el.setHeight(maxHeight);
34585 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34587 var pos = this.el.getBox(true);
34592 var maxX = pos.right;
34594 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34596 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34598 Roo.each(queue, function(box, k){
34600 Roo.each(box, function(b, kk){
34602 b.el.position('absolute');
34604 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34605 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34607 if(b.size == 'md-left' || b.size == 'md-right'){
34608 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34609 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34612 b.el.setWidth(width);
34613 b.el.setHeight(height);
34621 var positions = [];
34623 switch (box.length){
34625 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34628 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34631 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34634 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34640 Roo.each(box, function(b,kk){
34642 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34644 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34652 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34654 Roo.each(eItems, function(b,k){
34656 b.size = (k == 0) ? 'sm' : 'xs';
34657 b.x = (k == 0) ? 2 : 1;
34658 b.y = (k == 0) ? 2 : 1;
34660 b.el.position('absolute');
34662 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34664 b.el.setWidth(width);
34666 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34668 b.el.setHeight(height);
34672 var positions = [];
34675 x : maxX - this.unitWidth * 2 - this.gutter,
34680 x : maxX - this.unitWidth,
34681 y : minY + (this.unitWidth + this.gutter) * 2
34685 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34689 Roo.each(eItems, function(b,k){
34691 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34697 getVerticalOneBoxColPositions : function(x, y, box)
34701 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34703 if(box[0].size == 'md-left'){
34707 if(box[0].size == 'md-right'){
34712 x : x + (this.unitWidth + this.gutter) * rand,
34719 getVerticalTwoBoxColPositions : function(x, y, box)
34723 if(box[0].size == 'xs'){
34727 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34731 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34745 x : x + (this.unitWidth + this.gutter) * 2,
34746 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34753 getVerticalThreeBoxColPositions : function(x, y, box)
34757 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34765 x : x + (this.unitWidth + this.gutter) * 1,
34770 x : x + (this.unitWidth + this.gutter) * 2,
34778 if(box[0].size == 'xs' && box[1].size == 'xs'){
34787 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34791 x : x + (this.unitWidth + this.gutter) * 1,
34805 x : x + (this.unitWidth + this.gutter) * 2,
34810 x : x + (this.unitWidth + this.gutter) * 2,
34811 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34818 getVerticalFourBoxColPositions : function(x, y, box)
34822 if(box[0].size == 'xs'){
34831 y : y + (this.unitHeight + this.gutter) * 1
34836 y : y + (this.unitHeight + this.gutter) * 2
34840 x : x + (this.unitWidth + this.gutter) * 1,
34854 x : x + (this.unitWidth + this.gutter) * 2,
34859 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34860 y : y + (this.unitHeight + this.gutter) * 1
34864 x : x + (this.unitWidth + this.gutter) * 2,
34865 y : y + (this.unitWidth + this.gutter) * 2
34872 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34876 if(box[0].size == 'md-left'){
34878 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34885 if(box[0].size == 'md-right'){
34887 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34888 y : minY + (this.unitWidth + this.gutter) * 1
34894 var rand = Math.floor(Math.random() * (4 - box[0].y));
34897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898 y : minY + (this.unitWidth + this.gutter) * rand
34905 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34909 if(box[0].size == 'xs'){
34912 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34917 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34918 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34926 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34931 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34932 y : minY + (this.unitWidth + this.gutter) * 2
34939 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34943 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34946 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34951 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34952 y : minY + (this.unitWidth + this.gutter) * 1
34956 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34957 y : minY + (this.unitWidth + this.gutter) * 2
34964 if(box[0].size == 'xs' && box[1].size == 'xs'){
34967 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34977 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34978 y : minY + (this.unitWidth + this.gutter) * 1
34986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34991 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34992 y : minY + (this.unitWidth + this.gutter) * 2
34996 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34997 y : minY + (this.unitWidth + this.gutter) * 2
35004 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35008 if(box[0].size == 'xs'){
35011 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35016 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35021 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),
35026 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35027 y : minY + (this.unitWidth + this.gutter) * 1
35035 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35040 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35041 y : minY + (this.unitWidth + this.gutter) * 2
35045 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35046 y : minY + (this.unitWidth + this.gutter) * 2
35050 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),
35051 y : minY + (this.unitWidth + this.gutter) * 2
35059 * remove a Masonry Brick
35060 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35062 removeBrick : function(brick_id)
35068 for (var i = 0; i<this.bricks.length; i++) {
35069 if (this.bricks[i].id == brick_id) {
35070 this.bricks.splice(i,1);
35071 this.el.dom.removeChild(Roo.get(brick_id).dom);
35078 * adds a Masonry Brick
35079 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35081 addBrick : function(cfg)
35083 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35084 //this.register(cn);
35085 cn.parentId = this.id;
35086 cn.render(this.el);
35091 * register a Masonry Brick
35092 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35095 register : function(brick)
35097 this.bricks.push(brick);
35098 brick.masonryId = this.id;
35102 * clear all the Masonry Brick
35104 clearAll : function()
35107 //this.getChildContainer().dom.innerHTML = "";
35108 this.el.dom.innerHTML = '';
35111 getSelected : function()
35113 if (!this.selectedBrick) {
35117 return this.selectedBrick;
35121 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35125 * register a Masonry Layout
35126 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35129 register : function(layout)
35131 this.groups[layout.id] = layout;
35134 * fetch a Masonry Layout based on the masonry layout ID
35135 * @param {string} the masonry layout to add
35136 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35139 get: function(layout_id) {
35140 if (typeof(this.groups[layout_id]) == 'undefined') {
35143 return this.groups[layout_id] ;
35155 * http://masonry.desandro.com
35157 * The idea is to render all the bricks based on vertical width...
35159 * The original code extends 'outlayer' - we might need to use that....
35165 * @class Roo.bootstrap.LayoutMasonryAuto
35166 * @extends Roo.bootstrap.Component
35167 * Bootstrap Layout Masonry class
35170 * Create a new Element
35171 * @param {Object} config The config object
35174 Roo.bootstrap.LayoutMasonryAuto = function(config){
35175 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35178 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35181 * @cfg {Boolean} isFitWidth - resize the width..
35183 isFitWidth : false, // options..
35185 * @cfg {Boolean} isOriginLeft = left align?
35187 isOriginLeft : true,
35189 * @cfg {Boolean} isOriginTop = top align?
35191 isOriginTop : false,
35193 * @cfg {Boolean} isLayoutInstant = no animation?
35195 isLayoutInstant : false, // needed?
35197 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35199 isResizingContainer : true,
35201 * @cfg {Number} columnWidth width of the columns
35207 * @cfg {Number} maxCols maximum number of columns
35212 * @cfg {Number} padHeight padding below box..
35218 * @cfg {Boolean} isAutoInitial defalut true
35221 isAutoInitial : true,
35227 initialColumnWidth : 0,
35228 currentSize : null,
35230 colYs : null, // array.
35237 bricks: null, //CompositeElement
35238 cols : 0, // array?
35239 // element : null, // wrapped now this.el
35240 _isLayoutInited : null,
35243 getAutoCreate : function(){
35247 cls: 'blog-masonary-wrapper ' + this.cls,
35249 cls : 'mas-boxes masonary'
35256 getChildContainer: function( )
35258 if (this.boxesEl) {
35259 return this.boxesEl;
35262 this.boxesEl = this.el.select('.mas-boxes').first();
35264 return this.boxesEl;
35268 initEvents : function()
35272 if(this.isAutoInitial){
35273 Roo.log('hook children rendered');
35274 this.on('childrenrendered', function() {
35275 Roo.log('children rendered');
35282 initial : function()
35284 this.reloadItems();
35286 this.currentSize = this.el.getBox(true);
35288 /// was window resize... - let's see if this works..
35289 Roo.EventManager.onWindowResize(this.resize, this);
35291 if(!this.isAutoInitial){
35296 this.layout.defer(500,this);
35299 reloadItems: function()
35301 this.bricks = this.el.select('.masonry-brick', true);
35303 this.bricks.each(function(b) {
35304 //Roo.log(b.getSize());
35305 if (!b.attr('originalwidth')) {
35306 b.attr('originalwidth', b.getSize().width);
35311 Roo.log(this.bricks.elements.length);
35314 resize : function()
35317 var cs = this.el.getBox(true);
35319 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35320 Roo.log("no change in with or X");
35323 this.currentSize = cs;
35327 layout : function()
35330 this._resetLayout();
35331 //this._manageStamps();
35333 // don't animate first layout
35334 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35335 this.layoutItems( isInstant );
35337 // flag for initalized
35338 this._isLayoutInited = true;
35341 layoutItems : function( isInstant )
35343 //var items = this._getItemsForLayout( this.items );
35344 // original code supports filtering layout items.. we just ignore it..
35346 this._layoutItems( this.bricks , isInstant );
35348 this._postLayout();
35350 _layoutItems : function ( items , isInstant)
35352 //this.fireEvent( 'layout', this, items );
35355 if ( !items || !items.elements.length ) {
35356 // no items, emit event with empty array
35361 items.each(function(item) {
35362 Roo.log("layout item");
35364 // get x/y object from method
35365 var position = this._getItemLayoutPosition( item );
35367 position.item = item;
35368 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35369 queue.push( position );
35372 this._processLayoutQueue( queue );
35374 /** Sets position of item in DOM
35375 * @param {Element} item
35376 * @param {Number} x - horizontal position
35377 * @param {Number} y - vertical position
35378 * @param {Boolean} isInstant - disables transitions
35380 _processLayoutQueue : function( queue )
35382 for ( var i=0, len = queue.length; i < len; i++ ) {
35383 var obj = queue[i];
35384 obj.item.position('absolute');
35385 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35391 * Any logic you want to do after each layout,
35392 * i.e. size the container
35394 _postLayout : function()
35396 this.resizeContainer();
35399 resizeContainer : function()
35401 if ( !this.isResizingContainer ) {
35404 var size = this._getContainerSize();
35406 this.el.setSize(size.width,size.height);
35407 this.boxesEl.setSize(size.width,size.height);
35413 _resetLayout : function()
35415 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35416 this.colWidth = this.el.getWidth();
35417 //this.gutter = this.el.getWidth();
35419 this.measureColumns();
35425 this.colYs.push( 0 );
35431 measureColumns : function()
35433 this.getContainerWidth();
35434 // if columnWidth is 0, default to outerWidth of first item
35435 if ( !this.columnWidth ) {
35436 var firstItem = this.bricks.first();
35437 Roo.log(firstItem);
35438 this.columnWidth = this.containerWidth;
35439 if (firstItem && firstItem.attr('originalwidth') ) {
35440 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35442 // columnWidth fall back to item of first element
35443 Roo.log("set column width?");
35444 this.initialColumnWidth = this.columnWidth ;
35446 // if first elem has no width, default to size of container
35451 if (this.initialColumnWidth) {
35452 this.columnWidth = this.initialColumnWidth;
35457 // column width is fixed at the top - however if container width get's smaller we should
35460 // this bit calcs how man columns..
35462 var columnWidth = this.columnWidth += this.gutter;
35464 // calculate columns
35465 var containerWidth = this.containerWidth + this.gutter;
35467 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35468 // fix rounding errors, typically with gutters
35469 var excess = columnWidth - containerWidth % columnWidth;
35472 // if overshoot is less than a pixel, round up, otherwise floor it
35473 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35474 cols = Math[ mathMethod ]( cols );
35475 this.cols = Math.max( cols, 1 );
35476 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35478 // padding positioning..
35479 var totalColWidth = this.cols * this.columnWidth;
35480 var padavail = this.containerWidth - totalColWidth;
35481 // so for 2 columns - we need 3 'pads'
35483 var padNeeded = (1+this.cols) * this.padWidth;
35485 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35487 this.columnWidth += padExtra
35488 //this.padWidth = Math.floor(padavail / ( this.cols));
35490 // adjust colum width so that padding is fixed??
35492 // we have 3 columns ... total = width * 3
35493 // we have X left over... that should be used by
35495 //if (this.expandC) {
35503 getContainerWidth : function()
35505 /* // container is parent if fit width
35506 var container = this.isFitWidth ? this.element.parentNode : this.element;
35507 // check that this.size and size are there
35508 // IE8 triggers resize on body size change, so they might not be
35510 var size = getSize( container ); //FIXME
35511 this.containerWidth = size && size.innerWidth; //FIXME
35514 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35518 _getItemLayoutPosition : function( item ) // what is item?
35520 // we resize the item to our columnWidth..
35522 item.setWidth(this.columnWidth);
35523 item.autoBoxAdjust = false;
35525 var sz = item.getSize();
35527 // how many columns does this brick span
35528 var remainder = this.containerWidth % this.columnWidth;
35530 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35531 // round if off by 1 pixel, otherwise use ceil
35532 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35533 colSpan = Math.min( colSpan, this.cols );
35535 // normally this should be '1' as we dont' currently allow multi width columns..
35537 var colGroup = this._getColGroup( colSpan );
35538 // get the minimum Y value from the columns
35539 var minimumY = Math.min.apply( Math, colGroup );
35540 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35542 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35544 // position the brick
35546 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35547 y: this.currentSize.y + minimumY + this.padHeight
35551 // apply setHeight to necessary columns
35552 var setHeight = minimumY + sz.height + this.padHeight;
35553 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35555 var setSpan = this.cols + 1 - colGroup.length;
35556 for ( var i = 0; i < setSpan; i++ ) {
35557 this.colYs[ shortColIndex + i ] = setHeight ;
35564 * @param {Number} colSpan - number of columns the element spans
35565 * @returns {Array} colGroup
35567 _getColGroup : function( colSpan )
35569 if ( colSpan < 2 ) {
35570 // if brick spans only one column, use all the column Ys
35575 // how many different places could this brick fit horizontally
35576 var groupCount = this.cols + 1 - colSpan;
35577 // for each group potential horizontal position
35578 for ( var i = 0; i < groupCount; i++ ) {
35579 // make an array of colY values for that one group
35580 var groupColYs = this.colYs.slice( i, i + colSpan );
35581 // and get the max value of the array
35582 colGroup[i] = Math.max.apply( Math, groupColYs );
35587 _manageStamp : function( stamp )
35589 var stampSize = stamp.getSize();
35590 var offset = stamp.getBox();
35591 // get the columns that this stamp affects
35592 var firstX = this.isOriginLeft ? offset.x : offset.right;
35593 var lastX = firstX + stampSize.width;
35594 var firstCol = Math.floor( firstX / this.columnWidth );
35595 firstCol = Math.max( 0, firstCol );
35597 var lastCol = Math.floor( lastX / this.columnWidth );
35598 // lastCol should not go over if multiple of columnWidth #425
35599 lastCol -= lastX % this.columnWidth ? 0 : 1;
35600 lastCol = Math.min( this.cols - 1, lastCol );
35602 // set colYs to bottom of the stamp
35603 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35606 for ( var i = firstCol; i <= lastCol; i++ ) {
35607 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35612 _getContainerSize : function()
35614 this.maxY = Math.max.apply( Math, this.colYs );
35619 if ( this.isFitWidth ) {
35620 size.width = this._getContainerFitWidth();
35626 _getContainerFitWidth : function()
35628 var unusedCols = 0;
35629 // count unused columns
35632 if ( this.colYs[i] !== 0 ) {
35637 // fit container to columns that have been used
35638 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35641 needsResizeLayout : function()
35643 var previousWidth = this.containerWidth;
35644 this.getContainerWidth();
35645 return previousWidth !== this.containerWidth;
35660 * @class Roo.bootstrap.MasonryBrick
35661 * @extends Roo.bootstrap.Component
35662 * Bootstrap MasonryBrick class
35665 * Create a new MasonryBrick
35666 * @param {Object} config The config object
35669 Roo.bootstrap.MasonryBrick = function(config){
35671 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35673 Roo.bootstrap.MasonryBrick.register(this);
35679 * When a MasonryBrick is clcik
35680 * @param {Roo.bootstrap.MasonryBrick} this
35681 * @param {Roo.EventObject} e
35687 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35690 * @cfg {String} title
35694 * @cfg {String} html
35698 * @cfg {String} bgimage
35702 * @cfg {String} videourl
35706 * @cfg {String} cls
35710 * @cfg {String} href
35714 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35719 * @cfg {String} placetitle (center|bottom)
35724 * @cfg {Boolean} isFitContainer defalut true
35726 isFitContainer : true,
35729 * @cfg {Boolean} preventDefault defalut false
35731 preventDefault : false,
35734 * @cfg {Boolean} inverse defalut false
35736 maskInverse : false,
35738 getAutoCreate : function()
35740 if(!this.isFitContainer){
35741 return this.getSplitAutoCreate();
35744 var cls = 'masonry-brick masonry-brick-full';
35746 if(this.href.length){
35747 cls += ' masonry-brick-link';
35750 if(this.bgimage.length){
35751 cls += ' masonry-brick-image';
35754 if(this.maskInverse){
35755 cls += ' mask-inverse';
35758 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35759 cls += ' enable-mask';
35763 cls += ' masonry-' + this.size + '-brick';
35766 if(this.placetitle.length){
35768 switch (this.placetitle) {
35770 cls += ' masonry-center-title';
35773 cls += ' masonry-bottom-title';
35780 if(!this.html.length && !this.bgimage.length){
35781 cls += ' masonry-center-title';
35784 if(!this.html.length && this.bgimage.length){
35785 cls += ' masonry-bottom-title';
35790 cls += ' ' + this.cls;
35794 tag: (this.href.length) ? 'a' : 'div',
35799 cls: 'masonry-brick-mask'
35803 cls: 'masonry-brick-paragraph',
35809 if(this.href.length){
35810 cfg.href = this.href;
35813 var cn = cfg.cn[1].cn;
35815 if(this.title.length){
35818 cls: 'masonry-brick-title',
35823 if(this.html.length){
35826 cls: 'masonry-brick-text',
35831 if (!this.title.length && !this.html.length) {
35832 cfg.cn[1].cls += ' hide';
35835 if(this.bgimage.length){
35838 cls: 'masonry-brick-image-view',
35843 if(this.videourl.length){
35844 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35845 // youtube support only?
35848 cls: 'masonry-brick-image-view',
35851 allowfullscreen : true
35859 getSplitAutoCreate : function()
35861 var cls = 'masonry-brick masonry-brick-split';
35863 if(this.href.length){
35864 cls += ' masonry-brick-link';
35867 if(this.bgimage.length){
35868 cls += ' masonry-brick-image';
35872 cls += ' masonry-' + this.size + '-brick';
35875 switch (this.placetitle) {
35877 cls += ' masonry-center-title';
35880 cls += ' masonry-bottom-title';
35883 if(!this.bgimage.length){
35884 cls += ' masonry-center-title';
35887 if(this.bgimage.length){
35888 cls += ' masonry-bottom-title';
35894 cls += ' ' + this.cls;
35898 tag: (this.href.length) ? 'a' : 'div',
35903 cls: 'masonry-brick-split-head',
35907 cls: 'masonry-brick-paragraph',
35914 cls: 'masonry-brick-split-body',
35920 if(this.href.length){
35921 cfg.href = this.href;
35924 if(this.title.length){
35925 cfg.cn[0].cn[0].cn.push({
35927 cls: 'masonry-brick-title',
35932 if(this.html.length){
35933 cfg.cn[1].cn.push({
35935 cls: 'masonry-brick-text',
35940 if(this.bgimage.length){
35941 cfg.cn[0].cn.push({
35943 cls: 'masonry-brick-image-view',
35948 if(this.videourl.length){
35949 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35950 // youtube support only?
35951 cfg.cn[0].cn.cn.push({
35953 cls: 'masonry-brick-image-view',
35956 allowfullscreen : true
35963 initEvents: function()
35965 switch (this.size) {
35998 this.el.on('touchstart', this.onTouchStart, this);
35999 this.el.on('touchmove', this.onTouchMove, this);
36000 this.el.on('touchend', this.onTouchEnd, this);
36001 this.el.on('contextmenu', this.onContextMenu, this);
36003 this.el.on('mouseenter' ,this.enter, this);
36004 this.el.on('mouseleave', this.leave, this);
36005 this.el.on('click', this.onClick, this);
36008 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36009 this.parent().bricks.push(this);
36014 onClick: function(e, el)
36016 var time = this.endTimer - this.startTimer;
36017 // Roo.log(e.preventDefault());
36020 e.preventDefault();
36025 if(!this.preventDefault){
36029 e.preventDefault();
36031 if (this.activeClass != '') {
36032 this.selectBrick();
36035 this.fireEvent('click', this, e);
36038 enter: function(e, el)
36040 e.preventDefault();
36042 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36046 if(this.bgimage.length && this.html.length){
36047 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36051 leave: function(e, el)
36053 e.preventDefault();
36055 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36059 if(this.bgimage.length && this.html.length){
36060 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36064 onTouchStart: function(e, el)
36066 // e.preventDefault();
36068 this.touchmoved = false;
36070 if(!this.isFitContainer){
36074 if(!this.bgimage.length || !this.html.length){
36078 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36080 this.timer = new Date().getTime();
36084 onTouchMove: function(e, el)
36086 this.touchmoved = true;
36089 onContextMenu : function(e,el)
36091 e.preventDefault();
36092 e.stopPropagation();
36096 onTouchEnd: function(e, el)
36098 // e.preventDefault();
36100 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36107 if(!this.bgimage.length || !this.html.length){
36109 if(this.href.length){
36110 window.location.href = this.href;
36116 if(!this.isFitContainer){
36120 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36122 window.location.href = this.href;
36125 //selection on single brick only
36126 selectBrick : function() {
36128 if (!this.parentId) {
36132 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36133 var index = m.selectedBrick.indexOf(this.id);
36136 m.selectedBrick.splice(index,1);
36137 this.el.removeClass(this.activeClass);
36141 for(var i = 0; i < m.selectedBrick.length; i++) {
36142 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36143 b.el.removeClass(b.activeClass);
36146 m.selectedBrick = [];
36148 m.selectedBrick.push(this.id);
36149 this.el.addClass(this.activeClass);
36153 isSelected : function(){
36154 return this.el.hasClass(this.activeClass);
36159 Roo.apply(Roo.bootstrap.MasonryBrick, {
36162 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36164 * register a Masonry Brick
36165 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36168 register : function(brick)
36170 //this.groups[brick.id] = brick;
36171 this.groups.add(brick.id, brick);
36174 * fetch a masonry brick based on the masonry brick ID
36175 * @param {string} the masonry brick to add
36176 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36179 get: function(brick_id)
36181 // if (typeof(this.groups[brick_id]) == 'undefined') {
36184 // return this.groups[brick_id] ;
36186 if(this.groups.key(brick_id)) {
36187 return this.groups.key(brick_id);
36205 * @class Roo.bootstrap.Brick
36206 * @extends Roo.bootstrap.Component
36207 * Bootstrap Brick class
36210 * Create a new Brick
36211 * @param {Object} config The config object
36214 Roo.bootstrap.Brick = function(config){
36215 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36221 * When a Brick is click
36222 * @param {Roo.bootstrap.Brick} this
36223 * @param {Roo.EventObject} e
36229 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36232 * @cfg {String} title
36236 * @cfg {String} html
36240 * @cfg {String} bgimage
36244 * @cfg {String} cls
36248 * @cfg {String} href
36252 * @cfg {String} video
36256 * @cfg {Boolean} square
36260 getAutoCreate : function()
36262 var cls = 'roo-brick';
36264 if(this.href.length){
36265 cls += ' roo-brick-link';
36268 if(this.bgimage.length){
36269 cls += ' roo-brick-image';
36272 if(!this.html.length && !this.bgimage.length){
36273 cls += ' roo-brick-center-title';
36276 if(!this.html.length && this.bgimage.length){
36277 cls += ' roo-brick-bottom-title';
36281 cls += ' ' + this.cls;
36285 tag: (this.href.length) ? 'a' : 'div',
36290 cls: 'roo-brick-paragraph',
36296 if(this.href.length){
36297 cfg.href = this.href;
36300 var cn = cfg.cn[0].cn;
36302 if(this.title.length){
36305 cls: 'roo-brick-title',
36310 if(this.html.length){
36313 cls: 'roo-brick-text',
36320 if(this.bgimage.length){
36323 cls: 'roo-brick-image-view',
36331 initEvents: function()
36333 if(this.title.length || this.html.length){
36334 this.el.on('mouseenter' ,this.enter, this);
36335 this.el.on('mouseleave', this.leave, this);
36338 Roo.EventManager.onWindowResize(this.resize, this);
36340 if(this.bgimage.length){
36341 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36342 this.imageEl.on('load', this.onImageLoad, this);
36349 onImageLoad : function()
36354 resize : function()
36356 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36358 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36360 if(this.bgimage.length){
36361 var image = this.el.select('.roo-brick-image-view', true).first();
36363 image.setWidth(paragraph.getWidth());
36366 image.setHeight(paragraph.getWidth());
36369 this.el.setHeight(image.getHeight());
36370 paragraph.setHeight(image.getHeight());
36376 enter: function(e, el)
36378 e.preventDefault();
36380 if(this.bgimage.length){
36381 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36382 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36386 leave: function(e, el)
36388 e.preventDefault();
36390 if(this.bgimage.length){
36391 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36392 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36407 * @class Roo.bootstrap.NumberField
36408 * @extends Roo.bootstrap.Input
36409 * Bootstrap NumberField class
36415 * Create a new NumberField
36416 * @param {Object} config The config object
36419 Roo.bootstrap.NumberField = function(config){
36420 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36423 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36426 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36428 allowDecimals : true,
36430 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36432 decimalSeparator : ".",
36434 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36436 decimalPrecision : 2,
36438 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36440 allowNegative : true,
36443 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36447 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36449 minValue : Number.NEGATIVE_INFINITY,
36451 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36453 maxValue : Number.MAX_VALUE,
36455 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36457 minText : "The minimum value for this field is {0}",
36459 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36461 maxText : "The maximum value for this field is {0}",
36463 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36464 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36466 nanText : "{0} is not a valid number",
36468 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36470 thousandsDelimiter : false,
36472 * @cfg {String} valueAlign alignment of value
36474 valueAlign : "left",
36476 getAutoCreate : function()
36478 var hiddenInput = {
36482 cls: 'hidden-number-input'
36486 hiddenInput.name = this.name;
36491 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36493 this.name = hiddenInput.name;
36495 if(cfg.cn.length > 0) {
36496 cfg.cn.push(hiddenInput);
36503 initEvents : function()
36505 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36507 var allowed = "0123456789";
36509 if(this.allowDecimals){
36510 allowed += this.decimalSeparator;
36513 if(this.allowNegative){
36517 if(this.thousandsDelimiter) {
36521 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36523 var keyPress = function(e){
36525 var k = e.getKey();
36527 var c = e.getCharCode();
36530 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36531 allowed.indexOf(String.fromCharCode(c)) === -1
36537 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36541 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36546 this.el.on("keypress", keyPress, this);
36549 validateValue : function(value)
36552 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36556 var num = this.parseValue(value);
36559 this.markInvalid(String.format(this.nanText, value));
36563 if(num < this.minValue){
36564 this.markInvalid(String.format(this.minText, this.minValue));
36568 if(num > this.maxValue){
36569 this.markInvalid(String.format(this.maxText, this.maxValue));
36576 getValue : function()
36578 var v = this.hiddenEl().getValue();
36580 return this.fixPrecision(this.parseValue(v));
36583 parseValue : function(value)
36585 if(this.thousandsDelimiter) {
36587 r = new RegExp(",", "g");
36588 value = value.replace(r, "");
36591 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36592 return isNaN(value) ? '' : value;
36595 fixPrecision : function(value)
36597 if(this.thousandsDelimiter) {
36599 r = new RegExp(",", "g");
36600 value = value.replace(r, "");
36603 var nan = isNaN(value);
36605 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36606 return nan ? '' : value;
36608 return parseFloat(value).toFixed(this.decimalPrecision);
36611 setValue : function(v)
36613 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36619 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36621 this.inputEl().dom.value = (v == '') ? '' :
36622 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36624 if(!this.allowZero && v === '0') {
36625 this.hiddenEl().dom.value = '';
36626 this.inputEl().dom.value = '';
36633 decimalPrecisionFcn : function(v)
36635 return Math.floor(v);
36638 beforeBlur : function()
36640 var v = this.parseValue(this.getRawValue());
36642 if(v || v === 0 || v === ''){
36647 hiddenEl : function()
36649 return this.el.select('input.hidden-number-input',true).first();
36661 * @class Roo.bootstrap.DocumentSlider
36662 * @extends Roo.bootstrap.Component
36663 * Bootstrap DocumentSlider class
36666 * Create a new DocumentViewer
36667 * @param {Object} config The config object
36670 Roo.bootstrap.DocumentSlider = function(config){
36671 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36678 * Fire after initEvent
36679 * @param {Roo.bootstrap.DocumentSlider} this
36684 * Fire after update
36685 * @param {Roo.bootstrap.DocumentSlider} this
36691 * @param {Roo.bootstrap.DocumentSlider} this
36697 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36703 getAutoCreate : function()
36707 cls : 'roo-document-slider',
36711 cls : 'roo-document-slider-header',
36715 cls : 'roo-document-slider-header-title'
36721 cls : 'roo-document-slider-body',
36725 cls : 'roo-document-slider-prev',
36729 cls : 'fa fa-chevron-left'
36735 cls : 'roo-document-slider-thumb',
36739 cls : 'roo-document-slider-image'
36745 cls : 'roo-document-slider-next',
36749 cls : 'fa fa-chevron-right'
36761 initEvents : function()
36763 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36764 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36766 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36767 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36769 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36770 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36772 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36773 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36775 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36776 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36778 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36779 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36781 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36782 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36784 this.thumbEl.on('click', this.onClick, this);
36786 this.prevIndicator.on('click', this.prev, this);
36788 this.nextIndicator.on('click', this.next, this);
36792 initial : function()
36794 if(this.files.length){
36795 this.indicator = 1;
36799 this.fireEvent('initial', this);
36802 update : function()
36804 this.imageEl.attr('src', this.files[this.indicator - 1]);
36806 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36808 this.prevIndicator.show();
36810 if(this.indicator == 1){
36811 this.prevIndicator.hide();
36814 this.nextIndicator.show();
36816 if(this.indicator == this.files.length){
36817 this.nextIndicator.hide();
36820 this.thumbEl.scrollTo('top');
36822 this.fireEvent('update', this);
36825 onClick : function(e)
36827 e.preventDefault();
36829 this.fireEvent('click', this);
36834 e.preventDefault();
36836 this.indicator = Math.max(1, this.indicator - 1);
36843 e.preventDefault();
36845 this.indicator = Math.min(this.files.length, this.indicator + 1);
36859 * @class Roo.bootstrap.RadioSet
36860 * @extends Roo.bootstrap.Input
36861 * Bootstrap RadioSet class
36862 * @cfg {String} indicatorpos (left|right) default left
36863 * @cfg {Boolean} inline (true|false) inline the element (default true)
36864 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36866 * Create a new RadioSet
36867 * @param {Object} config The config object
36870 Roo.bootstrap.RadioSet = function(config){
36872 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36876 Roo.bootstrap.RadioSet.register(this);
36881 * Fires when the element is checked or unchecked.
36882 * @param {Roo.bootstrap.RadioSet} this This radio
36883 * @param {Roo.bootstrap.Radio} item The checked item
36888 * Fires when the element is click.
36889 * @param {Roo.bootstrap.RadioSet} this This radio set
36890 * @param {Roo.bootstrap.Radio} item The checked item
36891 * @param {Roo.EventObject} e The event object
36898 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36906 indicatorpos : 'left',
36908 getAutoCreate : function()
36912 cls : 'roo-radio-set-label',
36916 html : this.fieldLabel
36920 if (Roo.bootstrap.version == 3) {
36923 if(this.indicatorpos == 'left'){
36926 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36927 tooltip : 'This field is required'
36932 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36933 tooltip : 'This field is required'
36939 cls : 'roo-radio-set-items'
36942 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36944 if (align === 'left' && this.fieldLabel.length) {
36947 cls : "roo-radio-set-right",
36953 if(this.labelWidth > 12){
36954 label.style = "width: " + this.labelWidth + 'px';
36957 if(this.labelWidth < 13 && this.labelmd == 0){
36958 this.labelmd = this.labelWidth;
36961 if(this.labellg > 0){
36962 label.cls += ' col-lg-' + this.labellg;
36963 items.cls += ' col-lg-' + (12 - this.labellg);
36966 if(this.labelmd > 0){
36967 label.cls += ' col-md-' + this.labelmd;
36968 items.cls += ' col-md-' + (12 - this.labelmd);
36971 if(this.labelsm > 0){
36972 label.cls += ' col-sm-' + this.labelsm;
36973 items.cls += ' col-sm-' + (12 - this.labelsm);
36976 if(this.labelxs > 0){
36977 label.cls += ' col-xs-' + this.labelxs;
36978 items.cls += ' col-xs-' + (12 - this.labelxs);
36984 cls : 'roo-radio-set',
36988 cls : 'roo-radio-set-input',
36991 value : this.value ? this.value : ''
36998 if(this.weight.length){
36999 cfg.cls += ' roo-radio-' + this.weight;
37003 cfg.cls += ' roo-radio-set-inline';
37007 ['xs','sm','md','lg'].map(function(size){
37008 if (settings[size]) {
37009 cfg.cls += ' col-' + size + '-' + settings[size];
37017 initEvents : function()
37019 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37020 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37022 if(!this.fieldLabel.length){
37023 this.labelEl.hide();
37026 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37027 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37029 this.indicator = this.indicatorEl();
37031 if(this.indicator){
37032 this.indicator.addClass('invisible');
37035 this.originalValue = this.getValue();
37039 inputEl: function ()
37041 return this.el.select('.roo-radio-set-input', true).first();
37044 getChildContainer : function()
37046 return this.itemsEl;
37049 register : function(item)
37051 this.radioes.push(item);
37055 validate : function()
37057 if(this.getVisibilityEl().hasClass('hidden')){
37063 Roo.each(this.radioes, function(i){
37072 if(this.allowBlank) {
37076 if(this.disabled || valid){
37081 this.markInvalid();
37086 markValid : function()
37088 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37089 this.indicatorEl().removeClass('visible');
37090 this.indicatorEl().addClass('invisible');
37094 if (Roo.bootstrap.version == 3) {
37095 this.el.removeClass([this.invalidClass, this.validClass]);
37096 this.el.addClass(this.validClass);
37098 this.el.removeClass(['is-invalid','is-valid']);
37099 this.el.addClass(['is-valid']);
37101 this.fireEvent('valid', this);
37104 markInvalid : function(msg)
37106 if(this.allowBlank || this.disabled){
37110 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37111 this.indicatorEl().removeClass('invisible');
37112 this.indicatorEl().addClass('visible');
37114 if (Roo.bootstrap.version == 3) {
37115 this.el.removeClass([this.invalidClass, this.validClass]);
37116 this.el.addClass(this.invalidClass);
37118 this.el.removeClass(['is-invalid','is-valid']);
37119 this.el.addClass(['is-invalid']);
37122 this.fireEvent('invalid', this, msg);
37126 setValue : function(v, suppressEvent)
37128 if(this.value === v){
37135 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37138 Roo.each(this.radioes, function(i){
37140 i.el.removeClass('checked');
37143 Roo.each(this.radioes, function(i){
37145 if(i.value === v || i.value.toString() === v.toString()){
37147 i.el.addClass('checked');
37149 if(suppressEvent !== true){
37150 this.fireEvent('check', this, i);
37161 clearInvalid : function(){
37163 if(!this.el || this.preventMark){
37167 this.el.removeClass([this.invalidClass]);
37169 this.fireEvent('valid', this);
37174 Roo.apply(Roo.bootstrap.RadioSet, {
37178 register : function(set)
37180 this.groups[set.name] = set;
37183 get: function(name)
37185 if (typeof(this.groups[name]) == 'undefined') {
37189 return this.groups[name] ;
37195 * Ext JS Library 1.1.1
37196 * Copyright(c) 2006-2007, Ext JS, LLC.
37198 * Originally Released Under LGPL - original licence link has changed is not relivant.
37201 * <script type="text/javascript">
37206 * @class Roo.bootstrap.SplitBar
37207 * @extends Roo.util.Observable
37208 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37212 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37213 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37214 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37215 split.minSize = 100;
37216 split.maxSize = 600;
37217 split.animate = true;
37218 split.on('moved', splitterMoved);
37221 * Create a new SplitBar
37222 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37223 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37224 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37225 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37226 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37227 position of the SplitBar).
37229 Roo.bootstrap.SplitBar = function(cfg){
37234 // dragElement : elm
37235 // resizingElement: el,
37237 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37238 // placement : Roo.bootstrap.SplitBar.LEFT ,
37239 // existingProxy ???
37242 this.el = Roo.get(cfg.dragElement, true);
37243 this.el.dom.unselectable = "on";
37245 this.resizingEl = Roo.get(cfg.resizingElement, true);
37249 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37250 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37253 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37256 * The minimum size of the resizing element. (Defaults to 0)
37262 * The maximum size of the resizing element. (Defaults to 2000)
37265 this.maxSize = 2000;
37268 * Whether to animate the transition to the new size
37271 this.animate = false;
37274 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37277 this.useShim = false;
37282 if(!cfg.existingProxy){
37284 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37286 this.proxy = Roo.get(cfg.existingProxy).dom;
37289 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37292 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37295 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37298 this.dragSpecs = {};
37301 * @private The adapter to use to positon and resize elements
37303 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37304 this.adapter.init(this);
37306 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37308 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37309 this.el.addClass("roo-splitbar-h");
37312 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37313 this.el.addClass("roo-splitbar-v");
37319 * Fires when the splitter is moved (alias for {@link #event-moved})
37320 * @param {Roo.bootstrap.SplitBar} this
37321 * @param {Number} newSize the new width or height
37326 * Fires when the splitter is moved
37327 * @param {Roo.bootstrap.SplitBar} this
37328 * @param {Number} newSize the new width or height
37332 * @event beforeresize
37333 * Fires before the splitter is dragged
37334 * @param {Roo.bootstrap.SplitBar} this
37336 "beforeresize" : true,
37338 "beforeapply" : true
37341 Roo.util.Observable.call(this);
37344 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37345 onStartProxyDrag : function(x, y){
37346 this.fireEvent("beforeresize", this);
37348 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37350 o.enableDisplayMode("block");
37351 // all splitbars share the same overlay
37352 Roo.bootstrap.SplitBar.prototype.overlay = o;
37354 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37355 this.overlay.show();
37356 Roo.get(this.proxy).setDisplayed("block");
37357 var size = this.adapter.getElementSize(this);
37358 this.activeMinSize = this.getMinimumSize();;
37359 this.activeMaxSize = this.getMaximumSize();;
37360 var c1 = size - this.activeMinSize;
37361 var c2 = Math.max(this.activeMaxSize - size, 0);
37362 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37363 this.dd.resetConstraints();
37364 this.dd.setXConstraint(
37365 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37366 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37368 this.dd.setYConstraint(0, 0);
37370 this.dd.resetConstraints();
37371 this.dd.setXConstraint(0, 0);
37372 this.dd.setYConstraint(
37373 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37374 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37377 this.dragSpecs.startSize = size;
37378 this.dragSpecs.startPoint = [x, y];
37379 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37383 * @private Called after the drag operation by the DDProxy
37385 onEndProxyDrag : function(e){
37386 Roo.get(this.proxy).setDisplayed(false);
37387 var endPoint = Roo.lib.Event.getXY(e);
37389 this.overlay.hide();
37392 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37393 newSize = this.dragSpecs.startSize +
37394 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37395 endPoint[0] - this.dragSpecs.startPoint[0] :
37396 this.dragSpecs.startPoint[0] - endPoint[0]
37399 newSize = this.dragSpecs.startSize +
37400 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37401 endPoint[1] - this.dragSpecs.startPoint[1] :
37402 this.dragSpecs.startPoint[1] - endPoint[1]
37405 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37406 if(newSize != this.dragSpecs.startSize){
37407 if(this.fireEvent('beforeapply', this, newSize) !== false){
37408 this.adapter.setElementSize(this, newSize);
37409 this.fireEvent("moved", this, newSize);
37410 this.fireEvent("resize", this, newSize);
37416 * Get the adapter this SplitBar uses
37417 * @return The adapter object
37419 getAdapter : function(){
37420 return this.adapter;
37424 * Set the adapter this SplitBar uses
37425 * @param {Object} adapter A SplitBar adapter object
37427 setAdapter : function(adapter){
37428 this.adapter = adapter;
37429 this.adapter.init(this);
37433 * Gets the minimum size for the resizing element
37434 * @return {Number} The minimum size
37436 getMinimumSize : function(){
37437 return this.minSize;
37441 * Sets the minimum size for the resizing element
37442 * @param {Number} minSize The minimum size
37444 setMinimumSize : function(minSize){
37445 this.minSize = minSize;
37449 * Gets the maximum size for the resizing element
37450 * @return {Number} The maximum size
37452 getMaximumSize : function(){
37453 return this.maxSize;
37457 * Sets the maximum size for the resizing element
37458 * @param {Number} maxSize The maximum size
37460 setMaximumSize : function(maxSize){
37461 this.maxSize = maxSize;
37465 * Sets the initialize size for the resizing element
37466 * @param {Number} size The initial size
37468 setCurrentSize : function(size){
37469 var oldAnimate = this.animate;
37470 this.animate = false;
37471 this.adapter.setElementSize(this, size);
37472 this.animate = oldAnimate;
37476 * Destroy this splitbar.
37477 * @param {Boolean} removeEl True to remove the element
37479 destroy : function(removeEl){
37481 this.shim.remove();
37484 this.proxy.parentNode.removeChild(this.proxy);
37492 * @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.
37494 Roo.bootstrap.SplitBar.createProxy = function(dir){
37495 var proxy = new Roo.Element(document.createElement("div"));
37496 proxy.unselectable();
37497 var cls = 'roo-splitbar-proxy';
37498 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37499 document.body.appendChild(proxy.dom);
37504 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37505 * Default Adapter. It assumes the splitter and resizing element are not positioned
37506 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37508 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37511 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37512 // do nothing for now
37513 init : function(s){
37517 * Called before drag operations to get the current size of the resizing element.
37518 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37520 getElementSize : function(s){
37521 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37522 return s.resizingEl.getWidth();
37524 return s.resizingEl.getHeight();
37529 * Called after drag operations to set the size of the resizing element.
37530 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37531 * @param {Number} newSize The new size to set
37532 * @param {Function} onComplete A function to be invoked when resizing is complete
37534 setElementSize : function(s, newSize, onComplete){
37535 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37537 s.resizingEl.setWidth(newSize);
37539 onComplete(s, newSize);
37542 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37547 s.resizingEl.setHeight(newSize);
37549 onComplete(s, newSize);
37552 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37559 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37560 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37561 * Adapter that moves the splitter element to align with the resized sizing element.
37562 * Used with an absolute positioned SplitBar.
37563 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37564 * document.body, make sure you assign an id to the body element.
37566 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37567 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37568 this.container = Roo.get(container);
37571 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37572 init : function(s){
37573 this.basic.init(s);
37576 getElementSize : function(s){
37577 return this.basic.getElementSize(s);
37580 setElementSize : function(s, newSize, onComplete){
37581 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37584 moveSplitter : function(s){
37585 var yes = Roo.bootstrap.SplitBar;
37586 switch(s.placement){
37588 s.el.setX(s.resizingEl.getRight());
37591 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37594 s.el.setY(s.resizingEl.getBottom());
37597 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37604 * Orientation constant - Create a vertical SplitBar
37608 Roo.bootstrap.SplitBar.VERTICAL = 1;
37611 * Orientation constant - Create a horizontal SplitBar
37615 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37618 * Placement constant - The resizing element is to the left of the splitter element
37622 Roo.bootstrap.SplitBar.LEFT = 1;
37625 * Placement constant - The resizing element is to the right of the splitter element
37629 Roo.bootstrap.SplitBar.RIGHT = 2;
37632 * Placement constant - The resizing element is positioned above the splitter element
37636 Roo.bootstrap.SplitBar.TOP = 3;
37639 * Placement constant - The resizing element is positioned under splitter element
37643 Roo.bootstrap.SplitBar.BOTTOM = 4;
37644 Roo.namespace("Roo.bootstrap.layout");/*
37646 * Ext JS Library 1.1.1
37647 * Copyright(c) 2006-2007, Ext JS, LLC.
37649 * Originally Released Under LGPL - original licence link has changed is not relivant.
37652 * <script type="text/javascript">
37656 * @class Roo.bootstrap.layout.Manager
37657 * @extends Roo.bootstrap.Component
37658 * Base class for layout managers.
37660 Roo.bootstrap.layout.Manager = function(config)
37662 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37668 /** false to disable window resize monitoring @type Boolean */
37669 this.monitorWindowResize = true;
37674 * Fires when a layout is performed.
37675 * @param {Roo.LayoutManager} this
37679 * @event regionresized
37680 * Fires when the user resizes a region.
37681 * @param {Roo.LayoutRegion} region The resized region
37682 * @param {Number} newSize The new size (width for east/west, height for north/south)
37684 "regionresized" : true,
37686 * @event regioncollapsed
37687 * Fires when a region is collapsed.
37688 * @param {Roo.LayoutRegion} region The collapsed region
37690 "regioncollapsed" : true,
37692 * @event regionexpanded
37693 * Fires when a region is expanded.
37694 * @param {Roo.LayoutRegion} region The expanded region
37696 "regionexpanded" : true
37698 this.updating = false;
37701 this.el = Roo.get(config.el);
37707 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37712 monitorWindowResize : true,
37718 onRender : function(ct, position)
37721 this.el = Roo.get(ct);
37724 //this.fireEvent('render',this);
37728 initEvents: function()
37732 // ie scrollbar fix
37733 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37734 document.body.scroll = "no";
37735 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37736 this.el.position('relative');
37738 this.id = this.el.id;
37739 this.el.addClass("roo-layout-container");
37740 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37741 if(this.el.dom != document.body ) {
37742 this.el.on('resize', this.layout,this);
37743 this.el.on('show', this.layout,this);
37749 * Returns true if this layout is currently being updated
37750 * @return {Boolean}
37752 isUpdating : function(){
37753 return this.updating;
37757 * Suspend the LayoutManager from doing auto-layouts while
37758 * making multiple add or remove calls
37760 beginUpdate : function(){
37761 this.updating = true;
37765 * Restore auto-layouts and optionally disable the manager from performing a layout
37766 * @param {Boolean} noLayout true to disable a layout update
37768 endUpdate : function(noLayout){
37769 this.updating = false;
37775 layout: function(){
37779 onRegionResized : function(region, newSize){
37780 this.fireEvent("regionresized", region, newSize);
37784 onRegionCollapsed : function(region){
37785 this.fireEvent("regioncollapsed", region);
37788 onRegionExpanded : function(region){
37789 this.fireEvent("regionexpanded", region);
37793 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37794 * performs box-model adjustments.
37795 * @return {Object} The size as an object {width: (the width), height: (the height)}
37797 getViewSize : function()
37800 if(this.el.dom != document.body){
37801 size = this.el.getSize();
37803 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37805 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37806 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37811 * Returns the Element this layout is bound to.
37812 * @return {Roo.Element}
37814 getEl : function(){
37819 * Returns the specified region.
37820 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37821 * @return {Roo.LayoutRegion}
37823 getRegion : function(target){
37824 return this.regions[target.toLowerCase()];
37827 onWindowResize : function(){
37828 if(this.monitorWindowResize){
37835 * Ext JS Library 1.1.1
37836 * Copyright(c) 2006-2007, Ext JS, LLC.
37838 * Originally Released Under LGPL - original licence link has changed is not relivant.
37841 * <script type="text/javascript">
37844 * @class Roo.bootstrap.layout.Border
37845 * @extends Roo.bootstrap.layout.Manager
37846 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37847 * please see: examples/bootstrap/nested.html<br><br>
37849 <b>The container the layout is rendered into can be either the body element or any other element.
37850 If it is not the body element, the container needs to either be an absolute positioned element,
37851 or you will need to add "position:relative" to the css of the container. You will also need to specify
37852 the container size if it is not the body element.</b>
37855 * Create a new Border
37856 * @param {Object} config Configuration options
37858 Roo.bootstrap.layout.Border = function(config){
37859 config = config || {};
37860 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37864 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37865 if(config[region]){
37866 config[region].region = region;
37867 this.addRegion(config[region]);
37873 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37875 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37877 parent : false, // this might point to a 'nest' or a ???
37880 * Creates and adds a new region if it doesn't already exist.
37881 * @param {String} target The target region key (north, south, east, west or center).
37882 * @param {Object} config The regions config object
37883 * @return {BorderLayoutRegion} The new region
37885 addRegion : function(config)
37887 if(!this.regions[config.region]){
37888 var r = this.factory(config);
37889 this.bindRegion(r);
37891 return this.regions[config.region];
37895 bindRegion : function(r){
37896 this.regions[r.config.region] = r;
37898 r.on("visibilitychange", this.layout, this);
37899 r.on("paneladded", this.layout, this);
37900 r.on("panelremoved", this.layout, this);
37901 r.on("invalidated", this.layout, this);
37902 r.on("resized", this.onRegionResized, this);
37903 r.on("collapsed", this.onRegionCollapsed, this);
37904 r.on("expanded", this.onRegionExpanded, this);
37908 * Performs a layout update.
37910 layout : function()
37912 if(this.updating) {
37916 // render all the rebions if they have not been done alreayd?
37917 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37918 if(this.regions[region] && !this.regions[region].bodyEl){
37919 this.regions[region].onRender(this.el)
37923 var size = this.getViewSize();
37924 var w = size.width;
37925 var h = size.height;
37930 //var x = 0, y = 0;
37932 var rs = this.regions;
37933 var north = rs["north"];
37934 var south = rs["south"];
37935 var west = rs["west"];
37936 var east = rs["east"];
37937 var center = rs["center"];
37938 //if(this.hideOnLayout){ // not supported anymore
37939 //c.el.setStyle("display", "none");
37941 if(north && north.isVisible()){
37942 var b = north.getBox();
37943 var m = north.getMargins();
37944 b.width = w - (m.left+m.right);
37947 centerY = b.height + b.y + m.bottom;
37948 centerH -= centerY;
37949 north.updateBox(this.safeBox(b));
37951 if(south && south.isVisible()){
37952 var b = south.getBox();
37953 var m = south.getMargins();
37954 b.width = w - (m.left+m.right);
37956 var totalHeight = (b.height + m.top + m.bottom);
37957 b.y = h - totalHeight + m.top;
37958 centerH -= totalHeight;
37959 south.updateBox(this.safeBox(b));
37961 if(west && west.isVisible()){
37962 var b = west.getBox();
37963 var m = west.getMargins();
37964 b.height = centerH - (m.top+m.bottom);
37966 b.y = centerY + m.top;
37967 var totalWidth = (b.width + m.left + m.right);
37968 centerX += totalWidth;
37969 centerW -= totalWidth;
37970 west.updateBox(this.safeBox(b));
37972 if(east && east.isVisible()){
37973 var b = east.getBox();
37974 var m = east.getMargins();
37975 b.height = centerH - (m.top+m.bottom);
37976 var totalWidth = (b.width + m.left + m.right);
37977 b.x = w - totalWidth + m.left;
37978 b.y = centerY + m.top;
37979 centerW -= totalWidth;
37980 east.updateBox(this.safeBox(b));
37983 var m = center.getMargins();
37985 x: centerX + m.left,
37986 y: centerY + m.top,
37987 width: centerW - (m.left+m.right),
37988 height: centerH - (m.top+m.bottom)
37990 //if(this.hideOnLayout){
37991 //center.el.setStyle("display", "block");
37993 center.updateBox(this.safeBox(centerBox));
37996 this.fireEvent("layout", this);
38000 safeBox : function(box){
38001 box.width = Math.max(0, box.width);
38002 box.height = Math.max(0, box.height);
38007 * Adds a ContentPanel (or subclass) to this layout.
38008 * @param {String} target The target region key (north, south, east, west or center).
38009 * @param {Roo.ContentPanel} panel The panel to add
38010 * @return {Roo.ContentPanel} The added panel
38012 add : function(target, panel){
38014 target = target.toLowerCase();
38015 return this.regions[target].add(panel);
38019 * Remove a ContentPanel (or subclass) to this layout.
38020 * @param {String} target The target region key (north, south, east, west or center).
38021 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38022 * @return {Roo.ContentPanel} The removed panel
38024 remove : function(target, panel){
38025 target = target.toLowerCase();
38026 return this.regions[target].remove(panel);
38030 * Searches all regions for a panel with the specified id
38031 * @param {String} panelId
38032 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38034 findPanel : function(panelId){
38035 var rs = this.regions;
38036 for(var target in rs){
38037 if(typeof rs[target] != "function"){
38038 var p = rs[target].getPanel(panelId);
38048 * Searches all regions for a panel with the specified id and activates (shows) it.
38049 * @param {String/ContentPanel} panelId The panels id or the panel itself
38050 * @return {Roo.ContentPanel} The shown panel or null
38052 showPanel : function(panelId) {
38053 var rs = this.regions;
38054 for(var target in rs){
38055 var r = rs[target];
38056 if(typeof r != "function"){
38057 if(r.hasPanel(panelId)){
38058 return r.showPanel(panelId);
38066 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38067 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38070 restoreState : function(provider){
38072 provider = Roo.state.Manager;
38074 var sm = new Roo.LayoutStateManager();
38075 sm.init(this, provider);
38081 * Adds a xtype elements to the layout.
38085 xtype : 'ContentPanel',
38092 xtype : 'NestedLayoutPanel',
38098 items : [ ... list of content panels or nested layout panels.. ]
38102 * @param {Object} cfg Xtype definition of item to add.
38104 addxtype : function(cfg)
38106 // basically accepts a pannel...
38107 // can accept a layout region..!?!?
38108 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38111 // theory? children can only be panels??
38113 //if (!cfg.xtype.match(/Panel$/)) {
38118 if (typeof(cfg.region) == 'undefined') {
38119 Roo.log("Failed to add Panel, region was not set");
38123 var region = cfg.region;
38129 xitems = cfg.items;
38134 if ( region == 'center') {
38135 Roo.log("Center: " + cfg.title);
38141 case 'Content': // ContentPanel (el, cfg)
38142 case 'Scroll': // ContentPanel (el, cfg)
38144 cfg.autoCreate = cfg.autoCreate || true;
38145 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38147 // var el = this.el.createChild();
38148 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38151 this.add(region, ret);
38155 case 'TreePanel': // our new panel!
38156 cfg.el = this.el.createChild();
38157 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38158 this.add(region, ret);
38163 // create a new Layout (which is a Border Layout...
38165 var clayout = cfg.layout;
38166 clayout.el = this.el.createChild();
38167 clayout.items = clayout.items || [];
38171 // replace this exitems with the clayout ones..
38172 xitems = clayout.items;
38174 // force background off if it's in center...
38175 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38176 cfg.background = false;
38178 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38181 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38182 //console.log('adding nested layout panel ' + cfg.toSource());
38183 this.add(region, ret);
38184 nb = {}; /// find first...
38189 // needs grid and region
38191 //var el = this.getRegion(region).el.createChild();
38193 *var el = this.el.createChild();
38194 // create the grid first...
38195 cfg.grid.container = el;
38196 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38199 if (region == 'center' && this.active ) {
38200 cfg.background = false;
38203 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38205 this.add(region, ret);
38207 if (cfg.background) {
38208 // render grid on panel activation (if panel background)
38209 ret.on('activate', function(gp) {
38210 if (!gp.grid.rendered) {
38211 // gp.grid.render(el);
38215 // cfg.grid.render(el);
38221 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38222 // it was the old xcomponent building that caused this before.
38223 // espeically if border is the top element in the tree.
38233 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38235 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38236 this.add(region, ret);
38240 throw "Can not add '" + cfg.xtype + "' to Border";
38246 this.beginUpdate();
38250 Roo.each(xitems, function(i) {
38251 region = nb && i.region ? i.region : false;
38253 var add = ret.addxtype(i);
38256 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38257 if (!i.background) {
38258 abn[region] = nb[region] ;
38265 // make the last non-background panel active..
38266 //if (nb) { Roo.log(abn); }
38269 for(var r in abn) {
38270 region = this.getRegion(r);
38272 // tried using nb[r], but it does not work..
38274 region.showPanel(abn[r]);
38285 factory : function(cfg)
38288 var validRegions = Roo.bootstrap.layout.Border.regions;
38290 var target = cfg.region;
38293 var r = Roo.bootstrap.layout;
38297 return new r.North(cfg);
38299 return new r.South(cfg);
38301 return new r.East(cfg);
38303 return new r.West(cfg);
38305 return new r.Center(cfg);
38307 throw 'Layout region "'+target+'" not supported.';
38314 * Ext JS Library 1.1.1
38315 * Copyright(c) 2006-2007, Ext JS, LLC.
38317 * Originally Released Under LGPL - original licence link has changed is not relivant.
38320 * <script type="text/javascript">
38324 * @class Roo.bootstrap.layout.Basic
38325 * @extends Roo.util.Observable
38326 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38327 * and does not have a titlebar, tabs or any other features. All it does is size and position
38328 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38329 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38330 * @cfg {string} region the region that it inhabits..
38331 * @cfg {bool} skipConfig skip config?
38335 Roo.bootstrap.layout.Basic = function(config){
38337 this.mgr = config.mgr;
38339 this.position = config.region;
38341 var skipConfig = config.skipConfig;
38345 * @scope Roo.BasicLayoutRegion
38349 * @event beforeremove
38350 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38351 * @param {Roo.LayoutRegion} this
38352 * @param {Roo.ContentPanel} panel The panel
38353 * @param {Object} e The cancel event object
38355 "beforeremove" : true,
38357 * @event invalidated
38358 * Fires when the layout for this region is changed.
38359 * @param {Roo.LayoutRegion} this
38361 "invalidated" : true,
38363 * @event visibilitychange
38364 * Fires when this region is shown or hidden
38365 * @param {Roo.LayoutRegion} this
38366 * @param {Boolean} visibility true or false
38368 "visibilitychange" : true,
38370 * @event paneladded
38371 * Fires when a panel is added.
38372 * @param {Roo.LayoutRegion} this
38373 * @param {Roo.ContentPanel} panel The panel
38375 "paneladded" : true,
38377 * @event panelremoved
38378 * Fires when a panel is removed.
38379 * @param {Roo.LayoutRegion} this
38380 * @param {Roo.ContentPanel} panel The panel
38382 "panelremoved" : true,
38384 * @event beforecollapse
38385 * Fires when this region before collapse.
38386 * @param {Roo.LayoutRegion} this
38388 "beforecollapse" : true,
38391 * Fires when this region is collapsed.
38392 * @param {Roo.LayoutRegion} this
38394 "collapsed" : true,
38397 * Fires when this region is expanded.
38398 * @param {Roo.LayoutRegion} this
38403 * Fires when this region is slid into view.
38404 * @param {Roo.LayoutRegion} this
38406 "slideshow" : true,
38409 * Fires when this region slides out of view.
38410 * @param {Roo.LayoutRegion} this
38412 "slidehide" : true,
38414 * @event panelactivated
38415 * Fires when a panel is activated.
38416 * @param {Roo.LayoutRegion} this
38417 * @param {Roo.ContentPanel} panel The activated panel
38419 "panelactivated" : true,
38422 * Fires when the user resizes this region.
38423 * @param {Roo.LayoutRegion} this
38424 * @param {Number} newSize The new size (width for east/west, height for north/south)
38428 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38429 this.panels = new Roo.util.MixedCollection();
38430 this.panels.getKey = this.getPanelId.createDelegate(this);
38432 this.activePanel = null;
38433 // ensure listeners are added...
38435 if (config.listeners || config.events) {
38436 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38437 listeners : config.listeners || {},
38438 events : config.events || {}
38442 if(skipConfig !== true){
38443 this.applyConfig(config);
38447 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38449 getPanelId : function(p){
38453 applyConfig : function(config){
38454 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38455 this.config = config;
38460 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38461 * the width, for horizontal (north, south) the height.
38462 * @param {Number} newSize The new width or height
38464 resizeTo : function(newSize){
38465 var el = this.el ? this.el :
38466 (this.activePanel ? this.activePanel.getEl() : null);
38468 switch(this.position){
38471 el.setWidth(newSize);
38472 this.fireEvent("resized", this, newSize);
38476 el.setHeight(newSize);
38477 this.fireEvent("resized", this, newSize);
38483 getBox : function(){
38484 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38487 getMargins : function(){
38488 return this.margins;
38491 updateBox : function(box){
38493 var el = this.activePanel.getEl();
38494 el.dom.style.left = box.x + "px";
38495 el.dom.style.top = box.y + "px";
38496 this.activePanel.setSize(box.width, box.height);
38500 * Returns the container element for this region.
38501 * @return {Roo.Element}
38503 getEl : function(){
38504 return this.activePanel;
38508 * Returns true if this region is currently visible.
38509 * @return {Boolean}
38511 isVisible : function(){
38512 return this.activePanel ? true : false;
38515 setActivePanel : function(panel){
38516 panel = this.getPanel(panel);
38517 if(this.activePanel && this.activePanel != panel){
38518 this.activePanel.setActiveState(false);
38519 this.activePanel.getEl().setLeftTop(-10000,-10000);
38521 this.activePanel = panel;
38522 panel.setActiveState(true);
38524 panel.setSize(this.box.width, this.box.height);
38526 this.fireEvent("panelactivated", this, panel);
38527 this.fireEvent("invalidated");
38531 * Show the specified panel.
38532 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38533 * @return {Roo.ContentPanel} The shown panel or null
38535 showPanel : function(panel){
38536 panel = this.getPanel(panel);
38538 this.setActivePanel(panel);
38544 * Get the active panel for this region.
38545 * @return {Roo.ContentPanel} The active panel or null
38547 getActivePanel : function(){
38548 return this.activePanel;
38552 * Add the passed ContentPanel(s)
38553 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38554 * @return {Roo.ContentPanel} The panel added (if only one was added)
38556 add : function(panel){
38557 if(arguments.length > 1){
38558 for(var i = 0, len = arguments.length; i < len; i++) {
38559 this.add(arguments[i]);
38563 if(this.hasPanel(panel)){
38564 this.showPanel(panel);
38567 var el = panel.getEl();
38568 if(el.dom.parentNode != this.mgr.el.dom){
38569 this.mgr.el.dom.appendChild(el.dom);
38571 if(panel.setRegion){
38572 panel.setRegion(this);
38574 this.panels.add(panel);
38575 el.setStyle("position", "absolute");
38576 if(!panel.background){
38577 this.setActivePanel(panel);
38578 if(this.config.initialSize && this.panels.getCount()==1){
38579 this.resizeTo(this.config.initialSize);
38582 this.fireEvent("paneladded", this, panel);
38587 * Returns true if the panel is in this region.
38588 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38589 * @return {Boolean}
38591 hasPanel : function(panel){
38592 if(typeof panel == "object"){ // must be panel obj
38593 panel = panel.getId();
38595 return this.getPanel(panel) ? true : false;
38599 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38600 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38601 * @param {Boolean} preservePanel Overrides the config preservePanel option
38602 * @return {Roo.ContentPanel} The panel that was removed
38604 remove : function(panel, preservePanel){
38605 panel = this.getPanel(panel);
38610 this.fireEvent("beforeremove", this, panel, e);
38611 if(e.cancel === true){
38614 var panelId = panel.getId();
38615 this.panels.removeKey(panelId);
38620 * Returns the panel specified or null if it's not in this region.
38621 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38622 * @return {Roo.ContentPanel}
38624 getPanel : function(id){
38625 if(typeof id == "object"){ // must be panel obj
38628 return this.panels.get(id);
38632 * Returns this regions position (north/south/east/west/center).
38635 getPosition: function(){
38636 return this.position;
38640 * Ext JS Library 1.1.1
38641 * Copyright(c) 2006-2007, Ext JS, LLC.
38643 * Originally Released Under LGPL - original licence link has changed is not relivant.
38646 * <script type="text/javascript">
38650 * @class Roo.bootstrap.layout.Region
38651 * @extends Roo.bootstrap.layout.Basic
38652 * This class represents a region in a layout manager.
38654 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38655 * @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})
38656 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38657 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38658 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38659 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38660 * @cfg {String} title The title for the region (overrides panel titles)
38661 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38662 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38663 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38664 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38665 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38666 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38667 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38668 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38669 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38670 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38672 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38673 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38674 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38675 * @cfg {Number} width For East/West panels
38676 * @cfg {Number} height For North/South panels
38677 * @cfg {Boolean} split To show the splitter
38678 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38680 * @cfg {string} cls Extra CSS classes to add to region
38682 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38683 * @cfg {string} region the region that it inhabits..
38686 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38687 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38689 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38690 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38691 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38693 Roo.bootstrap.layout.Region = function(config)
38695 this.applyConfig(config);
38697 var mgr = config.mgr;
38698 var pos = config.region;
38699 config.skipConfig = true;
38700 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38703 this.onRender(mgr.el);
38706 this.visible = true;
38707 this.collapsed = false;
38708 this.unrendered_panels = [];
38711 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38713 position: '', // set by wrapper (eg. north/south etc..)
38714 unrendered_panels : null, // unrendered panels.
38716 tabPosition : false,
38718 mgr: false, // points to 'Border'
38721 createBody : function(){
38722 /** This region's body element
38723 * @type Roo.Element */
38724 this.bodyEl = this.el.createChild({
38726 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38730 onRender: function(ctr, pos)
38732 var dh = Roo.DomHelper;
38733 /** This region's container element
38734 * @type Roo.Element */
38735 this.el = dh.append(ctr.dom, {
38737 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38739 /** This region's title element
38740 * @type Roo.Element */
38742 this.titleEl = dh.append(this.el.dom, {
38744 unselectable: "on",
38745 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38747 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38748 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38752 this.titleEl.enableDisplayMode();
38753 /** This region's title text element
38754 * @type HTMLElement */
38755 this.titleTextEl = this.titleEl.dom.firstChild;
38756 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38758 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38759 this.closeBtn.enableDisplayMode();
38760 this.closeBtn.on("click", this.closeClicked, this);
38761 this.closeBtn.hide();
38763 this.createBody(this.config);
38764 if(this.config.hideWhenEmpty){
38766 this.on("paneladded", this.validateVisibility, this);
38767 this.on("panelremoved", this.validateVisibility, this);
38769 if(this.autoScroll){
38770 this.bodyEl.setStyle("overflow", "auto");
38772 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38774 //if(c.titlebar !== false){
38775 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38776 this.titleEl.hide();
38778 this.titleEl.show();
38779 if(this.config.title){
38780 this.titleTextEl.innerHTML = this.config.title;
38784 if(this.config.collapsed){
38785 this.collapse(true);
38787 if(this.config.hidden){
38791 if (this.unrendered_panels && this.unrendered_panels.length) {
38792 for (var i =0;i< this.unrendered_panels.length; i++) {
38793 this.add(this.unrendered_panels[i]);
38795 this.unrendered_panels = null;
38801 applyConfig : function(c)
38804 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38805 var dh = Roo.DomHelper;
38806 if(c.titlebar !== false){
38807 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38808 this.collapseBtn.on("click", this.collapse, this);
38809 this.collapseBtn.enableDisplayMode();
38811 if(c.showPin === true || this.showPin){
38812 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38813 this.stickBtn.enableDisplayMode();
38814 this.stickBtn.on("click", this.expand, this);
38815 this.stickBtn.hide();
38820 /** This region's collapsed element
38821 * @type Roo.Element */
38824 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38825 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38828 if(c.floatable !== false){
38829 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38830 this.collapsedEl.on("click", this.collapseClick, this);
38833 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38834 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38835 id: "message", unselectable: "on", style:{"float":"left"}});
38836 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38838 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38839 this.expandBtn.on("click", this.expand, this);
38843 if(this.collapseBtn){
38844 this.collapseBtn.setVisible(c.collapsible == true);
38847 this.cmargins = c.cmargins || this.cmargins ||
38848 (this.position == "west" || this.position == "east" ?
38849 {top: 0, left: 2, right:2, bottom: 0} :
38850 {top: 2, left: 0, right:0, bottom: 2});
38852 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38855 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38857 this.autoScroll = c.autoScroll || false;
38862 this.duration = c.duration || .30;
38863 this.slideDuration = c.slideDuration || .45;
38868 * Returns true if this region is currently visible.
38869 * @return {Boolean}
38871 isVisible : function(){
38872 return this.visible;
38876 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38877 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38879 //setCollapsedTitle : function(title){
38880 // title = title || " ";
38881 // if(this.collapsedTitleTextEl){
38882 // this.collapsedTitleTextEl.innerHTML = title;
38886 getBox : function(){
38888 // if(!this.collapsed){
38889 b = this.el.getBox(false, true);
38891 // b = this.collapsedEl.getBox(false, true);
38896 getMargins : function(){
38897 return this.margins;
38898 //return this.collapsed ? this.cmargins : this.margins;
38901 highlight : function(){
38902 this.el.addClass("x-layout-panel-dragover");
38905 unhighlight : function(){
38906 this.el.removeClass("x-layout-panel-dragover");
38909 updateBox : function(box)
38911 if (!this.bodyEl) {
38912 return; // not rendered yet..
38916 if(!this.collapsed){
38917 this.el.dom.style.left = box.x + "px";
38918 this.el.dom.style.top = box.y + "px";
38919 this.updateBody(box.width, box.height);
38921 this.collapsedEl.dom.style.left = box.x + "px";
38922 this.collapsedEl.dom.style.top = box.y + "px";
38923 this.collapsedEl.setSize(box.width, box.height);
38926 this.tabs.autoSizeTabs();
38930 updateBody : function(w, h)
38933 this.el.setWidth(w);
38934 w -= this.el.getBorderWidth("rl");
38935 if(this.config.adjustments){
38936 w += this.config.adjustments[0];
38939 if(h !== null && h > 0){
38940 this.el.setHeight(h);
38941 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38942 h -= this.el.getBorderWidth("tb");
38943 if(this.config.adjustments){
38944 h += this.config.adjustments[1];
38946 this.bodyEl.setHeight(h);
38948 h = this.tabs.syncHeight(h);
38951 if(this.panelSize){
38952 w = w !== null ? w : this.panelSize.width;
38953 h = h !== null ? h : this.panelSize.height;
38955 if(this.activePanel){
38956 var el = this.activePanel.getEl();
38957 w = w !== null ? w : el.getWidth();
38958 h = h !== null ? h : el.getHeight();
38959 this.panelSize = {width: w, height: h};
38960 this.activePanel.setSize(w, h);
38962 if(Roo.isIE && this.tabs){
38963 this.tabs.el.repaint();
38968 * Returns the container element for this region.
38969 * @return {Roo.Element}
38971 getEl : function(){
38976 * Hides this region.
38979 //if(!this.collapsed){
38980 this.el.dom.style.left = "-2000px";
38983 // this.collapsedEl.dom.style.left = "-2000px";
38984 // this.collapsedEl.hide();
38986 this.visible = false;
38987 this.fireEvent("visibilitychange", this, false);
38991 * Shows this region if it was previously hidden.
38994 //if(!this.collapsed){
38997 // this.collapsedEl.show();
38999 this.visible = true;
39000 this.fireEvent("visibilitychange", this, true);
39003 closeClicked : function(){
39004 if(this.activePanel){
39005 this.remove(this.activePanel);
39009 collapseClick : function(e){
39011 e.stopPropagation();
39014 e.stopPropagation();
39020 * Collapses this region.
39021 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39024 collapse : function(skipAnim, skipCheck = false){
39025 if(this.collapsed) {
39029 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39031 this.collapsed = true;
39033 this.split.el.hide();
39035 if(this.config.animate && skipAnim !== true){
39036 this.fireEvent("invalidated", this);
39037 this.animateCollapse();
39039 this.el.setLocation(-20000,-20000);
39041 this.collapsedEl.show();
39042 this.fireEvent("collapsed", this);
39043 this.fireEvent("invalidated", this);
39049 animateCollapse : function(){
39054 * Expands this region if it was previously collapsed.
39055 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39056 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39059 expand : function(e, skipAnim){
39061 e.stopPropagation();
39063 if(!this.collapsed || this.el.hasActiveFx()) {
39067 this.afterSlideIn();
39070 this.collapsed = false;
39071 if(this.config.animate && skipAnim !== true){
39072 this.animateExpand();
39076 this.split.el.show();
39078 this.collapsedEl.setLocation(-2000,-2000);
39079 this.collapsedEl.hide();
39080 this.fireEvent("invalidated", this);
39081 this.fireEvent("expanded", this);
39085 animateExpand : function(){
39089 initTabs : function()
39091 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39093 var ts = new Roo.bootstrap.panel.Tabs({
39094 el: this.bodyEl.dom,
39096 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39097 disableTooltips: this.config.disableTabTips,
39098 toolbar : this.config.toolbar
39101 if(this.config.hideTabs){
39102 ts.stripWrap.setDisplayed(false);
39105 ts.resizeTabs = this.config.resizeTabs === true;
39106 ts.minTabWidth = this.config.minTabWidth || 40;
39107 ts.maxTabWidth = this.config.maxTabWidth || 250;
39108 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39109 ts.monitorResize = false;
39110 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39111 ts.bodyEl.addClass('roo-layout-tabs-body');
39112 this.panels.each(this.initPanelAsTab, this);
39115 initPanelAsTab : function(panel){
39116 var ti = this.tabs.addTab(
39120 this.config.closeOnTab && panel.isClosable(),
39123 if(panel.tabTip !== undefined){
39124 ti.setTooltip(panel.tabTip);
39126 ti.on("activate", function(){
39127 this.setActivePanel(panel);
39130 if(this.config.closeOnTab){
39131 ti.on("beforeclose", function(t, e){
39133 this.remove(panel);
39137 panel.tabItem = ti;
39142 updatePanelTitle : function(panel, title)
39144 if(this.activePanel == panel){
39145 this.updateTitle(title);
39148 var ti = this.tabs.getTab(panel.getEl().id);
39150 if(panel.tabTip !== undefined){
39151 ti.setTooltip(panel.tabTip);
39156 updateTitle : function(title){
39157 if(this.titleTextEl && !this.config.title){
39158 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39162 setActivePanel : function(panel)
39164 panel = this.getPanel(panel);
39165 if(this.activePanel && this.activePanel != panel){
39166 if(this.activePanel.setActiveState(false) === false){
39170 this.activePanel = panel;
39171 panel.setActiveState(true);
39172 if(this.panelSize){
39173 panel.setSize(this.panelSize.width, this.panelSize.height);
39176 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39178 this.updateTitle(panel.getTitle());
39180 this.fireEvent("invalidated", this);
39182 this.fireEvent("panelactivated", this, panel);
39186 * Shows the specified panel.
39187 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39188 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39190 showPanel : function(panel)
39192 panel = this.getPanel(panel);
39195 var tab = this.tabs.getTab(panel.getEl().id);
39196 if(tab.isHidden()){
39197 this.tabs.unhideTab(tab.id);
39201 this.setActivePanel(panel);
39208 * Get the active panel for this region.
39209 * @return {Roo.ContentPanel} The active panel or null
39211 getActivePanel : function(){
39212 return this.activePanel;
39215 validateVisibility : function(){
39216 if(this.panels.getCount() < 1){
39217 this.updateTitle(" ");
39218 this.closeBtn.hide();
39221 if(!this.isVisible()){
39228 * Adds the passed ContentPanel(s) to this region.
39229 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39230 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39232 add : function(panel)
39234 if(arguments.length > 1){
39235 for(var i = 0, len = arguments.length; i < len; i++) {
39236 this.add(arguments[i]);
39241 // if we have not been rendered yet, then we can not really do much of this..
39242 if (!this.bodyEl) {
39243 this.unrendered_panels.push(panel);
39250 if(this.hasPanel(panel)){
39251 this.showPanel(panel);
39254 panel.setRegion(this);
39255 this.panels.add(panel);
39256 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39257 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39258 // and hide them... ???
39259 this.bodyEl.dom.appendChild(panel.getEl().dom);
39260 if(panel.background !== true){
39261 this.setActivePanel(panel);
39263 this.fireEvent("paneladded", this, panel);
39270 this.initPanelAsTab(panel);
39274 if(panel.background !== true){
39275 this.tabs.activate(panel.getEl().id);
39277 this.fireEvent("paneladded", this, panel);
39282 * Hides the tab for the specified panel.
39283 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39285 hidePanel : function(panel){
39286 if(this.tabs && (panel = this.getPanel(panel))){
39287 this.tabs.hideTab(panel.getEl().id);
39292 * Unhides the tab for a previously hidden panel.
39293 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39295 unhidePanel : function(panel){
39296 if(this.tabs && (panel = this.getPanel(panel))){
39297 this.tabs.unhideTab(panel.getEl().id);
39301 clearPanels : function(){
39302 while(this.panels.getCount() > 0){
39303 this.remove(this.panels.first());
39308 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39309 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39310 * @param {Boolean} preservePanel Overrides the config preservePanel option
39311 * @return {Roo.ContentPanel} The panel that was removed
39313 remove : function(panel, preservePanel)
39315 panel = this.getPanel(panel);
39320 this.fireEvent("beforeremove", this, panel, e);
39321 if(e.cancel === true){
39324 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39325 var panelId = panel.getId();
39326 this.panels.removeKey(panelId);
39328 document.body.appendChild(panel.getEl().dom);
39331 this.tabs.removeTab(panel.getEl().id);
39332 }else if (!preservePanel){
39333 this.bodyEl.dom.removeChild(panel.getEl().dom);
39335 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39336 var p = this.panels.first();
39337 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39338 tempEl.appendChild(p.getEl().dom);
39339 this.bodyEl.update("");
39340 this.bodyEl.dom.appendChild(p.getEl().dom);
39342 this.updateTitle(p.getTitle());
39344 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39345 this.setActivePanel(p);
39347 panel.setRegion(null);
39348 if(this.activePanel == panel){
39349 this.activePanel = null;
39351 if(this.config.autoDestroy !== false && preservePanel !== true){
39352 try{panel.destroy();}catch(e){}
39354 this.fireEvent("panelremoved", this, panel);
39359 * Returns the TabPanel component used by this region
39360 * @return {Roo.TabPanel}
39362 getTabs : function(){
39366 createTool : function(parentEl, className){
39367 var btn = Roo.DomHelper.append(parentEl, {
39369 cls: "x-layout-tools-button",
39372 cls: "roo-layout-tools-button-inner " + className,
39376 btn.addClassOnOver("roo-layout-tools-button-over");
39381 * Ext JS Library 1.1.1
39382 * Copyright(c) 2006-2007, Ext JS, LLC.
39384 * Originally Released Under LGPL - original licence link has changed is not relivant.
39387 * <script type="text/javascript">
39393 * @class Roo.SplitLayoutRegion
39394 * @extends Roo.LayoutRegion
39395 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39397 Roo.bootstrap.layout.Split = function(config){
39398 this.cursor = config.cursor;
39399 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39402 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39404 splitTip : "Drag to resize.",
39405 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39406 useSplitTips : false,
39408 applyConfig : function(config){
39409 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39412 onRender : function(ctr,pos) {
39414 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39415 if(!this.config.split){
39420 var splitEl = Roo.DomHelper.append(ctr.dom, {
39422 id: this.el.id + "-split",
39423 cls: "roo-layout-split roo-layout-split-"+this.position,
39426 /** The SplitBar for this region
39427 * @type Roo.SplitBar */
39428 // does not exist yet...
39429 Roo.log([this.position, this.orientation]);
39431 this.split = new Roo.bootstrap.SplitBar({
39432 dragElement : splitEl,
39433 resizingElement: this.el,
39434 orientation : this.orientation
39437 this.split.on("moved", this.onSplitMove, this);
39438 this.split.useShim = this.config.useShim === true;
39439 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39440 if(this.useSplitTips){
39441 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39443 //if(config.collapsible){
39444 // this.split.el.on("dblclick", this.collapse, this);
39447 if(typeof this.config.minSize != "undefined"){
39448 this.split.minSize = this.config.minSize;
39450 if(typeof this.config.maxSize != "undefined"){
39451 this.split.maxSize = this.config.maxSize;
39453 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39454 this.hideSplitter();
39459 getHMaxSize : function(){
39460 var cmax = this.config.maxSize || 10000;
39461 var center = this.mgr.getRegion("center");
39462 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39465 getVMaxSize : function(){
39466 var cmax = this.config.maxSize || 10000;
39467 var center = this.mgr.getRegion("center");
39468 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39471 onSplitMove : function(split, newSize){
39472 this.fireEvent("resized", this, newSize);
39476 * Returns the {@link Roo.SplitBar} for this region.
39477 * @return {Roo.SplitBar}
39479 getSplitBar : function(){
39484 this.hideSplitter();
39485 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39488 hideSplitter : function(){
39490 this.split.el.setLocation(-2000,-2000);
39491 this.split.el.hide();
39497 this.split.el.show();
39499 Roo.bootstrap.layout.Split.superclass.show.call(this);
39502 beforeSlide: function(){
39503 if(Roo.isGecko){// firefox overflow auto bug workaround
39504 this.bodyEl.clip();
39506 this.tabs.bodyEl.clip();
39508 if(this.activePanel){
39509 this.activePanel.getEl().clip();
39511 if(this.activePanel.beforeSlide){
39512 this.activePanel.beforeSlide();
39518 afterSlide : function(){
39519 if(Roo.isGecko){// firefox overflow auto bug workaround
39520 this.bodyEl.unclip();
39522 this.tabs.bodyEl.unclip();
39524 if(this.activePanel){
39525 this.activePanel.getEl().unclip();
39526 if(this.activePanel.afterSlide){
39527 this.activePanel.afterSlide();
39533 initAutoHide : function(){
39534 if(this.autoHide !== false){
39535 if(!this.autoHideHd){
39536 var st = new Roo.util.DelayedTask(this.slideIn, this);
39537 this.autoHideHd = {
39538 "mouseout": function(e){
39539 if(!e.within(this.el, true)){
39543 "mouseover" : function(e){
39549 this.el.on(this.autoHideHd);
39553 clearAutoHide : function(){
39554 if(this.autoHide !== false){
39555 this.el.un("mouseout", this.autoHideHd.mouseout);
39556 this.el.un("mouseover", this.autoHideHd.mouseover);
39560 clearMonitor : function(){
39561 Roo.get(document).un("click", this.slideInIf, this);
39564 // these names are backwards but not changed for compat
39565 slideOut : function(){
39566 if(this.isSlid || this.el.hasActiveFx()){
39569 this.isSlid = true;
39570 if(this.collapseBtn){
39571 this.collapseBtn.hide();
39573 this.closeBtnState = this.closeBtn.getStyle('display');
39574 this.closeBtn.hide();
39576 this.stickBtn.show();
39579 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39580 this.beforeSlide();
39581 this.el.setStyle("z-index", 10001);
39582 this.el.slideIn(this.getSlideAnchor(), {
39583 callback: function(){
39585 this.initAutoHide();
39586 Roo.get(document).on("click", this.slideInIf, this);
39587 this.fireEvent("slideshow", this);
39594 afterSlideIn : function(){
39595 this.clearAutoHide();
39596 this.isSlid = false;
39597 this.clearMonitor();
39598 this.el.setStyle("z-index", "");
39599 if(this.collapseBtn){
39600 this.collapseBtn.show();
39602 this.closeBtn.setStyle('display', this.closeBtnState);
39604 this.stickBtn.hide();
39606 this.fireEvent("slidehide", this);
39609 slideIn : function(cb){
39610 if(!this.isSlid || this.el.hasActiveFx()){
39614 this.isSlid = false;
39615 this.beforeSlide();
39616 this.el.slideOut(this.getSlideAnchor(), {
39617 callback: function(){
39618 this.el.setLeftTop(-10000, -10000);
39620 this.afterSlideIn();
39628 slideInIf : function(e){
39629 if(!e.within(this.el)){
39634 animateCollapse : function(){
39635 this.beforeSlide();
39636 this.el.setStyle("z-index", 20000);
39637 var anchor = this.getSlideAnchor();
39638 this.el.slideOut(anchor, {
39639 callback : function(){
39640 this.el.setStyle("z-index", "");
39641 this.collapsedEl.slideIn(anchor, {duration:.3});
39643 this.el.setLocation(-10000,-10000);
39645 this.fireEvent("collapsed", this);
39652 animateExpand : function(){
39653 this.beforeSlide();
39654 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39655 this.el.setStyle("z-index", 20000);
39656 this.collapsedEl.hide({
39659 this.el.slideIn(this.getSlideAnchor(), {
39660 callback : function(){
39661 this.el.setStyle("z-index", "");
39664 this.split.el.show();
39666 this.fireEvent("invalidated", this);
39667 this.fireEvent("expanded", this);
39695 getAnchor : function(){
39696 return this.anchors[this.position];
39699 getCollapseAnchor : function(){
39700 return this.canchors[this.position];
39703 getSlideAnchor : function(){
39704 return this.sanchors[this.position];
39707 getAlignAdj : function(){
39708 var cm = this.cmargins;
39709 switch(this.position){
39725 getExpandAdj : function(){
39726 var c = this.collapsedEl, cm = this.cmargins;
39727 switch(this.position){
39729 return [-(cm.right+c.getWidth()+cm.left), 0];
39732 return [cm.right+c.getWidth()+cm.left, 0];
39735 return [0, -(cm.top+cm.bottom+c.getHeight())];
39738 return [0, cm.top+cm.bottom+c.getHeight()];
39744 * Ext JS Library 1.1.1
39745 * Copyright(c) 2006-2007, Ext JS, LLC.
39747 * Originally Released Under LGPL - original licence link has changed is not relivant.
39750 * <script type="text/javascript">
39753 * These classes are private internal classes
39755 Roo.bootstrap.layout.Center = function(config){
39756 config.region = "center";
39757 Roo.bootstrap.layout.Region.call(this, config);
39758 this.visible = true;
39759 this.minWidth = config.minWidth || 20;
39760 this.minHeight = config.minHeight || 20;
39763 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39765 // center panel can't be hidden
39769 // center panel can't be hidden
39772 getMinWidth: function(){
39773 return this.minWidth;
39776 getMinHeight: function(){
39777 return this.minHeight;
39791 Roo.bootstrap.layout.North = function(config)
39793 config.region = 'north';
39794 config.cursor = 'n-resize';
39796 Roo.bootstrap.layout.Split.call(this, config);
39800 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39801 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39802 this.split.el.addClass("roo-layout-split-v");
39804 //var size = config.initialSize || config.height;
39805 //if(this.el && typeof size != "undefined"){
39806 // this.el.setHeight(size);
39809 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39811 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39814 onRender : function(ctr, pos)
39816 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39817 var size = this.config.initialSize || this.config.height;
39818 if(this.el && typeof size != "undefined"){
39819 this.el.setHeight(size);
39824 getBox : function(){
39825 if(this.collapsed){
39826 return this.collapsedEl.getBox();
39828 var box = this.el.getBox();
39830 box.height += this.split.el.getHeight();
39835 updateBox : function(box){
39836 if(this.split && !this.collapsed){
39837 box.height -= this.split.el.getHeight();
39838 this.split.el.setLeft(box.x);
39839 this.split.el.setTop(box.y+box.height);
39840 this.split.el.setWidth(box.width);
39842 if(this.collapsed){
39843 this.updateBody(box.width, null);
39845 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39853 Roo.bootstrap.layout.South = function(config){
39854 config.region = 'south';
39855 config.cursor = 's-resize';
39856 Roo.bootstrap.layout.Split.call(this, config);
39858 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39859 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39860 this.split.el.addClass("roo-layout-split-v");
39865 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39866 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39868 onRender : function(ctr, pos)
39870 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39871 var size = this.config.initialSize || this.config.height;
39872 if(this.el && typeof size != "undefined"){
39873 this.el.setHeight(size);
39878 getBox : function(){
39879 if(this.collapsed){
39880 return this.collapsedEl.getBox();
39882 var box = this.el.getBox();
39884 var sh = this.split.el.getHeight();
39891 updateBox : function(box){
39892 if(this.split && !this.collapsed){
39893 var sh = this.split.el.getHeight();
39896 this.split.el.setLeft(box.x);
39897 this.split.el.setTop(box.y-sh);
39898 this.split.el.setWidth(box.width);
39900 if(this.collapsed){
39901 this.updateBody(box.width, null);
39903 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39907 Roo.bootstrap.layout.East = function(config){
39908 config.region = "east";
39909 config.cursor = "e-resize";
39910 Roo.bootstrap.layout.Split.call(this, config);
39912 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39913 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39914 this.split.el.addClass("roo-layout-split-h");
39918 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39919 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39921 onRender : function(ctr, pos)
39923 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39924 var size = this.config.initialSize || this.config.width;
39925 if(this.el && typeof size != "undefined"){
39926 this.el.setWidth(size);
39931 getBox : function(){
39932 if(this.collapsed){
39933 return this.collapsedEl.getBox();
39935 var box = this.el.getBox();
39937 var sw = this.split.el.getWidth();
39944 updateBox : function(box){
39945 if(this.split && !this.collapsed){
39946 var sw = this.split.el.getWidth();
39948 this.split.el.setLeft(box.x);
39949 this.split.el.setTop(box.y);
39950 this.split.el.setHeight(box.height);
39953 if(this.collapsed){
39954 this.updateBody(null, box.height);
39956 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39960 Roo.bootstrap.layout.West = function(config){
39961 config.region = "west";
39962 config.cursor = "w-resize";
39964 Roo.bootstrap.layout.Split.call(this, config);
39966 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39967 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39968 this.split.el.addClass("roo-layout-split-h");
39972 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39973 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39975 onRender: function(ctr, pos)
39977 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39978 var size = this.config.initialSize || this.config.width;
39979 if(typeof size != "undefined"){
39980 this.el.setWidth(size);
39984 getBox : function(){
39985 if(this.collapsed){
39986 return this.collapsedEl.getBox();
39988 var box = this.el.getBox();
39989 if (box.width == 0) {
39990 box.width = this.config.width; // kludge?
39993 box.width += this.split.el.getWidth();
39998 updateBox : function(box){
39999 if(this.split && !this.collapsed){
40000 var sw = this.split.el.getWidth();
40002 this.split.el.setLeft(box.x+box.width);
40003 this.split.el.setTop(box.y);
40004 this.split.el.setHeight(box.height);
40006 if(this.collapsed){
40007 this.updateBody(null, box.height);
40009 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40011 });Roo.namespace("Roo.bootstrap.panel");/*
40013 * Ext JS Library 1.1.1
40014 * Copyright(c) 2006-2007, Ext JS, LLC.
40016 * Originally Released Under LGPL - original licence link has changed is not relivant.
40019 * <script type="text/javascript">
40022 * @class Roo.ContentPanel
40023 * @extends Roo.util.Observable
40024 * A basic ContentPanel element.
40025 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40026 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40027 * @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
40028 * @cfg {Boolean} closable True if the panel can be closed/removed
40029 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40030 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40031 * @cfg {Toolbar} toolbar A toolbar for this panel
40032 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40033 * @cfg {String} title The title for this panel
40034 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40035 * @cfg {String} url Calls {@link #setUrl} with this value
40036 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40037 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40038 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40039 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40040 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40041 * @cfg {Boolean} badges render the badges
40042 * @cfg {String} cls extra classes to use
40043 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40046 * Create a new ContentPanel.
40047 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40048 * @param {String/Object} config A string to set only the title or a config object
40049 * @param {String} content (optional) Set the HTML content for this panel
40050 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40052 Roo.bootstrap.panel.Content = function( config){
40054 this.tpl = config.tpl || false;
40056 var el = config.el;
40057 var content = config.content;
40059 if(config.autoCreate){ // xtype is available if this is called from factory
40062 this.el = Roo.get(el);
40063 if(!this.el && config && config.autoCreate){
40064 if(typeof config.autoCreate == "object"){
40065 if(!config.autoCreate.id){
40066 config.autoCreate.id = config.id||el;
40068 this.el = Roo.DomHelper.append(document.body,
40069 config.autoCreate, true);
40073 cls: (config.cls || '') +
40074 (config.background ? ' bg-' + config.background : '') +
40075 " roo-layout-inactive-content",
40078 if (config.iframe) {
40082 style : 'border: 0px',
40083 src : 'about:blank'
40089 elcfg.html = config.html;
40093 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40094 if (config.iframe) {
40095 this.iframeEl = this.el.select('iframe',true).first();
40100 this.closable = false;
40101 this.loaded = false;
40102 this.active = false;
40105 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40107 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40109 this.wrapEl = this.el; //this.el.wrap();
40111 if (config.toolbar.items) {
40112 ti = config.toolbar.items ;
40113 delete config.toolbar.items ;
40117 this.toolbar.render(this.wrapEl, 'before');
40118 for(var i =0;i < ti.length;i++) {
40119 // Roo.log(['add child', items[i]]);
40120 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40122 this.toolbar.items = nitems;
40123 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40124 delete config.toolbar;
40128 // xtype created footer. - not sure if will work as we normally have to render first..
40129 if (this.footer && !this.footer.el && this.footer.xtype) {
40130 if (!this.wrapEl) {
40131 this.wrapEl = this.el.wrap();
40134 this.footer.container = this.wrapEl.createChild();
40136 this.footer = Roo.factory(this.footer, Roo);
40141 if(typeof config == "string"){
40142 this.title = config;
40144 Roo.apply(this, config);
40148 this.resizeEl = Roo.get(this.resizeEl, true);
40150 this.resizeEl = this.el;
40152 // handle view.xtype
40160 * Fires when this panel is activated.
40161 * @param {Roo.ContentPanel} this
40165 * @event deactivate
40166 * Fires when this panel is activated.
40167 * @param {Roo.ContentPanel} this
40169 "deactivate" : true,
40173 * Fires when this panel is resized if fitToFrame is true.
40174 * @param {Roo.ContentPanel} this
40175 * @param {Number} width The width after any component adjustments
40176 * @param {Number} height The height after any component adjustments
40182 * Fires when this tab is created
40183 * @param {Roo.ContentPanel} this
40194 if(this.autoScroll && !this.iframe){
40195 this.resizeEl.setStyle("overflow", "auto");
40197 // fix randome scrolling
40198 //this.el.on('scroll', function() {
40199 // Roo.log('fix random scolling');
40200 // this.scrollTo('top',0);
40203 content = content || this.content;
40205 this.setContent(content);
40207 if(config && config.url){
40208 this.setUrl(this.url, this.params, this.loadOnce);
40213 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40215 if (this.view && typeof(this.view.xtype) != 'undefined') {
40216 this.view.el = this.el.appendChild(document.createElement("div"));
40217 this.view = Roo.factory(this.view);
40218 this.view.render && this.view.render(false, '');
40222 this.fireEvent('render', this);
40225 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40235 setRegion : function(region){
40236 this.region = region;
40237 this.setActiveClass(region && !this.background);
40241 setActiveClass: function(state)
40244 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40245 this.el.setStyle('position','relative');
40247 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40248 this.el.setStyle('position', 'absolute');
40253 * Returns the toolbar for this Panel if one was configured.
40254 * @return {Roo.Toolbar}
40256 getToolbar : function(){
40257 return this.toolbar;
40260 setActiveState : function(active)
40262 this.active = active;
40263 this.setActiveClass(active);
40265 if(this.fireEvent("deactivate", this) === false){
40270 this.fireEvent("activate", this);
40274 * Updates this panel's element (not for iframe)
40275 * @param {String} content The new content
40276 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40278 setContent : function(content, loadScripts){
40283 this.el.update(content, loadScripts);
40286 ignoreResize : function(w, h){
40287 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40290 this.lastSize = {width: w, height: h};
40295 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40296 * @return {Roo.UpdateManager} The UpdateManager
40298 getUpdateManager : function(){
40302 return this.el.getUpdateManager();
40305 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40306 * Does not work with IFRAME contents
40307 * @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:
40310 url: "your-url.php",
40311 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40312 callback: yourFunction,
40313 scope: yourObject, //(optional scope)
40316 text: "Loading...",
40322 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40323 * 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.
40324 * @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}
40325 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40326 * @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.
40327 * @return {Roo.ContentPanel} this
40335 var um = this.el.getUpdateManager();
40336 um.update.apply(um, arguments);
40342 * 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.
40343 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40344 * @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)
40345 * @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)
40346 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40348 setUrl : function(url, params, loadOnce){
40350 this.iframeEl.dom.src = url;
40354 if(this.refreshDelegate){
40355 this.removeListener("activate", this.refreshDelegate);
40357 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40358 this.on("activate", this.refreshDelegate);
40359 return this.el.getUpdateManager();
40362 _handleRefresh : function(url, params, loadOnce){
40363 if(!loadOnce || !this.loaded){
40364 var updater = this.el.getUpdateManager();
40365 updater.update(url, params, this._setLoaded.createDelegate(this));
40369 _setLoaded : function(){
40370 this.loaded = true;
40374 * Returns this panel's id
40377 getId : function(){
40382 * Returns this panel's element - used by regiosn to add.
40383 * @return {Roo.Element}
40385 getEl : function(){
40386 return this.wrapEl || this.el;
40391 adjustForComponents : function(width, height)
40393 //Roo.log('adjustForComponents ');
40394 if(this.resizeEl != this.el){
40395 width -= this.el.getFrameWidth('lr');
40396 height -= this.el.getFrameWidth('tb');
40399 var te = this.toolbar.getEl();
40400 te.setWidth(width);
40401 height -= te.getHeight();
40404 var te = this.footer.getEl();
40405 te.setWidth(width);
40406 height -= te.getHeight();
40410 if(this.adjustments){
40411 width += this.adjustments[0];
40412 height += this.adjustments[1];
40414 return {"width": width, "height": height};
40417 setSize : function(width, height){
40418 if(this.fitToFrame && !this.ignoreResize(width, height)){
40419 if(this.fitContainer && this.resizeEl != this.el){
40420 this.el.setSize(width, height);
40422 var size = this.adjustForComponents(width, height);
40424 this.iframeEl.setSize(width,height);
40427 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40428 this.fireEvent('resize', this, size.width, size.height);
40435 * Returns this panel's title
40438 getTitle : function(){
40440 if (typeof(this.title) != 'object') {
40445 for (var k in this.title) {
40446 if (!this.title.hasOwnProperty(k)) {
40450 if (k.indexOf('-') >= 0) {
40451 var s = k.split('-');
40452 for (var i = 0; i<s.length; i++) {
40453 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40456 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40463 * Set this panel's title
40464 * @param {String} title
40466 setTitle : function(title){
40467 this.title = title;
40469 this.region.updatePanelTitle(this, title);
40474 * Returns true is this panel was configured to be closable
40475 * @return {Boolean}
40477 isClosable : function(){
40478 return this.closable;
40481 beforeSlide : function(){
40483 this.resizeEl.clip();
40486 afterSlide : function(){
40488 this.resizeEl.unclip();
40492 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40493 * Will fail silently if the {@link #setUrl} method has not been called.
40494 * This does not activate the panel, just updates its content.
40496 refresh : function(){
40497 if(this.refreshDelegate){
40498 this.loaded = false;
40499 this.refreshDelegate();
40504 * Destroys this panel
40506 destroy : function(){
40507 this.el.removeAllListeners();
40508 var tempEl = document.createElement("span");
40509 tempEl.appendChild(this.el.dom);
40510 tempEl.innerHTML = "";
40516 * form - if the content panel contains a form - this is a reference to it.
40517 * @type {Roo.form.Form}
40521 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40522 * This contains a reference to it.
40528 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40538 * @param {Object} cfg Xtype definition of item to add.
40542 getChildContainer: function () {
40543 return this.getEl();
40548 var ret = new Roo.factory(cfg);
40553 if (cfg.xtype.match(/^Form$/)) {
40556 //if (this.footer) {
40557 // el = this.footer.container.insertSibling(false, 'before');
40559 el = this.el.createChild();
40562 this.form = new Roo.form.Form(cfg);
40565 if ( this.form.allItems.length) {
40566 this.form.render(el.dom);
40570 // should only have one of theses..
40571 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40572 // views.. should not be just added - used named prop 'view''
40574 cfg.el = this.el.appendChild(document.createElement("div"));
40577 var ret = new Roo.factory(cfg);
40579 ret.render && ret.render(false, ''); // render blank..
40589 * @class Roo.bootstrap.panel.Grid
40590 * @extends Roo.bootstrap.panel.Content
40592 * Create a new GridPanel.
40593 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40594 * @param {Object} config A the config object
40600 Roo.bootstrap.panel.Grid = function(config)
40604 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40605 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40607 config.el = this.wrapper;
40608 //this.el = this.wrapper;
40610 if (config.container) {
40611 // ctor'ed from a Border/panel.grid
40614 this.wrapper.setStyle("overflow", "hidden");
40615 this.wrapper.addClass('roo-grid-container');
40620 if(config.toolbar){
40621 var tool_el = this.wrapper.createChild();
40622 this.toolbar = Roo.factory(config.toolbar);
40624 if (config.toolbar.items) {
40625 ti = config.toolbar.items ;
40626 delete config.toolbar.items ;
40630 this.toolbar.render(tool_el);
40631 for(var i =0;i < ti.length;i++) {
40632 // Roo.log(['add child', items[i]]);
40633 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40635 this.toolbar.items = nitems;
40637 delete config.toolbar;
40640 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40641 config.grid.scrollBody = true;;
40642 config.grid.monitorWindowResize = false; // turn off autosizing
40643 config.grid.autoHeight = false;
40644 config.grid.autoWidth = false;
40646 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40648 if (config.background) {
40649 // render grid on panel activation (if panel background)
40650 this.on('activate', function(gp) {
40651 if (!gp.grid.rendered) {
40652 gp.grid.render(this.wrapper);
40653 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40658 this.grid.render(this.wrapper);
40659 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40662 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40663 // ??? needed ??? config.el = this.wrapper;
40668 // xtype created footer. - not sure if will work as we normally have to render first..
40669 if (this.footer && !this.footer.el && this.footer.xtype) {
40671 var ctr = this.grid.getView().getFooterPanel(true);
40672 this.footer.dataSource = this.grid.dataSource;
40673 this.footer = Roo.factory(this.footer, Roo);
40674 this.footer.render(ctr);
40684 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40685 getId : function(){
40686 return this.grid.id;
40690 * Returns the grid for this panel
40691 * @return {Roo.bootstrap.Table}
40693 getGrid : function(){
40697 setSize : function(width, height){
40698 if(!this.ignoreResize(width, height)){
40699 var grid = this.grid;
40700 var size = this.adjustForComponents(width, height);
40701 // tfoot is not a footer?
40704 var gridel = grid.getGridEl();
40705 gridel.setSize(size.width, size.height);
40707 var tbd = grid.getGridEl().select('tbody', true).first();
40708 var thd = grid.getGridEl().select('thead',true).first();
40709 var tbf= grid.getGridEl().select('tfoot', true).first();
40712 size.height -= tbf.getHeight();
40715 size.height -= thd.getHeight();
40718 tbd.setSize(size.width, size.height );
40719 // this is for the account management tab -seems to work there.
40720 var thd = grid.getGridEl().select('thead',true).first();
40722 // tbd.setSize(size.width, size.height - thd.getHeight());
40731 beforeSlide : function(){
40732 this.grid.getView().scroller.clip();
40735 afterSlide : function(){
40736 this.grid.getView().scroller.unclip();
40739 destroy : function(){
40740 this.grid.destroy();
40742 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40747 * @class Roo.bootstrap.panel.Nest
40748 * @extends Roo.bootstrap.panel.Content
40750 * Create a new Panel, that can contain a layout.Border.
40753 * @param {Roo.BorderLayout} layout The layout for this panel
40754 * @param {String/Object} config A string to set only the title or a config object
40756 Roo.bootstrap.panel.Nest = function(config)
40758 // construct with only one argument..
40759 /* FIXME - implement nicer consturctors
40760 if (layout.layout) {
40762 layout = config.layout;
40763 delete config.layout;
40765 if (layout.xtype && !layout.getEl) {
40766 // then layout needs constructing..
40767 layout = Roo.factory(layout, Roo);
40771 config.el = config.layout.getEl();
40773 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40775 config.layout.monitorWindowResize = false; // turn off autosizing
40776 this.layout = config.layout;
40777 this.layout.getEl().addClass("roo-layout-nested-layout");
40778 this.layout.parent = this;
40785 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40787 setSize : function(width, height){
40788 if(!this.ignoreResize(width, height)){
40789 var size = this.adjustForComponents(width, height);
40790 var el = this.layout.getEl();
40791 if (size.height < 1) {
40792 el.setWidth(size.width);
40794 el.setSize(size.width, size.height);
40796 var touch = el.dom.offsetWidth;
40797 this.layout.layout();
40798 // ie requires a double layout on the first pass
40799 if(Roo.isIE && !this.initialized){
40800 this.initialized = true;
40801 this.layout.layout();
40806 // activate all subpanels if not currently active..
40808 setActiveState : function(active){
40809 this.active = active;
40810 this.setActiveClass(active);
40813 this.fireEvent("deactivate", this);
40817 this.fireEvent("activate", this);
40818 // not sure if this should happen before or after..
40819 if (!this.layout) {
40820 return; // should not happen..
40823 for (var r in this.layout.regions) {
40824 reg = this.layout.getRegion(r);
40825 if (reg.getActivePanel()) {
40826 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40827 reg.setActivePanel(reg.getActivePanel());
40830 if (!reg.panels.length) {
40833 reg.showPanel(reg.getPanel(0));
40842 * Returns the nested BorderLayout for this panel
40843 * @return {Roo.BorderLayout}
40845 getLayout : function(){
40846 return this.layout;
40850 * Adds a xtype elements to the layout of the nested panel
40854 xtype : 'ContentPanel',
40861 xtype : 'NestedLayoutPanel',
40867 items : [ ... list of content panels or nested layout panels.. ]
40871 * @param {Object} cfg Xtype definition of item to add.
40873 addxtype : function(cfg) {
40874 return this.layout.addxtype(cfg);
40879 * Ext JS Library 1.1.1
40880 * Copyright(c) 2006-2007, Ext JS, LLC.
40882 * Originally Released Under LGPL - original licence link has changed is not relivant.
40885 * <script type="text/javascript">
40888 * @class Roo.TabPanel
40889 * @extends Roo.util.Observable
40890 * A lightweight tab container.
40894 // basic tabs 1, built from existing content
40895 var tabs = new Roo.TabPanel("tabs1");
40896 tabs.addTab("script", "View Script");
40897 tabs.addTab("markup", "View Markup");
40898 tabs.activate("script");
40900 // more advanced tabs, built from javascript
40901 var jtabs = new Roo.TabPanel("jtabs");
40902 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40904 // set up the UpdateManager
40905 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40906 var updater = tab2.getUpdateManager();
40907 updater.setDefaultUrl("ajax1.htm");
40908 tab2.on('activate', updater.refresh, updater, true);
40910 // Use setUrl for Ajax loading
40911 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40912 tab3.setUrl("ajax2.htm", null, true);
40915 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40918 jtabs.activate("jtabs-1");
40921 * Create a new TabPanel.
40922 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40923 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40925 Roo.bootstrap.panel.Tabs = function(config){
40927 * The container element for this TabPanel.
40928 * @type Roo.Element
40930 this.el = Roo.get(config.el);
40933 if(typeof config == "boolean"){
40934 this.tabPosition = config ? "bottom" : "top";
40936 Roo.apply(this, config);
40940 if(this.tabPosition == "bottom"){
40941 // if tabs are at the bottom = create the body first.
40942 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40943 this.el.addClass("roo-tabs-bottom");
40945 // next create the tabs holders
40947 if (this.tabPosition == "west"){
40949 var reg = this.region; // fake it..
40951 if (!reg.mgr.parent) {
40954 reg = reg.mgr.parent.region;
40956 Roo.log("got nest?");
40958 if (reg.mgr.getRegion('west')) {
40959 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40960 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40961 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40962 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40963 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40971 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40972 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40973 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40974 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40979 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40982 // finally - if tabs are at the top, then create the body last..
40983 if(this.tabPosition != "bottom"){
40984 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40985 * @type Roo.Element
40987 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40988 this.el.addClass("roo-tabs-top");
40992 this.bodyEl.setStyle("position", "relative");
40994 this.active = null;
40995 this.activateDelegate = this.activate.createDelegate(this);
41000 * Fires when the active tab changes
41001 * @param {Roo.TabPanel} this
41002 * @param {Roo.TabPanelItem} activePanel The new active tab
41006 * @event beforetabchange
41007 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41008 * @param {Roo.TabPanel} this
41009 * @param {Object} e Set cancel to true on this object to cancel the tab change
41010 * @param {Roo.TabPanelItem} tab The tab being changed to
41012 "beforetabchange" : true
41015 Roo.EventManager.onWindowResize(this.onResize, this);
41016 this.cpad = this.el.getPadding("lr");
41017 this.hiddenCount = 0;
41020 // toolbar on the tabbar support...
41021 if (this.toolbar) {
41022 alert("no toolbar support yet");
41023 this.toolbar = false;
41025 var tcfg = this.toolbar;
41026 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41027 this.toolbar = new Roo.Toolbar(tcfg);
41028 if (Roo.isSafari) {
41029 var tbl = tcfg.container.child('table', true);
41030 tbl.setAttribute('width', '100%');
41038 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41041 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41043 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41045 tabPosition : "top",
41047 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41049 currentTabWidth : 0,
41051 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41055 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41059 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41061 preferredTabWidth : 175,
41063 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41065 resizeTabs : false,
41067 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41069 monitorResize : true,
41071 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41073 toolbar : false, // set by caller..
41075 region : false, /// set by caller
41077 disableTooltips : true, // not used yet...
41080 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41081 * @param {String} id The id of the div to use <b>or create</b>
41082 * @param {String} text The text for the tab
41083 * @param {String} content (optional) Content to put in the TabPanelItem body
41084 * @param {Boolean} closable (optional) True to create a close icon on the tab
41085 * @return {Roo.TabPanelItem} The created TabPanelItem
41087 addTab : function(id, text, content, closable, tpl)
41089 var item = new Roo.bootstrap.panel.TabItem({
41093 closable : closable,
41096 this.addTabItem(item);
41098 item.setContent(content);
41104 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41105 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41106 * @return {Roo.TabPanelItem}
41108 getTab : function(id){
41109 return this.items[id];
41113 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41114 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41116 hideTab : function(id){
41117 var t = this.items[id];
41120 this.hiddenCount++;
41121 this.autoSizeTabs();
41126 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41127 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41129 unhideTab : function(id){
41130 var t = this.items[id];
41132 t.setHidden(false);
41133 this.hiddenCount--;
41134 this.autoSizeTabs();
41139 * Adds an existing {@link Roo.TabPanelItem}.
41140 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41142 addTabItem : function(item)
41144 this.items[item.id] = item;
41145 this.items.push(item);
41146 this.autoSizeTabs();
41147 // if(this.resizeTabs){
41148 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41149 // this.autoSizeTabs();
41151 // item.autoSize();
41156 * Removes a {@link Roo.TabPanelItem}.
41157 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41159 removeTab : function(id){
41160 var items = this.items;
41161 var tab = items[id];
41162 if(!tab) { return; }
41163 var index = items.indexOf(tab);
41164 if(this.active == tab && items.length > 1){
41165 var newTab = this.getNextAvailable(index);
41170 this.stripEl.dom.removeChild(tab.pnode.dom);
41171 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41172 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41174 items.splice(index, 1);
41175 delete this.items[tab.id];
41176 tab.fireEvent("close", tab);
41177 tab.purgeListeners();
41178 this.autoSizeTabs();
41181 getNextAvailable : function(start){
41182 var items = this.items;
41184 // look for a next tab that will slide over to
41185 // replace the one being removed
41186 while(index < items.length){
41187 var item = items[++index];
41188 if(item && !item.isHidden()){
41192 // if one isn't found select the previous tab (on the left)
41195 var item = items[--index];
41196 if(item && !item.isHidden()){
41204 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41205 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41207 disableTab : function(id){
41208 var tab = this.items[id];
41209 if(tab && this.active != tab){
41215 * Enables a {@link Roo.TabPanelItem} that is disabled.
41216 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41218 enableTab : function(id){
41219 var tab = this.items[id];
41224 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41225 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41226 * @return {Roo.TabPanelItem} The TabPanelItem.
41228 activate : function(id)
41230 //Roo.log('activite:' + id);
41232 var tab = this.items[id];
41236 if(tab == this.active || tab.disabled){
41240 this.fireEvent("beforetabchange", this, e, tab);
41241 if(e.cancel !== true && !tab.disabled){
41243 this.active.hide();
41245 this.active = this.items[id];
41246 this.active.show();
41247 this.fireEvent("tabchange", this, this.active);
41253 * Gets the active {@link Roo.TabPanelItem}.
41254 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41256 getActiveTab : function(){
41257 return this.active;
41261 * Updates the tab body element to fit the height of the container element
41262 * for overflow scrolling
41263 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41265 syncHeight : function(targetHeight){
41266 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41267 var bm = this.bodyEl.getMargins();
41268 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41269 this.bodyEl.setHeight(newHeight);
41273 onResize : function(){
41274 if(this.monitorResize){
41275 this.autoSizeTabs();
41280 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41282 beginUpdate : function(){
41283 this.updating = true;
41287 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41289 endUpdate : function(){
41290 this.updating = false;
41291 this.autoSizeTabs();
41295 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41297 autoSizeTabs : function()
41299 var count = this.items.length;
41300 var vcount = count - this.hiddenCount;
41303 this.stripEl.hide();
41305 this.stripEl.show();
41308 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41313 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41314 var availWidth = Math.floor(w / vcount);
41315 var b = this.stripBody;
41316 if(b.getWidth() > w){
41317 var tabs = this.items;
41318 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41319 if(availWidth < this.minTabWidth){
41320 /*if(!this.sleft){ // incomplete scrolling code
41321 this.createScrollButtons();
41324 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41327 if(this.currentTabWidth < this.preferredTabWidth){
41328 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41334 * Returns the number of tabs in this TabPanel.
41337 getCount : function(){
41338 return this.items.length;
41342 * Resizes all the tabs to the passed width
41343 * @param {Number} The new width
41345 setTabWidth : function(width){
41346 this.currentTabWidth = width;
41347 for(var i = 0, len = this.items.length; i < len; i++) {
41348 if(!this.items[i].isHidden()) {
41349 this.items[i].setWidth(width);
41355 * Destroys this TabPanel
41356 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41358 destroy : function(removeEl){
41359 Roo.EventManager.removeResizeListener(this.onResize, this);
41360 for(var i = 0, len = this.items.length; i < len; i++){
41361 this.items[i].purgeListeners();
41363 if(removeEl === true){
41364 this.el.update("");
41369 createStrip : function(container)
41371 var strip = document.createElement("nav");
41372 strip.className = Roo.bootstrap.version == 4 ?
41373 "navbar-light bg-light" :
41374 "navbar navbar-default"; //"x-tabs-wrap";
41375 container.appendChild(strip);
41379 createStripList : function(strip)
41381 // div wrapper for retard IE
41382 // returns the "tr" element.
41383 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41384 //'<div class="x-tabs-strip-wrap">'+
41385 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41386 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41387 return strip.firstChild; //.firstChild.firstChild.firstChild;
41389 createBody : function(container)
41391 var body = document.createElement("div");
41392 Roo.id(body, "tab-body");
41393 //Roo.fly(body).addClass("x-tabs-body");
41394 Roo.fly(body).addClass("tab-content");
41395 container.appendChild(body);
41398 createItemBody :function(bodyEl, id){
41399 var body = Roo.getDom(id);
41401 body = document.createElement("div");
41404 //Roo.fly(body).addClass("x-tabs-item-body");
41405 Roo.fly(body).addClass("tab-pane");
41406 bodyEl.insertBefore(body, bodyEl.firstChild);
41410 createStripElements : function(stripEl, text, closable, tpl)
41412 var td = document.createElement("li"); // was td..
41413 td.className = 'nav-item';
41415 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41418 stripEl.appendChild(td);
41420 td.className = "x-tabs-closable";
41421 if(!this.closeTpl){
41422 this.closeTpl = new Roo.Template(
41423 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41424 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41425 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41428 var el = this.closeTpl.overwrite(td, {"text": text});
41429 var close = el.getElementsByTagName("div")[0];
41430 var inner = el.getElementsByTagName("em")[0];
41431 return {"el": el, "close": close, "inner": inner};
41434 // not sure what this is..
41435 // if(!this.tabTpl){
41436 //this.tabTpl = new Roo.Template(
41437 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41438 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41440 // this.tabTpl = new Roo.Template(
41441 // '<a href="#">' +
41442 // '<span unselectable="on"' +
41443 // (this.disableTooltips ? '' : ' title="{text}"') +
41444 // ' >{text}</span></a>'
41450 var template = tpl || this.tabTpl || false;
41453 template = new Roo.Template(
41454 Roo.bootstrap.version == 4 ?
41456 '<a class="nav-link" href="#" unselectable="on"' +
41457 (this.disableTooltips ? '' : ' title="{text}"') +
41460 '<a class="nav-link" href="#">' +
41461 '<span unselectable="on"' +
41462 (this.disableTooltips ? '' : ' title="{text}"') +
41463 ' >{text}</span></a>'
41468 switch (typeof(template)) {
41472 template = new Roo.Template(template);
41478 var el = template.overwrite(td, {"text": text});
41480 var inner = el.getElementsByTagName("span")[0];
41482 return {"el": el, "inner": inner};
41490 * @class Roo.TabPanelItem
41491 * @extends Roo.util.Observable
41492 * Represents an individual item (tab plus body) in a TabPanel.
41493 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41494 * @param {String} id The id of this TabPanelItem
41495 * @param {String} text The text for the tab of this TabPanelItem
41496 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41498 Roo.bootstrap.panel.TabItem = function(config){
41500 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41501 * @type Roo.TabPanel
41503 this.tabPanel = config.panel;
41505 * The id for this TabPanelItem
41508 this.id = config.id;
41510 this.disabled = false;
41512 this.text = config.text;
41514 this.loaded = false;
41515 this.closable = config.closable;
41518 * The body element for this TabPanelItem.
41519 * @type Roo.Element
41521 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41522 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41523 this.bodyEl.setStyle("display", "block");
41524 this.bodyEl.setStyle("zoom", "1");
41525 //this.hideAction();
41527 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41529 this.el = Roo.get(els.el);
41530 this.inner = Roo.get(els.inner, true);
41531 this.textEl = Roo.bootstrap.version == 4 ?
41532 this.el : Roo.get(this.el.dom.firstChild, true);
41534 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41535 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41538 // this.el.on("mousedown", this.onTabMouseDown, this);
41539 this.el.on("click", this.onTabClick, this);
41541 if(config.closable){
41542 var c = Roo.get(els.close, true);
41543 c.dom.title = this.closeText;
41544 c.addClassOnOver("close-over");
41545 c.on("click", this.closeClick, this);
41551 * Fires when this tab becomes the active tab.
41552 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41553 * @param {Roo.TabPanelItem} this
41557 * @event beforeclose
41558 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41559 * @param {Roo.TabPanelItem} this
41560 * @param {Object} e Set cancel to true on this object to cancel the close.
41562 "beforeclose": true,
41565 * Fires when this tab is closed.
41566 * @param {Roo.TabPanelItem} this
41570 * @event deactivate
41571 * Fires when this tab is no longer the active tab.
41572 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41573 * @param {Roo.TabPanelItem} this
41575 "deactivate" : true
41577 this.hidden = false;
41579 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41582 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41584 purgeListeners : function(){
41585 Roo.util.Observable.prototype.purgeListeners.call(this);
41586 this.el.removeAllListeners();
41589 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41592 this.status_node.addClass("active");
41595 this.tabPanel.stripWrap.repaint();
41597 this.fireEvent("activate", this.tabPanel, this);
41601 * Returns true if this tab is the active tab.
41602 * @return {Boolean}
41604 isActive : function(){
41605 return this.tabPanel.getActiveTab() == this;
41609 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41612 this.status_node.removeClass("active");
41614 this.fireEvent("deactivate", this.tabPanel, this);
41617 hideAction : function(){
41618 this.bodyEl.hide();
41619 this.bodyEl.setStyle("position", "absolute");
41620 this.bodyEl.setLeft("-20000px");
41621 this.bodyEl.setTop("-20000px");
41624 showAction : function(){
41625 this.bodyEl.setStyle("position", "relative");
41626 this.bodyEl.setTop("");
41627 this.bodyEl.setLeft("");
41628 this.bodyEl.show();
41632 * Set the tooltip for the tab.
41633 * @param {String} tooltip The tab's tooltip
41635 setTooltip : function(text){
41636 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41637 this.textEl.dom.qtip = text;
41638 this.textEl.dom.removeAttribute('title');
41640 this.textEl.dom.title = text;
41644 onTabClick : function(e){
41645 e.preventDefault();
41646 this.tabPanel.activate(this.id);
41649 onTabMouseDown : function(e){
41650 e.preventDefault();
41651 this.tabPanel.activate(this.id);
41654 getWidth : function(){
41655 return this.inner.getWidth();
41658 setWidth : function(width){
41659 var iwidth = width - this.linode.getPadding("lr");
41660 this.inner.setWidth(iwidth);
41661 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41662 this.linode.setWidth(width);
41666 * Show or hide the tab
41667 * @param {Boolean} hidden True to hide or false to show.
41669 setHidden : function(hidden){
41670 this.hidden = hidden;
41671 this.linode.setStyle("display", hidden ? "none" : "");
41675 * Returns true if this tab is "hidden"
41676 * @return {Boolean}
41678 isHidden : function(){
41679 return this.hidden;
41683 * Returns the text for this tab
41686 getText : function(){
41690 autoSize : function(){
41691 //this.el.beginMeasure();
41692 this.textEl.setWidth(1);
41694 * #2804 [new] Tabs in Roojs
41695 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41697 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41698 //this.el.endMeasure();
41702 * Sets the text for the tab (Note: this also sets the tooltip text)
41703 * @param {String} text The tab's text and tooltip
41705 setText : function(text){
41707 this.textEl.update(text);
41708 this.setTooltip(text);
41709 //if(!this.tabPanel.resizeTabs){
41710 // this.autoSize();
41714 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41716 activate : function(){
41717 this.tabPanel.activate(this.id);
41721 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41723 disable : function(){
41724 if(this.tabPanel.active != this){
41725 this.disabled = true;
41726 this.status_node.addClass("disabled");
41731 * Enables this TabPanelItem if it was previously disabled.
41733 enable : function(){
41734 this.disabled = false;
41735 this.status_node.removeClass("disabled");
41739 * Sets the content for this TabPanelItem.
41740 * @param {String} content The content
41741 * @param {Boolean} loadScripts true to look for and load scripts
41743 setContent : function(content, loadScripts){
41744 this.bodyEl.update(content, loadScripts);
41748 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41749 * @return {Roo.UpdateManager} The UpdateManager
41751 getUpdateManager : function(){
41752 return this.bodyEl.getUpdateManager();
41756 * Set a URL to be used to load the content for this TabPanelItem.
41757 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41758 * @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)
41759 * @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)
41760 * @return {Roo.UpdateManager} The UpdateManager
41762 setUrl : function(url, params, loadOnce){
41763 if(this.refreshDelegate){
41764 this.un('activate', this.refreshDelegate);
41766 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41767 this.on("activate", this.refreshDelegate);
41768 return this.bodyEl.getUpdateManager();
41772 _handleRefresh : function(url, params, loadOnce){
41773 if(!loadOnce || !this.loaded){
41774 var updater = this.bodyEl.getUpdateManager();
41775 updater.update(url, params, this._setLoaded.createDelegate(this));
41780 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41781 * Will fail silently if the setUrl method has not been called.
41782 * This does not activate the panel, just updates its content.
41784 refresh : function(){
41785 if(this.refreshDelegate){
41786 this.loaded = false;
41787 this.refreshDelegate();
41792 _setLoaded : function(){
41793 this.loaded = true;
41797 closeClick : function(e){
41800 this.fireEvent("beforeclose", this, o);
41801 if(o.cancel !== true){
41802 this.tabPanel.removeTab(this.id);
41806 * The text displayed in the tooltip for the close icon.
41809 closeText : "Close this tab"
41812 * This script refer to:
41813 * Title: International Telephone Input
41814 * Author: Jack O'Connor
41815 * Code version: v12.1.12
41816 * Availability: https://github.com/jackocnr/intl-tel-input.git
41819 Roo.bootstrap.PhoneInputData = function() {
41822 "Afghanistan (افغانستان)",
41827 "Albania (Shqipëri)",
41832 "Algeria (الجزائر)",
41857 "Antigua and Barbuda",
41867 "Armenia (Հայաստան)",
41883 "Austria (Österreich)",
41888 "Azerbaijan (Azərbaycan)",
41898 "Bahrain (البحرين)",
41903 "Bangladesh (বাংলাদেশ)",
41913 "Belarus (Беларусь)",
41918 "Belgium (België)",
41948 "Bosnia and Herzegovina (Босна и Херцеговина)",
41963 "British Indian Ocean Territory",
41968 "British Virgin Islands",
41978 "Bulgaria (България)",
41988 "Burundi (Uburundi)",
41993 "Cambodia (កម្ពុជា)",
41998 "Cameroon (Cameroun)",
42007 ["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"]
42010 "Cape Verde (Kabu Verdi)",
42015 "Caribbean Netherlands",
42026 "Central African Republic (République centrafricaine)",
42046 "Christmas Island",
42052 "Cocos (Keeling) Islands",
42063 "Comoros (جزر القمر)",
42068 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42073 "Congo (Republic) (Congo-Brazzaville)",
42093 "Croatia (Hrvatska)",
42114 "Czech Republic (Česká republika)",
42119 "Denmark (Danmark)",
42134 "Dominican Republic (República Dominicana)",
42138 ["809", "829", "849"]
42156 "Equatorial Guinea (Guinea Ecuatorial)",
42176 "Falkland Islands (Islas Malvinas)",
42181 "Faroe Islands (Føroyar)",
42202 "French Guiana (Guyane française)",
42207 "French Polynesia (Polynésie française)",
42222 "Georgia (საქართველო)",
42227 "Germany (Deutschland)",
42247 "Greenland (Kalaallit Nunaat)",
42284 "Guinea-Bissau (Guiné Bissau)",
42309 "Hungary (Magyarország)",
42314 "Iceland (Ísland)",
42334 "Iraq (العراق)",
42350 "Israel (ישראל)",
42377 "Jordan (الأردن)",
42382 "Kazakhstan (Казахстан)",
42403 "Kuwait (الكويت)",
42408 "Kyrgyzstan (Кыргызстан)",
42418 "Latvia (Latvija)",
42423 "Lebanon (لبنان)",
42438 "Libya (ليبيا)",
42448 "Lithuania (Lietuva)",
42463 "Macedonia (FYROM) (Македонија)",
42468 "Madagascar (Madagasikara)",
42498 "Marshall Islands",
42508 "Mauritania (موريتانيا)",
42513 "Mauritius (Moris)",
42534 "Moldova (Republica Moldova)",
42544 "Mongolia (Монгол)",
42549 "Montenegro (Crna Gora)",
42559 "Morocco (المغرب)",
42565 "Mozambique (Moçambique)",
42570 "Myanmar (Burma) (မြန်မာ)",
42575 "Namibia (Namibië)",
42590 "Netherlands (Nederland)",
42595 "New Caledonia (Nouvelle-Calédonie)",
42630 "North Korea (조선 민주주의 인민 공화국)",
42635 "Northern Mariana Islands",
42651 "Pakistan (پاکستان)",
42661 "Palestine (فلسطين)",
42671 "Papua New Guinea",
42713 "Réunion (La Réunion)",
42719 "Romania (România)",
42735 "Saint Barthélemy",
42746 "Saint Kitts and Nevis",
42756 "Saint Martin (Saint-Martin (partie française))",
42762 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42767 "Saint Vincent and the Grenadines",
42782 "São Tomé and Príncipe (São Tomé e Príncipe)",
42787 "Saudi Arabia (المملكة العربية السعودية)",
42792 "Senegal (Sénégal)",
42822 "Slovakia (Slovensko)",
42827 "Slovenia (Slovenija)",
42837 "Somalia (Soomaaliya)",
42847 "South Korea (대한민국)",
42852 "South Sudan (جنوب السودان)",
42862 "Sri Lanka (ශ්රී ලංකාව)",
42867 "Sudan (السودان)",
42877 "Svalbard and Jan Mayen",
42888 "Sweden (Sverige)",
42893 "Switzerland (Schweiz)",
42898 "Syria (سوريا)",
42943 "Trinidad and Tobago",
42948 "Tunisia (تونس)",
42953 "Turkey (Türkiye)",
42963 "Turks and Caicos Islands",
42973 "U.S. Virgin Islands",
42983 "Ukraine (Україна)",
42988 "United Arab Emirates (الإمارات العربية المتحدة)",
43010 "Uzbekistan (Oʻzbekiston)",
43020 "Vatican City (Città del Vaticano)",
43031 "Vietnam (Việt Nam)",
43036 "Wallis and Futuna (Wallis-et-Futuna)",
43041 "Western Sahara (الصحراء الغربية)",
43047 "Yemen (اليمن)",
43071 * This script refer to:
43072 * Title: International Telephone Input
43073 * Author: Jack O'Connor
43074 * Code version: v12.1.12
43075 * Availability: https://github.com/jackocnr/intl-tel-input.git
43079 * @class Roo.bootstrap.PhoneInput
43080 * @extends Roo.bootstrap.TriggerField
43081 * An input with International dial-code selection
43083 * @cfg {String} defaultDialCode default '+852'
43084 * @cfg {Array} preferedCountries default []
43087 * Create a new PhoneInput.
43088 * @param {Object} config Configuration options
43091 Roo.bootstrap.PhoneInput = function(config) {
43092 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43095 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43097 listWidth: undefined,
43099 selectedClass: 'active',
43101 invalidClass : "has-warning",
43103 validClass: 'has-success',
43105 allowed: '0123456789',
43110 * @cfg {String} defaultDialCode The default dial code when initializing the input
43112 defaultDialCode: '+852',
43115 * @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
43117 preferedCountries: false,
43119 getAutoCreate : function()
43121 var data = Roo.bootstrap.PhoneInputData();
43122 var align = this.labelAlign || this.parentLabelAlign();
43125 this.allCountries = [];
43126 this.dialCodeMapping = [];
43128 for (var i = 0; i < data.length; i++) {
43130 this.allCountries[i] = {
43134 priority: c[3] || 0,
43135 areaCodes: c[4] || null
43137 this.dialCodeMapping[c[2]] = {
43140 priority: c[3] || 0,
43141 areaCodes: c[4] || null
43153 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43154 maxlength: this.max_length,
43155 cls : 'form-control tel-input',
43156 autocomplete: 'new-password'
43159 var hiddenInput = {
43162 cls: 'hidden-tel-input'
43166 hiddenInput.name = this.name;
43169 if (this.disabled) {
43170 input.disabled = true;
43173 var flag_container = {
43190 cls: this.hasFeedback ? 'has-feedback' : '',
43196 cls: 'dial-code-holder',
43203 cls: 'roo-select2-container input-group',
43210 if (this.fieldLabel.length) {
43213 tooltip: 'This field is required'
43219 cls: 'control-label',
43225 html: this.fieldLabel
43228 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43234 if(this.indicatorpos == 'right') {
43235 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43242 if(align == 'left') {
43250 if(this.labelWidth > 12){
43251 label.style = "width: " + this.labelWidth + 'px';
43253 if(this.labelWidth < 13 && this.labelmd == 0){
43254 this.labelmd = this.labelWidth;
43256 if(this.labellg > 0){
43257 label.cls += ' col-lg-' + this.labellg;
43258 input.cls += ' col-lg-' + (12 - this.labellg);
43260 if(this.labelmd > 0){
43261 label.cls += ' col-md-' + this.labelmd;
43262 container.cls += ' col-md-' + (12 - this.labelmd);
43264 if(this.labelsm > 0){
43265 label.cls += ' col-sm-' + this.labelsm;
43266 container.cls += ' col-sm-' + (12 - this.labelsm);
43268 if(this.labelxs > 0){
43269 label.cls += ' col-xs-' + this.labelxs;
43270 container.cls += ' col-xs-' + (12 - this.labelxs);
43280 var settings = this;
43282 ['xs','sm','md','lg'].map(function(size){
43283 if (settings[size]) {
43284 cfg.cls += ' col-' + size + '-' + settings[size];
43288 this.store = new Roo.data.Store({
43289 proxy : new Roo.data.MemoryProxy({}),
43290 reader : new Roo.data.JsonReader({
43301 'name' : 'dialCode',
43305 'name' : 'priority',
43309 'name' : 'areaCodes',
43316 if(!this.preferedCountries) {
43317 this.preferedCountries = [
43324 var p = this.preferedCountries.reverse();
43327 for (var i = 0; i < p.length; i++) {
43328 for (var j = 0; j < this.allCountries.length; j++) {
43329 if(this.allCountries[j].iso2 == p[i]) {
43330 var t = this.allCountries[j];
43331 this.allCountries.splice(j,1);
43332 this.allCountries.unshift(t);
43338 this.store.proxy.data = {
43340 data: this.allCountries
43346 initEvents : function()
43349 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43351 this.indicator = this.indicatorEl();
43352 this.flag = this.flagEl();
43353 this.dialCodeHolder = this.dialCodeHolderEl();
43355 this.trigger = this.el.select('div.flag-box',true).first();
43356 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43361 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43362 _this.list.setWidth(lw);
43365 this.list.on('mouseover', this.onViewOver, this);
43366 this.list.on('mousemove', this.onViewMove, this);
43367 this.inputEl().on("keyup", this.onKeyUp, this);
43368 this.inputEl().on("keypress", this.onKeyPress, this);
43370 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43372 this.view = new Roo.View(this.list, this.tpl, {
43373 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43376 this.view.on('click', this.onViewClick, this);
43377 this.setValue(this.defaultDialCode);
43380 onTriggerClick : function(e)
43382 Roo.log('trigger click');
43387 if(this.isExpanded()){
43389 this.hasFocus = false;
43391 this.store.load({});
43392 this.hasFocus = true;
43397 isExpanded : function()
43399 return this.list.isVisible();
43402 collapse : function()
43404 if(!this.isExpanded()){
43408 Roo.get(document).un('mousedown', this.collapseIf, this);
43409 Roo.get(document).un('mousewheel', this.collapseIf, this);
43410 this.fireEvent('collapse', this);
43414 expand : function()
43418 if(this.isExpanded() || !this.hasFocus){
43422 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43423 this.list.setWidth(lw);
43426 this.restrictHeight();
43428 Roo.get(document).on('mousedown', this.collapseIf, this);
43429 Roo.get(document).on('mousewheel', this.collapseIf, this);
43431 this.fireEvent('expand', this);
43434 restrictHeight : function()
43436 this.list.alignTo(this.inputEl(), this.listAlign);
43437 this.list.alignTo(this.inputEl(), this.listAlign);
43440 onViewOver : function(e, t)
43442 if(this.inKeyMode){
43445 var item = this.view.findItemFromChild(t);
43448 var index = this.view.indexOf(item);
43449 this.select(index, false);
43454 onViewClick : function(view, doFocus, el, e)
43456 var index = this.view.getSelectedIndexes()[0];
43458 var r = this.store.getAt(index);
43461 this.onSelect(r, index);
43463 if(doFocus !== false && !this.blockFocus){
43464 this.inputEl().focus();
43468 onViewMove : function(e, t)
43470 this.inKeyMode = false;
43473 select : function(index, scrollIntoView)
43475 this.selectedIndex = index;
43476 this.view.select(index);
43477 if(scrollIntoView !== false){
43478 var el = this.view.getNode(index);
43480 this.list.scrollChildIntoView(el, false);
43485 createList : function()
43487 this.list = Roo.get(document.body).createChild({
43489 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43490 style: 'display:none'
43493 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43496 collapseIf : function(e)
43498 var in_combo = e.within(this.el);
43499 var in_list = e.within(this.list);
43500 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43502 if (in_combo || in_list || is_list) {
43508 onSelect : function(record, index)
43510 if(this.fireEvent('beforeselect', this, record, index) !== false){
43512 this.setFlagClass(record.data.iso2);
43513 this.setDialCode(record.data.dialCode);
43514 this.hasFocus = false;
43516 this.fireEvent('select', this, record, index);
43520 flagEl : function()
43522 var flag = this.el.select('div.flag',true).first();
43529 dialCodeHolderEl : function()
43531 var d = this.el.select('input.dial-code-holder',true).first();
43538 setDialCode : function(v)
43540 this.dialCodeHolder.dom.value = '+'+v;
43543 setFlagClass : function(n)
43545 this.flag.dom.className = 'flag '+n;
43548 getValue : function()
43550 var v = this.inputEl().getValue();
43551 if(this.dialCodeHolder) {
43552 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43557 setValue : function(v)
43559 var d = this.getDialCode(v);
43561 //invalid dial code
43562 if(v.length == 0 || !d || d.length == 0) {
43564 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43565 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43571 this.setFlagClass(this.dialCodeMapping[d].iso2);
43572 this.setDialCode(d);
43573 this.inputEl().dom.value = v.replace('+'+d,'');
43574 this.hiddenEl().dom.value = this.getValue();
43579 getDialCode : function(v)
43583 if (v.length == 0) {
43584 return this.dialCodeHolder.dom.value;
43588 if (v.charAt(0) != "+") {
43591 var numericChars = "";
43592 for (var i = 1; i < v.length; i++) {
43593 var c = v.charAt(i);
43596 if (this.dialCodeMapping[numericChars]) {
43597 dialCode = v.substr(1, i);
43599 if (numericChars.length == 4) {
43609 this.setValue(this.defaultDialCode);
43613 hiddenEl : function()
43615 return this.el.select('input.hidden-tel-input',true).first();
43618 // after setting val
43619 onKeyUp : function(e){
43620 this.setValue(this.getValue());
43623 onKeyPress : function(e){
43624 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43631 * @class Roo.bootstrap.MoneyField
43632 * @extends Roo.bootstrap.ComboBox
43633 * Bootstrap MoneyField class
43636 * Create a new MoneyField.
43637 * @param {Object} config Configuration options
43640 Roo.bootstrap.MoneyField = function(config) {
43642 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43646 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43649 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43651 allowDecimals : true,
43653 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43655 decimalSeparator : ".",
43657 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43659 decimalPrecision : 0,
43661 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43663 allowNegative : true,
43665 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43669 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43671 minValue : Number.NEGATIVE_INFINITY,
43673 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43675 maxValue : Number.MAX_VALUE,
43677 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43679 minText : "The minimum value for this field is {0}",
43681 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43683 maxText : "The maximum value for this field is {0}",
43685 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43686 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43688 nanText : "{0} is not a valid number",
43690 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43694 * @cfg {String} defaults currency of the MoneyField
43695 * value should be in lkey
43697 defaultCurrency : false,
43699 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43701 thousandsDelimiter : false,
43703 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43714 getAutoCreate : function()
43716 var align = this.labelAlign || this.parentLabelAlign();
43728 cls : 'form-control roo-money-amount-input',
43729 autocomplete: 'new-password'
43732 var hiddenInput = {
43736 cls: 'hidden-number-input'
43739 if(this.max_length) {
43740 input.maxlength = this.max_length;
43744 hiddenInput.name = this.name;
43747 if (this.disabled) {
43748 input.disabled = true;
43751 var clg = 12 - this.inputlg;
43752 var cmd = 12 - this.inputmd;
43753 var csm = 12 - this.inputsm;
43754 var cxs = 12 - this.inputxs;
43758 cls : 'row roo-money-field',
43762 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43766 cls: 'roo-select2-container input-group',
43770 cls : 'form-control roo-money-currency-input',
43771 autocomplete: 'new-password',
43773 name : this.currencyName
43777 cls : 'input-group-addon',
43791 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43795 cls: this.hasFeedback ? 'has-feedback' : '',
43806 if (this.fieldLabel.length) {
43809 tooltip: 'This field is required'
43815 cls: 'control-label',
43821 html: this.fieldLabel
43824 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43830 if(this.indicatorpos == 'right') {
43831 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43838 if(align == 'left') {
43846 if(this.labelWidth > 12){
43847 label.style = "width: " + this.labelWidth + 'px';
43849 if(this.labelWidth < 13 && this.labelmd == 0){
43850 this.labelmd = this.labelWidth;
43852 if(this.labellg > 0){
43853 label.cls += ' col-lg-' + this.labellg;
43854 input.cls += ' col-lg-' + (12 - this.labellg);
43856 if(this.labelmd > 0){
43857 label.cls += ' col-md-' + this.labelmd;
43858 container.cls += ' col-md-' + (12 - this.labelmd);
43860 if(this.labelsm > 0){
43861 label.cls += ' col-sm-' + this.labelsm;
43862 container.cls += ' col-sm-' + (12 - this.labelsm);
43864 if(this.labelxs > 0){
43865 label.cls += ' col-xs-' + this.labelxs;
43866 container.cls += ' col-xs-' + (12 - this.labelxs);
43877 var settings = this;
43879 ['xs','sm','md','lg'].map(function(size){
43880 if (settings[size]) {
43881 cfg.cls += ' col-' + size + '-' + settings[size];
43888 initEvents : function()
43890 this.indicator = this.indicatorEl();
43892 this.initCurrencyEvent();
43894 this.initNumberEvent();
43897 initCurrencyEvent : function()
43900 throw "can not find store for combo";
43903 this.store = Roo.factory(this.store, Roo.data);
43904 this.store.parent = this;
43908 this.triggerEl = this.el.select('.input-group-addon', true).first();
43910 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43915 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43916 _this.list.setWidth(lw);
43919 this.list.on('mouseover', this.onViewOver, this);
43920 this.list.on('mousemove', this.onViewMove, this);
43921 this.list.on('scroll', this.onViewScroll, this);
43924 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43927 this.view = new Roo.View(this.list, this.tpl, {
43928 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43931 this.view.on('click', this.onViewClick, this);
43933 this.store.on('beforeload', this.onBeforeLoad, this);
43934 this.store.on('load', this.onLoad, this);
43935 this.store.on('loadexception', this.onLoadException, this);
43937 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43938 "up" : function(e){
43939 this.inKeyMode = true;
43943 "down" : function(e){
43944 if(!this.isExpanded()){
43945 this.onTriggerClick();
43947 this.inKeyMode = true;
43952 "enter" : function(e){
43955 if(this.fireEvent("specialkey", this, e)){
43956 this.onViewClick(false);
43962 "esc" : function(e){
43966 "tab" : function(e){
43969 if(this.fireEvent("specialkey", this, e)){
43970 this.onViewClick(false);
43978 doRelay : function(foo, bar, hname){
43979 if(hname == 'down' || this.scope.isExpanded()){
43980 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43988 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43992 initNumberEvent : function(e)
43994 this.inputEl().on("keydown" , this.fireKey, this);
43995 this.inputEl().on("focus", this.onFocus, this);
43996 this.inputEl().on("blur", this.onBlur, this);
43998 this.inputEl().relayEvent('keyup', this);
44000 if(this.indicator){
44001 this.indicator.addClass('invisible');
44004 this.originalValue = this.getValue();
44006 if(this.validationEvent == 'keyup'){
44007 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44008 this.inputEl().on('keyup', this.filterValidation, this);
44010 else if(this.validationEvent !== false){
44011 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44014 if(this.selectOnFocus){
44015 this.on("focus", this.preFocus, this);
44018 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44019 this.inputEl().on("keypress", this.filterKeys, this);
44021 this.inputEl().relayEvent('keypress', this);
44024 var allowed = "0123456789";
44026 if(this.allowDecimals){
44027 allowed += this.decimalSeparator;
44030 if(this.allowNegative){
44034 if(this.thousandsDelimiter) {
44038 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44040 var keyPress = function(e){
44042 var k = e.getKey();
44044 var c = e.getCharCode();
44047 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44048 allowed.indexOf(String.fromCharCode(c)) === -1
44054 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44058 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44063 this.inputEl().on("keypress", keyPress, this);
44067 onTriggerClick : function(e)
44074 this.loadNext = false;
44076 if(this.isExpanded()){
44081 this.hasFocus = true;
44083 if(this.triggerAction == 'all') {
44084 this.doQuery(this.allQuery, true);
44088 this.doQuery(this.getRawValue());
44091 getCurrency : function()
44093 var v = this.currencyEl().getValue();
44098 restrictHeight : function()
44100 this.list.alignTo(this.currencyEl(), this.listAlign);
44101 this.list.alignTo(this.currencyEl(), this.listAlign);
44104 onViewClick : function(view, doFocus, el, e)
44106 var index = this.view.getSelectedIndexes()[0];
44108 var r = this.store.getAt(index);
44111 this.onSelect(r, index);
44115 onSelect : function(record, index){
44117 if(this.fireEvent('beforeselect', this, record, index) !== false){
44119 this.setFromCurrencyData(index > -1 ? record.data : false);
44123 this.fireEvent('select', this, record, index);
44127 setFromCurrencyData : function(o)
44131 this.lastCurrency = o;
44133 if (this.currencyField) {
44134 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44136 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44139 this.lastSelectionText = currency;
44141 //setting default currency
44142 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44143 this.setCurrency(this.defaultCurrency);
44147 this.setCurrency(currency);
44150 setFromData : function(o)
44154 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44156 this.setFromCurrencyData(c);
44161 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44163 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44166 this.setValue(value);
44170 setCurrency : function(v)
44172 this.currencyValue = v;
44175 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44180 setValue : function(v)
44182 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44188 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44190 this.inputEl().dom.value = (v == '') ? '' :
44191 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44193 if(!this.allowZero && v === '0') {
44194 this.hiddenEl().dom.value = '';
44195 this.inputEl().dom.value = '';
44202 getRawValue : function()
44204 var v = this.inputEl().getValue();
44209 getValue : function()
44211 return this.fixPrecision(this.parseValue(this.getRawValue()));
44214 parseValue : function(value)
44216 if(this.thousandsDelimiter) {
44218 r = new RegExp(",", "g");
44219 value = value.replace(r, "");
44222 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44223 return isNaN(value) ? '' : value;
44227 fixPrecision : function(value)
44229 if(this.thousandsDelimiter) {
44231 r = new RegExp(",", "g");
44232 value = value.replace(r, "");
44235 var nan = isNaN(value);
44237 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44238 return nan ? '' : value;
44240 return parseFloat(value).toFixed(this.decimalPrecision);
44243 decimalPrecisionFcn : function(v)
44245 return Math.floor(v);
44248 validateValue : function(value)
44250 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44254 var num = this.parseValue(value);
44257 this.markInvalid(String.format(this.nanText, value));
44261 if(num < this.minValue){
44262 this.markInvalid(String.format(this.minText, this.minValue));
44266 if(num > this.maxValue){
44267 this.markInvalid(String.format(this.maxText, this.maxValue));
44274 validate : function()
44276 if(this.disabled || this.allowBlank){
44281 var currency = this.getCurrency();
44283 if(this.validateValue(this.getRawValue()) && currency.length){
44288 this.markInvalid();
44292 getName: function()
44297 beforeBlur : function()
44303 var v = this.parseValue(this.getRawValue());
44310 onBlur : function()
44314 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44315 //this.el.removeClass(this.focusClass);
44318 this.hasFocus = false;
44320 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44324 var v = this.getValue();
44326 if(String(v) !== String(this.startValue)){
44327 this.fireEvent('change', this, v, this.startValue);
44330 this.fireEvent("blur", this);
44333 inputEl : function()
44335 return this.el.select('.roo-money-amount-input', true).first();
44338 currencyEl : function()
44340 return this.el.select('.roo-money-currency-input', true).first();
44343 hiddenEl : function()
44345 return this.el.select('input.hidden-number-input',true).first();
44349 * @class Roo.bootstrap.BezierSignature
44350 * @extends Roo.bootstrap.Component
44351 * Bootstrap BezierSignature class
44352 * This script refer to:
44353 * Title: Signature Pad
44355 * Availability: https://github.com/szimek/signature_pad
44358 * Create a new BezierSignature
44359 * @param {Object} config The config object
44362 Roo.bootstrap.BezierSignature = function(config){
44363 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44369 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44376 mouse_btn_down: true,
44379 * @cfg {int} canvas height
44381 canvas_height: '200px',
44384 * @cfg {float|function} Radius of a single dot.
44389 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44394 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44399 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44404 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44409 * @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.
44411 bg_color: 'rgba(0, 0, 0, 0)',
44414 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44416 dot_color: 'black',
44419 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44421 velocity_filter_weight: 0.7,
44424 * @cfg {function} Callback when stroke begin.
44429 * @cfg {function} Callback when stroke end.
44433 getAutoCreate : function()
44435 var cls = 'roo-signature column';
44438 cls += ' ' + this.cls;
44448 for(var i = 0; i < col_sizes.length; i++) {
44449 if(this[col_sizes[i]]) {
44450 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44460 cls: 'roo-signature-body',
44464 cls: 'roo-signature-body-canvas',
44465 height: this.canvas_height,
44466 width: this.canvas_width
44473 style: 'display: none'
44481 initEvents: function()
44483 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44485 var canvas = this.canvasEl();
44487 // mouse && touch event swapping...
44488 canvas.dom.style.touchAction = 'none';
44489 canvas.dom.style.msTouchAction = 'none';
44491 this.mouse_btn_down = false;
44492 canvas.on('mousedown', this._handleMouseDown, this);
44493 canvas.on('mousemove', this._handleMouseMove, this);
44494 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44496 if (window.PointerEvent) {
44497 canvas.on('pointerdown', this._handleMouseDown, this);
44498 canvas.on('pointermove', this._handleMouseMove, this);
44499 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44502 if ('ontouchstart' in window) {
44503 canvas.on('touchstart', this._handleTouchStart, this);
44504 canvas.on('touchmove', this._handleTouchMove, this);
44505 canvas.on('touchend', this._handleTouchEnd, this);
44508 Roo.EventManager.onWindowResize(this.resize, this, true);
44510 // file input event
44511 this.fileEl().on('change', this.uploadImage, this);
44518 resize: function(){
44520 var canvas = this.canvasEl().dom;
44521 var ctx = this.canvasElCtx();
44522 var img_data = false;
44524 if(canvas.width > 0) {
44525 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44527 // setting canvas width will clean img data
44530 var style = window.getComputedStyle ?
44531 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44533 var padding_left = parseInt(style.paddingLeft) || 0;
44534 var padding_right = parseInt(style.paddingRight) || 0;
44536 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44539 ctx.putImageData(img_data, 0, 0);
44543 _handleMouseDown: function(e)
44545 if (e.browserEvent.which === 1) {
44546 this.mouse_btn_down = true;
44547 this.strokeBegin(e);
44551 _handleMouseMove: function (e)
44553 if (this.mouse_btn_down) {
44554 this.strokeMoveUpdate(e);
44558 _handleMouseUp: function (e)
44560 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44561 this.mouse_btn_down = false;
44566 _handleTouchStart: function (e) {
44568 e.preventDefault();
44569 if (e.browserEvent.targetTouches.length === 1) {
44570 // var touch = e.browserEvent.changedTouches[0];
44571 // this.strokeBegin(touch);
44573 this.strokeBegin(e); // assume e catching the correct xy...
44577 _handleTouchMove: function (e) {
44578 e.preventDefault();
44579 // var touch = event.targetTouches[0];
44580 // _this._strokeMoveUpdate(touch);
44581 this.strokeMoveUpdate(e);
44584 _handleTouchEnd: function (e) {
44585 var wasCanvasTouched = e.target === this.canvasEl().dom;
44586 if (wasCanvasTouched) {
44587 e.preventDefault();
44588 // var touch = event.changedTouches[0];
44589 // _this._strokeEnd(touch);
44594 reset: function () {
44595 this._lastPoints = [];
44596 this._lastVelocity = 0;
44597 this._lastWidth = (this.min_width + this.max_width) / 2;
44598 this.canvasElCtx().fillStyle = this.dot_color;
44601 strokeMoveUpdate: function(e)
44603 this.strokeUpdate(e);
44605 if (this.throttle) {
44606 this.throttleStroke(this.strokeUpdate, this.throttle);
44609 this.strokeUpdate(e);
44613 strokeBegin: function(e)
44615 var newPointGroup = {
44616 color: this.dot_color,
44620 if (typeof this.onBegin === 'function') {
44624 this.curve_data.push(newPointGroup);
44626 this.strokeUpdate(e);
44629 strokeUpdate: function(e)
44631 var rect = this.canvasEl().dom.getBoundingClientRect();
44632 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44633 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44634 var lastPoints = lastPointGroup.points;
44635 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44636 var isLastPointTooClose = lastPoint
44637 ? point.distanceTo(lastPoint) <= this.min_distance
44639 var color = lastPointGroup.color;
44640 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44641 var curve = this.addPoint(point);
44643 this.drawDot({color: color, point: point});
44646 this.drawCurve({color: color, curve: curve});
44656 strokeEnd: function(e)
44658 this.strokeUpdate(e);
44659 if (typeof this.onEnd === 'function') {
44664 addPoint: function (point) {
44665 var _lastPoints = this._lastPoints;
44666 _lastPoints.push(point);
44667 if (_lastPoints.length > 2) {
44668 if (_lastPoints.length === 3) {
44669 _lastPoints.unshift(_lastPoints[0]);
44671 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44672 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44673 _lastPoints.shift();
44679 calculateCurveWidths: function (startPoint, endPoint) {
44680 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44681 (1 - this.velocity_filter_weight) * this._lastVelocity;
44683 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44686 start: this._lastWidth
44689 this._lastVelocity = velocity;
44690 this._lastWidth = newWidth;
44694 drawDot: function (_a) {
44695 var color = _a.color, point = _a.point;
44696 var ctx = this.canvasElCtx();
44697 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44699 this.drawCurveSegment(point.x, point.y, width);
44701 ctx.fillStyle = color;
44705 drawCurve: function (_a) {
44706 var color = _a.color, curve = _a.curve;
44707 var ctx = this.canvasElCtx();
44708 var widthDelta = curve.endWidth - curve.startWidth;
44709 var drawSteps = Math.floor(curve.length()) * 2;
44711 ctx.fillStyle = color;
44712 for (var i = 0; i < drawSteps; i += 1) {
44713 var t = i / drawSteps;
44719 var x = uuu * curve.startPoint.x;
44720 x += 3 * uu * t * curve.control1.x;
44721 x += 3 * u * tt * curve.control2.x;
44722 x += ttt * curve.endPoint.x;
44723 var y = uuu * curve.startPoint.y;
44724 y += 3 * uu * t * curve.control1.y;
44725 y += 3 * u * tt * curve.control2.y;
44726 y += ttt * curve.endPoint.y;
44727 var width = curve.startWidth + ttt * widthDelta;
44728 this.drawCurveSegment(x, y, width);
44734 drawCurveSegment: function (x, y, width) {
44735 var ctx = this.canvasElCtx();
44737 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44738 this.is_empty = false;
44743 var ctx = this.canvasElCtx();
44744 var canvas = this.canvasEl().dom;
44745 ctx.fillStyle = this.bg_color;
44746 ctx.clearRect(0, 0, canvas.width, canvas.height);
44747 ctx.fillRect(0, 0, canvas.width, canvas.height);
44748 this.curve_data = [];
44750 this.is_empty = true;
44755 return this.el.select('input',true).first();
44758 canvasEl: function()
44760 return this.el.select('canvas',true).first();
44763 canvasElCtx: function()
44765 return this.el.select('canvas',true).first().dom.getContext('2d');
44768 getImage: function(type)
44770 if(this.is_empty) {
44775 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44778 drawFromImage: function(img_src)
44780 var img = new Image();
44782 img.onload = function(){
44783 this.canvasElCtx().drawImage(img, 0, 0);
44788 this.is_empty = false;
44791 selectImage: function()
44793 this.fileEl().dom.click();
44796 uploadImage: function(e)
44798 var reader = new FileReader();
44800 reader.onload = function(e){
44801 var img = new Image();
44802 img.onload = function(){
44804 this.canvasElCtx().drawImage(img, 0, 0);
44806 img.src = e.target.result;
44809 reader.readAsDataURL(e.target.files[0]);
44812 // Bezier Point Constructor
44813 Point: (function () {
44814 function Point(x, y, time) {
44817 this.time = time || Date.now();
44819 Point.prototype.distanceTo = function (start) {
44820 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44822 Point.prototype.equals = function (other) {
44823 return this.x === other.x && this.y === other.y && this.time === other.time;
44825 Point.prototype.velocityFrom = function (start) {
44826 return this.time !== start.time
44827 ? this.distanceTo(start) / (this.time - start.time)
44834 // Bezier Constructor
44835 Bezier: (function () {
44836 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44837 this.startPoint = startPoint;
44838 this.control2 = control2;
44839 this.control1 = control1;
44840 this.endPoint = endPoint;
44841 this.startWidth = startWidth;
44842 this.endWidth = endWidth;
44844 Bezier.fromPoints = function (points, widths, scope) {
44845 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44846 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44847 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44849 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44850 var dx1 = s1.x - s2.x;
44851 var dy1 = s1.y - s2.y;
44852 var dx2 = s2.x - s3.x;
44853 var dy2 = s2.y - s3.y;
44854 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44855 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44856 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44857 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44858 var dxm = m1.x - m2.x;
44859 var dym = m1.y - m2.y;
44860 var k = l2 / (l1 + l2);
44861 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44862 var tx = s2.x - cm.x;
44863 var ty = s2.y - cm.y;
44865 c1: new scope.Point(m1.x + tx, m1.y + ty),
44866 c2: new scope.Point(m2.x + tx, m2.y + ty)
44869 Bezier.prototype.length = function () {
44874 for (var i = 0; i <= steps; i += 1) {
44876 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44877 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44879 var xdiff = cx - px;
44880 var ydiff = cy - py;
44881 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44888 Bezier.prototype.point = function (t, start, c1, c2, end) {
44889 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44890 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44891 + (3.0 * c2 * (1.0 - t) * t * t)
44892 + (end * t * t * t);
44897 throttleStroke: function(fn, wait) {
44898 if (wait === void 0) { wait = 250; }
44900 var timeout = null;
44904 var later = function () {
44905 previous = Date.now();
44907 result = fn.apply(storedContext, storedArgs);
44909 storedContext = null;
44913 return function wrapper() {
44915 for (var _i = 0; _i < arguments.length; _i++) {
44916 args[_i] = arguments[_i];
44918 var now = Date.now();
44919 var remaining = wait - (now - previous);
44920 storedContext = this;
44922 if (remaining <= 0 || remaining > wait) {
44924 clearTimeout(timeout);
44928 result = fn.apply(storedContext, storedArgs);
44930 storedContext = null;
44934 else if (!timeout) {
44935 timeout = window.setTimeout(later, remaining);