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,
8536 html: cm.getColumnHeader(i)
8541 if(typeof(config.sortable) != 'undefined' && config.sortable){
8543 c.html = '<i class="fa"></i>' + c.html;
8546 // could use BS4 hidden-..-down
8548 if(typeof(config.lgHeader) != 'undefined'){
8549 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8552 if(typeof(config.mdHeader) != 'undefined'){
8553 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8556 if(typeof(config.smHeader) != 'undefined'){
8557 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8560 if(typeof(config.xsHeader) != 'undefined'){
8561 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8568 if(typeof(config.tooltip) != 'undefined'){
8569 c.tooltip = config.tooltip;
8572 if(typeof(config.colspan) != 'undefined'){
8573 c.colspan = config.colspan;
8576 if(typeof(config.hidden) != 'undefined' && config.hidden){
8577 c.style += ' display:none;';
8580 if(typeof(config.dataIndex) != 'undefined'){
8581 c.sort = config.dataIndex;
8586 if(typeof(config.align) != 'undefined' && config.align.length){
8587 c.style += ' text-align:' + config.align + ';';
8590 if(typeof(config.width) != 'undefined'){
8591 c.style += ' width:' + config.width + 'px;';
8592 this.totalWidth += config.width;
8594 this.totalWidth += 100; // assume minimum of 100 per column?
8597 if(typeof(config.cls) != 'undefined'){
8598 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8601 ['xs','sm','md','lg'].map(function(size){
8603 if(typeof(config[size]) == 'undefined'){
8607 if (!config[size]) { // 0 = hidden
8608 // BS 4 '0' is treated as hide that column and below.
8609 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8613 c.cls += ' col-' + size + '-' + config[size] + (
8614 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8626 renderBody : function()
8636 colspan : this.cm.getColumnCount()
8646 renderFooter : function()
8656 colspan : this.cm.getColumnCount()
8670 // Roo.log('ds onload');
8675 var ds = this.store;
8677 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8678 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8679 if (_this.store.sortInfo) {
8681 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8682 e.select('i', true).addClass(['fa-arrow-up']);
8685 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8686 e.select('i', true).addClass(['fa-arrow-down']);
8691 var tbody = this.mainBody;
8693 if(ds.getCount() > 0){
8694 ds.data.each(function(d,rowIndex){
8695 var row = this.renderRow(cm, ds, rowIndex);
8697 tbody.createChild(row);
8701 if(row.cellObjects.length){
8702 Roo.each(row.cellObjects, function(r){
8703 _this.renderCellObject(r);
8710 var tfoot = this.el.select('tfoot', true).first();
8712 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716 var total = this.ds.getTotalCount();
8718 if(this.footer.pageSize < total){
8719 this.mainFoot.show();
8723 Roo.each(this.el.select('tbody td', true).elements, function(e){
8724 e.on('mouseover', _this.onMouseover, _this);
8727 Roo.each(this.el.select('tbody td', true).elements, function(e){
8728 e.on('mouseout', _this.onMouseout, _this);
8730 this.fireEvent('rowsrendered', this);
8736 onUpdate : function(ds,record)
8738 this.refreshRow(record);
8742 onRemove : function(ds, record, index, isUpdate){
8743 if(isUpdate !== true){
8744 this.fireEvent("beforerowremoved", this, index, record);
8746 var bt = this.mainBody.dom;
8748 var rows = this.el.select('tbody > tr', true).elements;
8750 if(typeof(rows[index]) != 'undefined'){
8751 bt.removeChild(rows[index].dom);
8754 // if(bt.rows[index]){
8755 // bt.removeChild(bt.rows[index]);
8758 if(isUpdate !== true){
8759 //this.stripeRows(index);
8760 //this.syncRowHeights(index, index);
8762 this.fireEvent("rowremoved", this, index, record);
8766 onAdd : function(ds, records, rowIndex)
8768 //Roo.log('on Add called');
8769 // - note this does not handle multiple adding very well..
8770 var bt = this.mainBody.dom;
8771 for (var i =0 ; i < records.length;i++) {
8772 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8773 //Roo.log(records[i]);
8774 //Roo.log(this.store.getAt(rowIndex+i));
8775 this.insertRow(this.store, rowIndex + i, false);
8782 refreshRow : function(record){
8783 var ds = this.store, index;
8784 if(typeof record == 'number'){
8786 record = ds.getAt(index);
8788 index = ds.indexOf(record);
8790 return; // should not happen - but seems to
8793 this.insertRow(ds, index, true);
8795 this.onRemove(ds, record, index+1, true);
8797 //this.syncRowHeights(index, index);
8799 this.fireEvent("rowupdated", this, index, record);
8802 insertRow : function(dm, rowIndex, isUpdate){
8805 this.fireEvent("beforerowsinserted", this, rowIndex);
8807 //var s = this.getScrollState();
8808 var row = this.renderRow(this.cm, this.store, rowIndex);
8809 // insert before rowIndex..
8810 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8814 if(row.cellObjects.length){
8815 Roo.each(row.cellObjects, function(r){
8816 _this.renderCellObject(r);
8821 this.fireEvent("rowsinserted", this, rowIndex);
8822 //this.syncRowHeights(firstRow, lastRow);
8823 //this.stripeRows(firstRow);
8830 getRowDom : function(rowIndex)
8832 var rows = this.el.select('tbody > tr', true).elements;
8834 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8837 // returns the object tree for a tr..
8840 renderRow : function(cm, ds, rowIndex)
8842 var d = ds.getAt(rowIndex);
8846 cls : 'x-row-' + rowIndex,
8850 var cellObjects = [];
8852 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8853 var config = cm.config[i];
8855 var renderer = cm.getRenderer(i);
8859 if(typeof(renderer) !== 'undefined'){
8860 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8863 // and are rendered into the cells after the row is rendered - using the id for the element.
8865 if(typeof(value) === 'object'){
8875 rowIndex : rowIndex,
8880 this.fireEvent('rowclass', this, rowcfg);
8884 // this might end up displaying HTML?
8885 // this is too messy... - better to only do it on columsn you know are going to be too long
8886 //tooltip : (typeof(value) === 'object') ? '' : value,
8887 cls : rowcfg.rowClass + ' x-col-' + i,
8889 html: (typeof(value) === 'object') ? '' : value
8896 if(typeof(config.colspan) != 'undefined'){
8897 td.colspan = config.colspan;
8900 if(typeof(config.hidden) != 'undefined' && config.hidden){
8901 td.style += ' display:none;';
8904 if(typeof(config.align) != 'undefined' && config.align.length){
8905 td.style += ' text-align:' + config.align + ';';
8907 if(typeof(config.valign) != 'undefined' && config.valign.length){
8908 td.style += ' vertical-align:' + config.valign + ';';
8911 if(typeof(config.width) != 'undefined'){
8912 td.style += ' width:' + config.width + 'px;';
8915 if(typeof(config.cursor) != 'undefined'){
8916 td.style += ' cursor:' + config.cursor + ';';
8919 if(typeof(config.cls) != 'undefined'){
8920 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8923 ['xs','sm','md','lg'].map(function(size){
8925 if(typeof(config[size]) == 'undefined'){
8931 if (!config[size]) { // 0 = hidden
8932 // BS 4 '0' is treated as hide that column and below.
8933 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8937 td.cls += ' col-' + size + '-' + config[size] + (
8938 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8948 row.cellObjects = cellObjects;
8956 onBeforeLoad : function()
8965 this.el.select('tbody', true).first().dom.innerHTML = '';
8968 * Show or hide a row.
8969 * @param {Number} rowIndex to show or hide
8970 * @param {Boolean} state hide
8972 setRowVisibility : function(rowIndex, state)
8974 var bt = this.mainBody.dom;
8976 var rows = this.el.select('tbody > tr', true).elements;
8978 if(typeof(rows[rowIndex]) == 'undefined'){
8981 rows[rowIndex].dom.style.display = state ? '' : 'none';
8985 getSelectionModel : function(){
8987 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8989 return this.selModel;
8992 * Render the Roo.bootstrap object from renderder
8994 renderCellObject : function(r)
8998 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9000 var t = r.cfg.render(r.container);
9003 Roo.each(r.cfg.cn, function(c){
9005 container: t.getChildContainer(),
9008 _this.renderCellObject(child);
9013 getRowIndex : function(row)
9017 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9028 * Returns the grid's underlying element = used by panel.Grid
9029 * @return {Element} The element
9031 getGridEl : function(){
9035 * Forces a resize - used by panel.Grid
9036 * @return {Element} The element
9038 autoSize : function()
9040 //var ctr = Roo.get(this.container.dom.parentElement);
9041 var ctr = Roo.get(this.el.dom);
9043 var thd = this.getGridEl().select('thead',true).first();
9044 var tbd = this.getGridEl().select('tbody', true).first();
9045 var tfd = this.getGridEl().select('tfoot', true).first();
9047 var cw = ctr.getWidth();
9048 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9052 tbd.setWidth(ctr.getWidth());
9053 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9054 // this needs fixing for various usage - currently only hydra job advers I think..
9056 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9058 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9061 cw = Math.max(cw, this.totalWidth);
9062 this.getGridEl().select('tbody tr',true).setWidth(cw);
9064 // resize 'expandable coloumn?
9066 return; // we doe not have a view in this design..
9069 onBodyScroll: function()
9071 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9073 this.mainHead.setStyle({
9074 'position' : 'relative',
9075 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9081 var scrollHeight = this.mainBody.dom.scrollHeight;
9083 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9085 var height = this.mainBody.getHeight();
9087 if(scrollHeight - height == scrollTop) {
9089 var total = this.ds.getTotalCount();
9091 if(this.footer.cursor + this.footer.pageSize < total){
9093 this.footer.ds.load({
9095 start : this.footer.cursor + this.footer.pageSize,
9096 limit : this.footer.pageSize
9106 onHeaderChange : function()
9108 var header = this.renderHeader();
9109 var table = this.el.select('table', true).first();
9111 this.mainHead.remove();
9112 this.mainHead = table.createChild(header, this.mainBody, false);
9114 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9115 e.on('click', this.sort, this);
9121 onHiddenChange : function(colModel, colIndex, hidden)
9123 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9124 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9126 this.CSS.updateRule(thSelector, "display", "");
9127 this.CSS.updateRule(tdSelector, "display", "");
9130 this.CSS.updateRule(thSelector, "display", "none");
9131 this.CSS.updateRule(tdSelector, "display", "none");
9134 this.onHeaderChange();
9138 setColumnWidth: function(col_index, width)
9140 // width = "md-2 xs-2..."
9141 if(!this.colModel.config[col_index]) {
9145 var w = width.split(" ");
9147 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9149 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9152 for(var j = 0; j < w.length; j++) {
9158 var size_cls = w[j].split("-");
9160 if(!Number.isInteger(size_cls[1] * 1)) {
9164 if(!this.colModel.config[col_index][size_cls[0]]) {
9168 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9172 h_row[0].classList.replace(
9173 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174 "col-"+size_cls[0]+"-"+size_cls[1]
9177 for(var i = 0; i < rows.length; i++) {
9179 var size_cls = w[j].split("-");
9181 if(!Number.isInteger(size_cls[1] * 1)) {
9185 if(!this.colModel.config[col_index][size_cls[0]]) {
9189 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9193 rows[i].classList.replace(
9194 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9195 "col-"+size_cls[0]+"-"+size_cls[1]
9199 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9214 * @class Roo.bootstrap.TableCell
9215 * @extends Roo.bootstrap.Component
9216 * Bootstrap TableCell class
9217 * @cfg {String} html cell contain text
9218 * @cfg {String} cls cell class
9219 * @cfg {String} tag cell tag (td|th) default td
9220 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9221 * @cfg {String} align Aligns the content in a cell
9222 * @cfg {String} axis Categorizes cells
9223 * @cfg {String} bgcolor Specifies the background color of a cell
9224 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9225 * @cfg {Number} colspan Specifies the number of columns a cell should span
9226 * @cfg {String} headers Specifies one or more header cells a cell is related to
9227 * @cfg {Number} height Sets the height of a cell
9228 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9229 * @cfg {Number} rowspan Sets the number of rows a cell should span
9230 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9231 * @cfg {String} valign Vertical aligns the content in a cell
9232 * @cfg {Number} width Specifies the width of a cell
9235 * Create a new TableCell
9236 * @param {Object} config The config object
9239 Roo.bootstrap.TableCell = function(config){
9240 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9243 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9263 getAutoCreate : function(){
9264 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9284 cfg.align=this.align
9290 cfg.bgcolor=this.bgcolor
9293 cfg.charoff=this.charoff
9296 cfg.colspan=this.colspan
9299 cfg.headers=this.headers
9302 cfg.height=this.height
9305 cfg.nowrap=this.nowrap
9308 cfg.rowspan=this.rowspan
9311 cfg.scope=this.scope
9314 cfg.valign=this.valign
9317 cfg.width=this.width
9336 * @class Roo.bootstrap.TableRow
9337 * @extends Roo.bootstrap.Component
9338 * Bootstrap TableRow class
9339 * @cfg {String} cls row class
9340 * @cfg {String} align Aligns the content in a table row
9341 * @cfg {String} bgcolor Specifies a background color for a table row
9342 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9343 * @cfg {String} valign Vertical aligns the content in a table row
9346 * Create a new TableRow
9347 * @param {Object} config The config object
9350 Roo.bootstrap.TableRow = function(config){
9351 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9354 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9362 getAutoCreate : function(){
9363 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9373 cfg.align = this.align;
9376 cfg.bgcolor = this.bgcolor;
9379 cfg.charoff = this.charoff;
9382 cfg.valign = this.valign;
9400 * @class Roo.bootstrap.TableBody
9401 * @extends Roo.bootstrap.Component
9402 * Bootstrap TableBody class
9403 * @cfg {String} cls element class
9404 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9405 * @cfg {String} align Aligns the content inside the element
9406 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9407 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9410 * Create a new TableBody
9411 * @param {Object} config The config object
9414 Roo.bootstrap.TableBody = function(config){
9415 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9418 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9426 getAutoCreate : function(){
9427 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9441 cfg.align = this.align;
9444 cfg.charoff = this.charoff;
9447 cfg.valign = this.valign;
9454 // initEvents : function()
9461 // this.store = Roo.factory(this.store, Roo.data);
9462 // this.store.on('load', this.onLoad, this);
9464 // this.store.load();
9468 // onLoad: function ()
9470 // this.fireEvent('load', this);
9480 * Ext JS Library 1.1.1
9481 * Copyright(c) 2006-2007, Ext JS, LLC.
9483 * Originally Released Under LGPL - original licence link has changed is not relivant.
9486 * <script type="text/javascript">
9489 // as we use this in bootstrap.
9490 Roo.namespace('Roo.form');
9492 * @class Roo.form.Action
9493 * Internal Class used to handle form actions
9495 * @param {Roo.form.BasicForm} el The form element or its id
9496 * @param {Object} config Configuration options
9501 // define the action interface
9502 Roo.form.Action = function(form, options){
9504 this.options = options || {};
9507 * Client Validation Failed
9510 Roo.form.Action.CLIENT_INVALID = 'client';
9512 * Server Validation Failed
9515 Roo.form.Action.SERVER_INVALID = 'server';
9517 * Connect to Server Failed
9520 Roo.form.Action.CONNECT_FAILURE = 'connect';
9522 * Reading Data from Server Failed
9525 Roo.form.Action.LOAD_FAILURE = 'load';
9527 Roo.form.Action.prototype = {
9529 failureType : undefined,
9530 response : undefined,
9534 run : function(options){
9539 success : function(response){
9544 handleResponse : function(response){
9548 // default connection failure
9549 failure : function(response){
9551 this.response = response;
9552 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9553 this.form.afterAction(this, false);
9556 processResponse : function(response){
9557 this.response = response;
9558 if(!response.responseText){
9561 this.result = this.handleResponse(response);
9565 // utility functions used internally
9566 getUrl : function(appendParams){
9567 var url = this.options.url || this.form.url || this.form.el.dom.action;
9569 var p = this.getParams();
9571 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9577 getMethod : function(){
9578 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9581 getParams : function(){
9582 var bp = this.form.baseParams;
9583 var p = this.options.params;
9585 if(typeof p == "object"){
9586 p = Roo.urlEncode(Roo.applyIf(p, bp));
9587 }else if(typeof p == 'string' && bp){
9588 p += '&' + Roo.urlEncode(bp);
9591 p = Roo.urlEncode(bp);
9596 createCallback : function(){
9598 success: this.success,
9599 failure: this.failure,
9601 timeout: (this.form.timeout*1000),
9602 upload: this.form.fileUpload ? this.success : undefined
9607 Roo.form.Action.Submit = function(form, options){
9608 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9611 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9614 haveProgress : false,
9615 uploadComplete : false,
9617 // uploadProgress indicator.
9618 uploadProgress : function()
9620 if (!this.form.progressUrl) {
9624 if (!this.haveProgress) {
9625 Roo.MessageBox.progress("Uploading", "Uploading");
9627 if (this.uploadComplete) {
9628 Roo.MessageBox.hide();
9632 this.haveProgress = true;
9634 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9636 var c = new Roo.data.Connection();
9638 url : this.form.progressUrl,
9643 success : function(req){
9644 //console.log(data);
9648 rdata = Roo.decode(req.responseText)
9650 Roo.log("Invalid data from server..");
9654 if (!rdata || !rdata.success) {
9656 Roo.MessageBox.alert(Roo.encode(rdata));
9659 var data = rdata.data;
9661 if (this.uploadComplete) {
9662 Roo.MessageBox.hide();
9667 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9668 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9671 this.uploadProgress.defer(2000,this);
9674 failure: function(data) {
9675 Roo.log('progress url failed ');
9686 // run get Values on the form, so it syncs any secondary forms.
9687 this.form.getValues();
9689 var o = this.options;
9690 var method = this.getMethod();
9691 var isPost = method == 'POST';
9692 if(o.clientValidation === false || this.form.isValid()){
9694 if (this.form.progressUrl) {
9695 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9696 (new Date() * 1) + '' + Math.random());
9701 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9702 form:this.form.el.dom,
9703 url:this.getUrl(!isPost),
9705 params:isPost ? this.getParams() : null,
9706 isUpload: this.form.fileUpload,
9707 formData : this.form.formData
9710 this.uploadProgress();
9712 }else if (o.clientValidation !== false){ // client validation failed
9713 this.failureType = Roo.form.Action.CLIENT_INVALID;
9714 this.form.afterAction(this, false);
9718 success : function(response)
9720 this.uploadComplete= true;
9721 if (this.haveProgress) {
9722 Roo.MessageBox.hide();
9726 var result = this.processResponse(response);
9727 if(result === true || result.success){
9728 this.form.afterAction(this, true);
9732 this.form.markInvalid(result.errors);
9733 this.failureType = Roo.form.Action.SERVER_INVALID;
9735 this.form.afterAction(this, false);
9737 failure : function(response)
9739 this.uploadComplete= true;
9740 if (this.haveProgress) {
9741 Roo.MessageBox.hide();
9744 this.response = response;
9745 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9746 this.form.afterAction(this, false);
9749 handleResponse : function(response){
9750 if(this.form.errorReader){
9751 var rs = this.form.errorReader.read(response);
9754 for(var i = 0, len = rs.records.length; i < len; i++) {
9755 var r = rs.records[i];
9759 if(errors.length < 1){
9763 success : rs.success,
9769 ret = Roo.decode(response.responseText);
9773 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9783 Roo.form.Action.Load = function(form, options){
9784 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9785 this.reader = this.form.reader;
9788 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9793 Roo.Ajax.request(Roo.apply(
9794 this.createCallback(), {
9795 method:this.getMethod(),
9796 url:this.getUrl(false),
9797 params:this.getParams()
9801 success : function(response){
9803 var result = this.processResponse(response);
9804 if(result === true || !result.success || !result.data){
9805 this.failureType = Roo.form.Action.LOAD_FAILURE;
9806 this.form.afterAction(this, false);
9809 this.form.clearInvalid();
9810 this.form.setValues(result.data);
9811 this.form.afterAction(this, true);
9814 handleResponse : function(response){
9815 if(this.form.reader){
9816 var rs = this.form.reader.read(response);
9817 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9819 success : rs.success,
9823 return Roo.decode(response.responseText);
9827 Roo.form.Action.ACTION_TYPES = {
9828 'load' : Roo.form.Action.Load,
9829 'submit' : Roo.form.Action.Submit
9838 * @class Roo.bootstrap.Form
9839 * @extends Roo.bootstrap.Component
9840 * Bootstrap Form class
9841 * @cfg {String} method GET | POST (default POST)
9842 * @cfg {String} labelAlign top | left (default top)
9843 * @cfg {String} align left | right - for navbars
9844 * @cfg {Boolean} loadMask load mask when submit (default true)
9849 * @param {Object} config The config object
9853 Roo.bootstrap.Form = function(config){
9855 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9857 Roo.bootstrap.Form.popover.apply();
9861 * @event clientvalidation
9862 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9863 * @param {Form} this
9864 * @param {Boolean} valid true if the form has passed client-side validation
9866 clientvalidation: true,
9868 * @event beforeaction
9869 * Fires before any action is performed. Return false to cancel the action.
9870 * @param {Form} this
9871 * @param {Action} action The action to be performed
9875 * @event actionfailed
9876 * Fires when an action fails.
9877 * @param {Form} this
9878 * @param {Action} action The action that failed
9880 actionfailed : true,
9882 * @event actioncomplete
9883 * Fires when an action is completed.
9884 * @param {Form} this
9885 * @param {Action} action The action that completed
9887 actioncomplete : true
9891 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9894 * @cfg {String} method
9895 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9900 * The URL to use for form actions if one isn't supplied in the action options.
9903 * @cfg {Boolean} fileUpload
9904 * Set to true if this form is a file upload.
9908 * @cfg {Object} baseParams
9909 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9913 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9917 * @cfg {Sting} align (left|right) for navbar forms
9922 activeAction : null,
9925 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9926 * element by passing it or its id or mask the form itself by passing in true.
9929 waitMsgTarget : false,
9934 * @cfg {Boolean} errorMask (true|false) default false
9939 * @cfg {Number} maskOffset Default 100
9944 * @cfg {Boolean} maskBody
9948 getAutoCreate : function(){
9952 method : this.method || 'POST',
9953 id : this.id || Roo.id(),
9956 if (this.parent().xtype.match(/^Nav/)) {
9957 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9961 if (this.labelAlign == 'left' ) {
9962 cfg.cls += ' form-horizontal';
9968 initEvents : function()
9970 this.el.on('submit', this.onSubmit, this);
9971 // this was added as random key presses on the form where triggering form submit.
9972 this.el.on('keypress', function(e) {
9973 if (e.getCharCode() != 13) {
9976 // we might need to allow it for textareas.. and some other items.
9977 // check e.getTarget().
9979 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9983 Roo.log("keypress blocked");
9991 onSubmit : function(e){
9996 * Returns true if client-side validation on the form is successful.
9999 isValid : function(){
10000 var items = this.getItems();
10002 var target = false;
10004 items.each(function(f){
10010 Roo.log('invalid field: ' + f.name);
10014 if(!target && f.el.isVisible(true)){
10020 if(this.errorMask && !valid){
10021 Roo.bootstrap.Form.popover.mask(this, target);
10028 * Returns true if any fields in this form have changed since their original load.
10031 isDirty : function(){
10033 var items = this.getItems();
10034 items.each(function(f){
10044 * Performs a predefined action (submit or load) or custom actions you define on this form.
10045 * @param {String} actionName The name of the action type
10046 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10047 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10048 * accept other config options):
10050 Property Type Description
10051 ---------------- --------------- ----------------------------------------------------------------------------------
10052 url String The url for the action (defaults to the form's url)
10053 method String The form method to use (defaults to the form's method, or POST if not defined)
10054 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10055 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10056 validate the form on the client (defaults to false)
10058 * @return {BasicForm} this
10060 doAction : function(action, options){
10061 if(typeof action == 'string'){
10062 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10064 if(this.fireEvent('beforeaction', this, action) !== false){
10065 this.beforeAction(action);
10066 action.run.defer(100, action);
10072 beforeAction : function(action){
10073 var o = action.options;
10078 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10080 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10083 // not really supported yet.. ??
10085 //if(this.waitMsgTarget === true){
10086 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10087 //}else if(this.waitMsgTarget){
10088 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10089 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10091 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10097 afterAction : function(action, success){
10098 this.activeAction = null;
10099 var o = action.options;
10104 Roo.get(document.body).unmask();
10110 //if(this.waitMsgTarget === true){
10111 // this.el.unmask();
10112 //}else if(this.waitMsgTarget){
10113 // this.waitMsgTarget.unmask();
10115 // Roo.MessageBox.updateProgress(1);
10116 // Roo.MessageBox.hide();
10123 Roo.callback(o.success, o.scope, [this, action]);
10124 this.fireEvent('actioncomplete', this, action);
10128 // failure condition..
10129 // we have a scenario where updates need confirming.
10130 // eg. if a locking scenario exists..
10131 // we look for { errors : { needs_confirm : true }} in the response.
10133 (typeof(action.result) != 'undefined') &&
10134 (typeof(action.result.errors) != 'undefined') &&
10135 (typeof(action.result.errors.needs_confirm) != 'undefined')
10138 Roo.log("not supported yet");
10141 Roo.MessageBox.confirm(
10142 "Change requires confirmation",
10143 action.result.errorMsg,
10148 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10158 Roo.callback(o.failure, o.scope, [this, action]);
10159 // show an error message if no failed handler is set..
10160 if (!this.hasListener('actionfailed')) {
10161 Roo.log("need to add dialog support");
10163 Roo.MessageBox.alert("Error",
10164 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10165 action.result.errorMsg :
10166 "Saving Failed, please check your entries or try again"
10171 this.fireEvent('actionfailed', this, action);
10176 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10177 * @param {String} id The value to search for
10180 findField : function(id){
10181 var items = this.getItems();
10182 var field = items.get(id);
10184 items.each(function(f){
10185 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10192 return field || null;
10195 * Mark fields in this form invalid in bulk.
10196 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10197 * @return {BasicForm} this
10199 markInvalid : function(errors){
10200 if(errors instanceof Array){
10201 for(var i = 0, len = errors.length; i < len; i++){
10202 var fieldError = errors[i];
10203 var f = this.findField(fieldError.id);
10205 f.markInvalid(fieldError.msg);
10211 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10212 field.markInvalid(errors[id]);
10216 //Roo.each(this.childForms || [], function (f) {
10217 // f.markInvalid(errors);
10224 * Set values for fields in this form in bulk.
10225 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10226 * @return {BasicForm} this
10228 setValues : function(values){
10229 if(values instanceof Array){ // array of objects
10230 for(var i = 0, len = values.length; i < len; i++){
10232 var f = this.findField(v.id);
10234 f.setValue(v.value);
10235 if(this.trackResetOnLoad){
10236 f.originalValue = f.getValue();
10240 }else{ // object hash
10243 if(typeof values[id] != 'function' && (field = this.findField(id))){
10245 if (field.setFromData &&
10246 field.valueField &&
10247 field.displayField &&
10248 // combos' with local stores can
10249 // be queried via setValue()
10250 // to set their value..
10251 (field.store && !field.store.isLocal)
10255 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10256 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10257 field.setFromData(sd);
10259 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10261 field.setFromData(values);
10264 field.setValue(values[id]);
10268 if(this.trackResetOnLoad){
10269 field.originalValue = field.getValue();
10275 //Roo.each(this.childForms || [], function (f) {
10276 // f.setValues(values);
10283 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10284 * they are returned as an array.
10285 * @param {Boolean} asString
10288 getValues : function(asString){
10289 //if (this.childForms) {
10290 // copy values from the child forms
10291 // Roo.each(this.childForms, function (f) {
10292 // this.setValues(f.getValues());
10298 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10299 if(asString === true){
10302 return Roo.urlDecode(fs);
10306 * Returns the fields in this form as an object with key/value pairs.
10307 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10310 getFieldValues : function(with_hidden)
10312 var items = this.getItems();
10314 items.each(function(f){
10316 if (!f.getName()) {
10320 var v = f.getValue();
10322 if (f.inputType =='radio') {
10323 if (typeof(ret[f.getName()]) == 'undefined') {
10324 ret[f.getName()] = ''; // empty..
10327 if (!f.el.dom.checked) {
10331 v = f.el.dom.value;
10335 if(f.xtype == 'MoneyField'){
10336 ret[f.currencyName] = f.getCurrency();
10339 // not sure if this supported any more..
10340 if ((typeof(v) == 'object') && f.getRawValue) {
10341 v = f.getRawValue() ; // dates..
10343 // combo boxes where name != hiddenName...
10344 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10345 ret[f.name] = f.getRawValue();
10347 ret[f.getName()] = v;
10354 * Clears all invalid messages in this form.
10355 * @return {BasicForm} this
10357 clearInvalid : function(){
10358 var items = this.getItems();
10360 items.each(function(f){
10368 * Resets this form.
10369 * @return {BasicForm} this
10371 reset : function(){
10372 var items = this.getItems();
10373 items.each(function(f){
10377 Roo.each(this.childForms || [], function (f) {
10385 getItems : function()
10387 var r=new Roo.util.MixedCollection(false, function(o){
10388 return o.id || (o.id = Roo.id());
10390 var iter = function(el) {
10397 Roo.each(el.items,function(e) {
10406 hideFields : function(items)
10408 Roo.each(items, function(i){
10410 var f = this.findField(i);
10421 showFields : function(items)
10423 Roo.each(items, function(i){
10425 var f = this.findField(i);
10438 Roo.apply(Roo.bootstrap.Form, {
10454 intervalID : false,
10460 if(this.isApplied){
10465 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10466 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10467 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10468 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10471 this.maskEl.top.enableDisplayMode("block");
10472 this.maskEl.left.enableDisplayMode("block");
10473 this.maskEl.bottom.enableDisplayMode("block");
10474 this.maskEl.right.enableDisplayMode("block");
10476 this.toolTip = new Roo.bootstrap.Tooltip({
10477 cls : 'roo-form-error-popover',
10479 'left' : ['r-l', [-2,0], 'right'],
10480 'right' : ['l-r', [2,0], 'left'],
10481 'bottom' : ['tl-bl', [0,2], 'top'],
10482 'top' : [ 'bl-tl', [0,-2], 'bottom']
10486 this.toolTip.render(Roo.get(document.body));
10488 this.toolTip.el.enableDisplayMode("block");
10490 Roo.get(document.body).on('click', function(){
10494 Roo.get(document.body).on('touchstart', function(){
10498 this.isApplied = true
10501 mask : function(form, target)
10505 this.target = target;
10507 if(!this.form.errorMask || !target.el){
10511 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10513 Roo.log(scrollable);
10515 var ot = this.target.el.calcOffsetsTo(scrollable);
10517 var scrollTo = ot[1] - this.form.maskOffset;
10519 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10521 scrollable.scrollTo('top', scrollTo);
10523 var box = this.target.el.getBox();
10525 var zIndex = Roo.bootstrap.Modal.zIndex++;
10528 this.maskEl.top.setStyle('position', 'absolute');
10529 this.maskEl.top.setStyle('z-index', zIndex);
10530 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10531 this.maskEl.top.setLeft(0);
10532 this.maskEl.top.setTop(0);
10533 this.maskEl.top.show();
10535 this.maskEl.left.setStyle('position', 'absolute');
10536 this.maskEl.left.setStyle('z-index', zIndex);
10537 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10538 this.maskEl.left.setLeft(0);
10539 this.maskEl.left.setTop(box.y - this.padding);
10540 this.maskEl.left.show();
10542 this.maskEl.bottom.setStyle('position', 'absolute');
10543 this.maskEl.bottom.setStyle('z-index', zIndex);
10544 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10545 this.maskEl.bottom.setLeft(0);
10546 this.maskEl.bottom.setTop(box.bottom + this.padding);
10547 this.maskEl.bottom.show();
10549 this.maskEl.right.setStyle('position', 'absolute');
10550 this.maskEl.right.setStyle('z-index', zIndex);
10551 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10552 this.maskEl.right.setLeft(box.right + this.padding);
10553 this.maskEl.right.setTop(box.y - this.padding);
10554 this.maskEl.right.show();
10556 this.toolTip.bindEl = this.target.el;
10558 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10560 var tip = this.target.blankText;
10562 if(this.target.getValue() !== '' ) {
10564 if (this.target.invalidText.length) {
10565 tip = this.target.invalidText;
10566 } else if (this.target.regexText.length){
10567 tip = this.target.regexText;
10571 this.toolTip.show(tip);
10573 this.intervalID = window.setInterval(function() {
10574 Roo.bootstrap.Form.popover.unmask();
10577 window.onwheel = function(){ return false;};
10579 (function(){ this.isMasked = true; }).defer(500, this);
10583 unmask : function()
10585 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10589 this.maskEl.top.setStyle('position', 'absolute');
10590 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10591 this.maskEl.top.hide();
10593 this.maskEl.left.setStyle('position', 'absolute');
10594 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10595 this.maskEl.left.hide();
10597 this.maskEl.bottom.setStyle('position', 'absolute');
10598 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10599 this.maskEl.bottom.hide();
10601 this.maskEl.right.setStyle('position', 'absolute');
10602 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10603 this.maskEl.right.hide();
10605 this.toolTip.hide();
10607 this.toolTip.el.hide();
10609 window.onwheel = function(){ return true;};
10611 if(this.intervalID){
10612 window.clearInterval(this.intervalID);
10613 this.intervalID = false;
10616 this.isMasked = false;
10626 * Ext JS Library 1.1.1
10627 * Copyright(c) 2006-2007, Ext JS, LLC.
10629 * Originally Released Under LGPL - original licence link has changed is not relivant.
10632 * <script type="text/javascript">
10635 * @class Roo.form.VTypes
10636 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10639 Roo.form.VTypes = function(){
10640 // closure these in so they are only created once.
10641 var alpha = /^[a-zA-Z_]+$/;
10642 var alphanum = /^[a-zA-Z0-9_]+$/;
10643 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10644 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10646 // All these messages and functions are configurable
10649 * The function used to validate email addresses
10650 * @param {String} value The email address
10652 'email' : function(v){
10653 return email.test(v);
10656 * The error text to display when the email validation function returns false
10659 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10661 * The keystroke filter mask to be applied on email input
10664 'emailMask' : /[a-z0-9_\.\-@]/i,
10667 * The function used to validate URLs
10668 * @param {String} value The URL
10670 'url' : function(v){
10671 return url.test(v);
10674 * The error text to display when the url validation function returns false
10677 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10680 * The function used to validate alpha values
10681 * @param {String} value The value
10683 'alpha' : function(v){
10684 return alpha.test(v);
10687 * The error text to display when the alpha validation function returns false
10690 'alphaText' : 'This field should only contain letters and _',
10692 * The keystroke filter mask to be applied on alpha input
10695 'alphaMask' : /[a-z_]/i,
10698 * The function used to validate alphanumeric values
10699 * @param {String} value The value
10701 'alphanum' : function(v){
10702 return alphanum.test(v);
10705 * The error text to display when the alphanumeric validation function returns false
10708 'alphanumText' : 'This field should only contain letters, numbers and _',
10710 * The keystroke filter mask to be applied on alphanumeric input
10713 'alphanumMask' : /[a-z0-9_]/i
10723 * @class Roo.bootstrap.Input
10724 * @extends Roo.bootstrap.Component
10725 * Bootstrap Input class
10726 * @cfg {Boolean} disabled is it disabled
10727 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10728 * @cfg {String} name name of the input
10729 * @cfg {string} fieldLabel - the label associated
10730 * @cfg {string} placeholder - placeholder to put in text.
10731 * @cfg {string} before - input group add on before
10732 * @cfg {string} after - input group add on after
10733 * @cfg {string} size - (lg|sm) or leave empty..
10734 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10735 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10736 * @cfg {Number} md colspan out of 12 for computer-sized screens
10737 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10738 * @cfg {string} value default value of the input
10739 * @cfg {Number} labelWidth set the width of label
10740 * @cfg {Number} labellg set the width of label (1-12)
10741 * @cfg {Number} labelmd set the width of label (1-12)
10742 * @cfg {Number} labelsm set the width of label (1-12)
10743 * @cfg {Number} labelxs set the width of label (1-12)
10744 * @cfg {String} labelAlign (top|left)
10745 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10746 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10747 * @cfg {String} indicatorpos (left|right) default left
10748 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10749 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10750 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10752 * @cfg {String} align (left|center|right) Default left
10753 * @cfg {Boolean} forceFeedback (true|false) Default false
10756 * Create a new Input
10757 * @param {Object} config The config object
10760 Roo.bootstrap.Input = function(config){
10762 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10767 * Fires when this field receives input focus.
10768 * @param {Roo.form.Field} this
10773 * Fires when this field loses input focus.
10774 * @param {Roo.form.Field} this
10778 * @event specialkey
10779 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10780 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10781 * @param {Roo.form.Field} this
10782 * @param {Roo.EventObject} e The event object
10787 * Fires just before the field blurs if the field value has changed.
10788 * @param {Roo.form.Field} this
10789 * @param {Mixed} newValue The new value
10790 * @param {Mixed} oldValue The original value
10795 * Fires after the field has been marked as invalid.
10796 * @param {Roo.form.Field} this
10797 * @param {String} msg The validation message
10802 * Fires after the field has been validated with no errors.
10803 * @param {Roo.form.Field} this
10808 * Fires after the key up
10809 * @param {Roo.form.Field} this
10810 * @param {Roo.EventObject} e The event Object
10815 * Fires after the user pastes into input
10816 * @param {Roo.form.Field} this
10817 * @param {Roo.EventObject} e The event Object
10823 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10825 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10826 automatic validation (defaults to "keyup").
10828 validationEvent : "keyup",
10830 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10832 validateOnBlur : true,
10834 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10836 validationDelay : 250,
10838 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10840 focusClass : "x-form-focus", // not needed???
10844 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10846 invalidClass : "has-warning",
10849 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10851 validClass : "has-success",
10854 * @cfg {Boolean} hasFeedback (true|false) default true
10856 hasFeedback : true,
10859 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10861 invalidFeedbackClass : "glyphicon-warning-sign",
10864 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10866 validFeedbackClass : "glyphicon-ok",
10869 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10871 selectOnFocus : false,
10874 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10878 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10883 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10885 disableKeyFilter : false,
10888 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10892 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10896 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10898 blankText : "Please complete this mandatory field",
10901 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10905 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10907 maxLength : Number.MAX_VALUE,
10909 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10911 minLengthText : "The minimum length for this field is {0}",
10913 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10915 maxLengthText : "The maximum length for this field is {0}",
10919 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10920 * If available, this function will be called only after the basic validators all return true, and will be passed the
10921 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10925 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10926 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10927 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10931 * @cfg {String} regexText -- Depricated - use Invalid Text
10936 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10942 autocomplete: false,
10946 inputType : 'text',
10949 placeholder: false,
10954 preventMark: false,
10955 isFormField : true,
10958 labelAlign : false,
10961 formatedValue : false,
10962 forceFeedback : false,
10964 indicatorpos : 'left',
10974 parentLabelAlign : function()
10977 while (parent.parent()) {
10978 parent = parent.parent();
10979 if (typeof(parent.labelAlign) !='undefined') {
10980 return parent.labelAlign;
10987 getAutoCreate : function()
10989 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10995 if(this.inputType != 'hidden'){
10996 cfg.cls = 'form-group' //input-group
11002 type : this.inputType,
11003 value : this.value,
11004 cls : 'form-control',
11005 placeholder : this.placeholder || '',
11006 autocomplete : this.autocomplete || 'new-password'
11008 if (this.inputType == 'file') {
11009 input.style = 'overflow:hidden'; // why not in CSS?
11012 if(this.capture.length){
11013 input.capture = this.capture;
11016 if(this.accept.length){
11017 input.accept = this.accept + "/*";
11021 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11024 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11025 input.maxLength = this.maxLength;
11028 if (this.disabled) {
11029 input.disabled=true;
11032 if (this.readOnly) {
11033 input.readonly=true;
11037 input.name = this.name;
11041 input.cls += ' input-' + this.size;
11045 ['xs','sm','md','lg'].map(function(size){
11046 if (settings[size]) {
11047 cfg.cls += ' col-' + size + '-' + settings[size];
11051 var inputblock = input;
11055 cls: 'glyphicon form-control-feedback'
11058 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11061 cls : 'has-feedback',
11069 if (this.before || this.after) {
11072 cls : 'input-group',
11076 if (this.before && typeof(this.before) == 'string') {
11078 inputblock.cn.push({
11080 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11084 if (this.before && typeof(this.before) == 'object') {
11085 this.before = Roo.factory(this.before);
11087 inputblock.cn.push({
11089 cls : 'roo-input-before input-group-prepend input-group-' +
11090 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11094 inputblock.cn.push(input);
11096 if (this.after && typeof(this.after) == 'string') {
11097 inputblock.cn.push({
11099 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11103 if (this.after && typeof(this.after) == 'object') {
11104 this.after = Roo.factory(this.after);
11106 inputblock.cn.push({
11108 cls : 'roo-input-after input-group-append input-group-' +
11109 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11113 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11114 inputblock.cls += ' has-feedback';
11115 inputblock.cn.push(feedback);
11120 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11121 tooltip : 'This field is required'
11123 if (this.allowBlank ) {
11124 indicator.style = this.allowBlank ? ' display:none' : '';
11126 if (align ==='left' && this.fieldLabel.length) {
11128 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11135 cls : 'control-label col-form-label',
11136 html : this.fieldLabel
11147 var labelCfg = cfg.cn[1];
11148 var contentCfg = cfg.cn[2];
11150 if(this.indicatorpos == 'right'){
11155 cls : 'control-label col-form-label',
11159 html : this.fieldLabel
11173 labelCfg = cfg.cn[0];
11174 contentCfg = cfg.cn[1];
11178 if(this.labelWidth > 12){
11179 labelCfg.style = "width: " + this.labelWidth + 'px';
11182 if(this.labelWidth < 13 && this.labelmd == 0){
11183 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11186 if(this.labellg > 0){
11187 labelCfg.cls += ' col-lg-' + this.labellg;
11188 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11191 if(this.labelmd > 0){
11192 labelCfg.cls += ' col-md-' + this.labelmd;
11193 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11196 if(this.labelsm > 0){
11197 labelCfg.cls += ' col-sm-' + this.labelsm;
11198 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11201 if(this.labelxs > 0){
11202 labelCfg.cls += ' col-xs-' + this.labelxs;
11203 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11207 } else if ( this.fieldLabel.length) {
11214 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11215 tooltip : 'This field is required',
11216 style : this.allowBlank ? ' display:none' : ''
11220 //cls : 'input-group-addon',
11221 html : this.fieldLabel
11229 if(this.indicatorpos == 'right'){
11234 //cls : 'input-group-addon',
11235 html : this.fieldLabel
11240 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11241 tooltip : 'This field is required',
11242 style : this.allowBlank ? ' display:none' : ''
11262 if (this.parentType === 'Navbar' && this.parent().bar) {
11263 cfg.cls += ' navbar-form';
11266 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11267 // on BS4 we do this only if not form
11268 cfg.cls += ' navbar-form';
11276 * return the real input element.
11278 inputEl: function ()
11280 return this.el.select('input.form-control',true).first();
11283 tooltipEl : function()
11285 return this.inputEl();
11288 indicatorEl : function()
11290 if (Roo.bootstrap.version == 4) {
11291 return false; // not enabled in v4 yet.
11294 var indicator = this.el.select('i.roo-required-indicator',true).first();
11304 setDisabled : function(v)
11306 var i = this.inputEl().dom;
11308 i.removeAttribute('disabled');
11312 i.setAttribute('disabled','true');
11314 initEvents : function()
11317 this.inputEl().on("keydown" , this.fireKey, this);
11318 this.inputEl().on("focus", this.onFocus, this);
11319 this.inputEl().on("blur", this.onBlur, this);
11321 this.inputEl().relayEvent('keyup', this);
11322 this.inputEl().relayEvent('paste', this);
11324 this.indicator = this.indicatorEl();
11326 if(this.indicator){
11327 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11330 // reference to original value for reset
11331 this.originalValue = this.getValue();
11332 //Roo.form.TextField.superclass.initEvents.call(this);
11333 if(this.validationEvent == 'keyup'){
11334 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11335 this.inputEl().on('keyup', this.filterValidation, this);
11337 else if(this.validationEvent !== false){
11338 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11341 if(this.selectOnFocus){
11342 this.on("focus", this.preFocus, this);
11345 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11346 this.inputEl().on("keypress", this.filterKeys, this);
11348 this.inputEl().relayEvent('keypress', this);
11351 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11352 this.el.on("click", this.autoSize, this);
11355 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11356 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11359 if (typeof(this.before) == 'object') {
11360 this.before.render(this.el.select('.roo-input-before',true).first());
11362 if (typeof(this.after) == 'object') {
11363 this.after.render(this.el.select('.roo-input-after',true).first());
11366 this.inputEl().on('change', this.onChange, this);
11369 filterValidation : function(e){
11370 if(!e.isNavKeyPress()){
11371 this.validationTask.delay(this.validationDelay);
11375 * Validates the field value
11376 * @return {Boolean} True if the value is valid, else false
11378 validate : function(){
11379 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11380 if(this.disabled || this.validateValue(this.getRawValue())){
11385 this.markInvalid();
11391 * Validates a value according to the field's validation rules and marks the field as invalid
11392 * if the validation fails
11393 * @param {Mixed} value The value to validate
11394 * @return {Boolean} True if the value is valid, else false
11396 validateValue : function(value)
11398 if(this.getVisibilityEl().hasClass('hidden')){
11402 if(value.length < 1) { // if it's blank
11403 if(this.allowBlank){
11409 if(value.length < this.minLength){
11412 if(value.length > this.maxLength){
11416 var vt = Roo.form.VTypes;
11417 if(!vt[this.vtype](value, this)){
11421 if(typeof this.validator == "function"){
11422 var msg = this.validator(value);
11426 if (typeof(msg) == 'string') {
11427 this.invalidText = msg;
11431 if(this.regex && !this.regex.test(value)){
11439 fireKey : function(e){
11440 //Roo.log('field ' + e.getKey());
11441 if(e.isNavKeyPress()){
11442 this.fireEvent("specialkey", this, e);
11445 focus : function (selectText){
11447 this.inputEl().focus();
11448 if(selectText === true){
11449 this.inputEl().dom.select();
11455 onFocus : function(){
11456 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11457 // this.el.addClass(this.focusClass);
11459 if(!this.hasFocus){
11460 this.hasFocus = true;
11461 this.startValue = this.getValue();
11462 this.fireEvent("focus", this);
11466 beforeBlur : Roo.emptyFn,
11470 onBlur : function(){
11472 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11473 //this.el.removeClass(this.focusClass);
11475 this.hasFocus = false;
11476 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11479 var v = this.getValue();
11480 if(String(v) !== String(this.startValue)){
11481 this.fireEvent('change', this, v, this.startValue);
11483 this.fireEvent("blur", this);
11486 onChange : function(e)
11488 var v = this.getValue();
11489 if(String(v) !== String(this.startValue)){
11490 this.fireEvent('change', this, v, this.startValue);
11496 * Resets the current field value to the originally loaded value and clears any validation messages
11498 reset : function(){
11499 this.setValue(this.originalValue);
11503 * Returns the name of the field
11504 * @return {Mixed} name The name field
11506 getName: function(){
11510 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11511 * @return {Mixed} value The field value
11513 getValue : function(){
11515 var v = this.inputEl().getValue();
11520 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11521 * @return {Mixed} value The field value
11523 getRawValue : function(){
11524 var v = this.inputEl().getValue();
11530 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11531 * @param {Mixed} value The value to set
11533 setRawValue : function(v){
11534 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11537 selectText : function(start, end){
11538 var v = this.getRawValue();
11540 start = start === undefined ? 0 : start;
11541 end = end === undefined ? v.length : end;
11542 var d = this.inputEl().dom;
11543 if(d.setSelectionRange){
11544 d.setSelectionRange(start, end);
11545 }else if(d.createTextRange){
11546 var range = d.createTextRange();
11547 range.moveStart("character", start);
11548 range.moveEnd("character", v.length-end);
11555 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11556 * @param {Mixed} value The value to set
11558 setValue : function(v){
11561 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11567 processValue : function(value){
11568 if(this.stripCharsRe){
11569 var newValue = value.replace(this.stripCharsRe, '');
11570 if(newValue !== value){
11571 this.setRawValue(newValue);
11578 preFocus : function(){
11580 if(this.selectOnFocus){
11581 this.inputEl().dom.select();
11584 filterKeys : function(e){
11585 var k = e.getKey();
11586 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11589 var c = e.getCharCode(), cc = String.fromCharCode(c);
11590 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11593 if(!this.maskRe.test(cc)){
11598 * Clear any invalid styles/messages for this field
11600 clearInvalid : function(){
11602 if(!this.el || this.preventMark){ // not rendered
11607 this.el.removeClass([this.invalidClass, 'is-invalid']);
11609 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11611 var feedback = this.el.select('.form-control-feedback', true).first();
11614 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11619 if(this.indicator){
11620 this.indicator.removeClass('visible');
11621 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11624 this.fireEvent('valid', this);
11628 * Mark this field as valid
11630 markValid : function()
11632 if(!this.el || this.preventMark){ // not rendered...
11636 this.el.removeClass([this.invalidClass, this.validClass]);
11637 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11639 var feedback = this.el.select('.form-control-feedback', true).first();
11642 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11645 if(this.indicator){
11646 this.indicator.removeClass('visible');
11647 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11655 if(this.allowBlank && !this.getRawValue().length){
11658 if (Roo.bootstrap.version == 3) {
11659 this.el.addClass(this.validClass);
11661 this.inputEl().addClass('is-valid');
11664 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11666 var feedback = this.el.select('.form-control-feedback', true).first();
11669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11670 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11675 this.fireEvent('valid', this);
11679 * Mark this field as invalid
11680 * @param {String} msg The validation message
11682 markInvalid : function(msg)
11684 if(!this.el || this.preventMark){ // not rendered
11688 this.el.removeClass([this.invalidClass, this.validClass]);
11689 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11691 var feedback = this.el.select('.form-control-feedback', true).first();
11694 this.el.select('.form-control-feedback', true).first().removeClass(
11695 [this.invalidFeedbackClass, this.validFeedbackClass]);
11702 if(this.allowBlank && !this.getRawValue().length){
11706 if(this.indicator){
11707 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11708 this.indicator.addClass('visible');
11710 if (Roo.bootstrap.version == 3) {
11711 this.el.addClass(this.invalidClass);
11713 this.inputEl().addClass('is-invalid');
11718 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11720 var feedback = this.el.select('.form-control-feedback', true).first();
11723 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11725 if(this.getValue().length || this.forceFeedback){
11726 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11733 this.fireEvent('invalid', this, msg);
11736 SafariOnKeyDown : function(event)
11738 // this is a workaround for a password hang bug on chrome/ webkit.
11739 if (this.inputEl().dom.type != 'password') {
11743 var isSelectAll = false;
11745 if(this.inputEl().dom.selectionEnd > 0){
11746 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11748 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11749 event.preventDefault();
11754 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11756 event.preventDefault();
11757 // this is very hacky as keydown always get's upper case.
11759 var cc = String.fromCharCode(event.getCharCode());
11760 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11764 adjustWidth : function(tag, w){
11765 tag = tag.toLowerCase();
11766 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11767 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11768 if(tag == 'input'){
11771 if(tag == 'textarea'){
11774 }else if(Roo.isOpera){
11775 if(tag == 'input'){
11778 if(tag == 'textarea'){
11786 setFieldLabel : function(v)
11788 if(!this.rendered){
11792 if(this.indicatorEl()){
11793 var ar = this.el.select('label > span',true);
11795 if (ar.elements.length) {
11796 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11797 this.fieldLabel = v;
11801 var br = this.el.select('label',true);
11803 if(br.elements.length) {
11804 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11805 this.fieldLabel = v;
11809 Roo.log('Cannot Found any of label > span || label in input');
11813 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11814 this.fieldLabel = v;
11829 * @class Roo.bootstrap.TextArea
11830 * @extends Roo.bootstrap.Input
11831 * Bootstrap TextArea class
11832 * @cfg {Number} cols Specifies the visible width of a text area
11833 * @cfg {Number} rows Specifies the visible number of lines in a text area
11834 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11835 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11836 * @cfg {string} html text
11839 * Create a new TextArea
11840 * @param {Object} config The config object
11843 Roo.bootstrap.TextArea = function(config){
11844 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11848 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11858 getAutoCreate : function(){
11860 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11866 if(this.inputType != 'hidden'){
11867 cfg.cls = 'form-group' //input-group
11875 value : this.value || '',
11876 html: this.html || '',
11877 cls : 'form-control',
11878 placeholder : this.placeholder || ''
11882 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11883 input.maxLength = this.maxLength;
11887 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11891 input.cols = this.cols;
11894 if (this.readOnly) {
11895 input.readonly = true;
11899 input.name = this.name;
11903 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11907 ['xs','sm','md','lg'].map(function(size){
11908 if (settings[size]) {
11909 cfg.cls += ' col-' + size + '-' + settings[size];
11913 var inputblock = input;
11915 if(this.hasFeedback && !this.allowBlank){
11919 cls: 'glyphicon form-control-feedback'
11923 cls : 'has-feedback',
11932 if (this.before || this.after) {
11935 cls : 'input-group',
11939 inputblock.cn.push({
11941 cls : 'input-group-addon',
11946 inputblock.cn.push(input);
11948 if(this.hasFeedback && !this.allowBlank){
11949 inputblock.cls += ' has-feedback';
11950 inputblock.cn.push(feedback);
11954 inputblock.cn.push({
11956 cls : 'input-group-addon',
11963 if (align ==='left' && this.fieldLabel.length) {
11968 cls : 'control-label',
11969 html : this.fieldLabel
11980 if(this.labelWidth > 12){
11981 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11984 if(this.labelWidth < 13 && this.labelmd == 0){
11985 this.labelmd = this.labelWidth;
11988 if(this.labellg > 0){
11989 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11990 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11993 if(this.labelmd > 0){
11994 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11995 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11998 if(this.labelsm > 0){
11999 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12000 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12003 if(this.labelxs > 0){
12004 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12005 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12008 } else if ( this.fieldLabel.length) {
12013 //cls : 'input-group-addon',
12014 html : this.fieldLabel
12032 if (this.disabled) {
12033 input.disabled=true;
12040 * return the real textarea element.
12042 inputEl: function ()
12044 return this.el.select('textarea.form-control',true).first();
12048 * Clear any invalid styles/messages for this field
12050 clearInvalid : function()
12053 if(!this.el || this.preventMark){ // not rendered
12057 var label = this.el.select('label', true).first();
12058 var icon = this.el.select('i.fa-star', true).first();
12063 this.el.removeClass( this.validClass);
12064 this.inputEl().removeClass('is-invalid');
12066 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12068 var feedback = this.el.select('.form-control-feedback', true).first();
12071 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12076 this.fireEvent('valid', this);
12080 * Mark this field as valid
12082 markValid : function()
12084 if(!this.el || this.preventMark){ // not rendered
12088 this.el.removeClass([this.invalidClass, this.validClass]);
12089 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12091 var feedback = this.el.select('.form-control-feedback', true).first();
12094 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12097 if(this.disabled || this.allowBlank){
12101 var label = this.el.select('label', true).first();
12102 var icon = this.el.select('i.fa-star', true).first();
12107 if (Roo.bootstrap.version == 3) {
12108 this.el.addClass(this.validClass);
12110 this.inputEl().addClass('is-valid');
12114 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12116 var feedback = this.el.select('.form-control-feedback', true).first();
12119 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12120 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12125 this.fireEvent('valid', this);
12129 * Mark this field as invalid
12130 * @param {String} msg The validation message
12132 markInvalid : function(msg)
12134 if(!this.el || this.preventMark){ // not rendered
12138 this.el.removeClass([this.invalidClass, this.validClass]);
12139 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12141 var feedback = this.el.select('.form-control-feedback', true).first();
12144 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12147 if(this.disabled || this.allowBlank){
12151 var label = this.el.select('label', true).first();
12152 var icon = this.el.select('i.fa-star', true).first();
12154 if(!this.getValue().length && label && !icon){
12155 this.el.createChild({
12157 cls : 'text-danger fa fa-lg fa-star',
12158 tooltip : 'This field is required',
12159 style : 'margin-right:5px;'
12163 if (Roo.bootstrap.version == 3) {
12164 this.el.addClass(this.invalidClass);
12166 this.inputEl().addClass('is-invalid');
12169 // fixme ... this may be depricated need to test..
12170 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12172 var feedback = this.el.select('.form-control-feedback', true).first();
12175 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12177 if(this.getValue().length || this.forceFeedback){
12178 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12185 this.fireEvent('invalid', this, msg);
12193 * trigger field - base class for combo..
12198 * @class Roo.bootstrap.TriggerField
12199 * @extends Roo.bootstrap.Input
12200 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12201 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12202 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12203 * for which you can provide a custom implementation. For example:
12205 var trigger = new Roo.bootstrap.TriggerField();
12206 trigger.onTriggerClick = myTriggerFn;
12207 trigger.applyTo('my-field');
12210 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12211 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12212 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12213 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12214 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12217 * Create a new TriggerField.
12218 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12219 * to the base TextField)
12221 Roo.bootstrap.TriggerField = function(config){
12222 this.mimicing = false;
12223 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12226 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12228 * @cfg {String} triggerClass A CSS class to apply to the trigger
12231 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12236 * @cfg {Boolean} removable (true|false) special filter default false
12240 /** @cfg {Boolean} grow @hide */
12241 /** @cfg {Number} growMin @hide */
12242 /** @cfg {Number} growMax @hide */
12248 autoSize: Roo.emptyFn,
12252 deferHeight : true,
12255 actionMode : 'wrap',
12260 getAutoCreate : function(){
12262 var align = this.labelAlign || this.parentLabelAlign();
12267 cls: 'form-group' //input-group
12274 type : this.inputType,
12275 cls : 'form-control',
12276 autocomplete: 'new-password',
12277 placeholder : this.placeholder || ''
12281 input.name = this.name;
12284 input.cls += ' input-' + this.size;
12287 if (this.disabled) {
12288 input.disabled=true;
12291 var inputblock = input;
12293 if(this.hasFeedback && !this.allowBlank){
12297 cls: 'glyphicon form-control-feedback'
12300 if(this.removable && !this.editable ){
12302 cls : 'has-feedback',
12308 cls : 'roo-combo-removable-btn close'
12315 cls : 'has-feedback',
12324 if(this.removable && !this.editable ){
12326 cls : 'roo-removable',
12332 cls : 'roo-combo-removable-btn close'
12339 if (this.before || this.after) {
12342 cls : 'input-group',
12346 inputblock.cn.push({
12348 cls : 'input-group-addon input-group-prepend input-group-text',
12353 inputblock.cn.push(input);
12355 if(this.hasFeedback && !this.allowBlank){
12356 inputblock.cls += ' has-feedback';
12357 inputblock.cn.push(feedback);
12361 inputblock.cn.push({
12363 cls : 'input-group-addon input-group-append input-group-text',
12372 var ibwrap = inputblock;
12377 cls: 'roo-select2-choices',
12381 cls: 'roo-select2-search-field',
12393 cls: 'roo-select2-container input-group',
12398 cls: 'form-hidden-field'
12404 if(!this.multiple && this.showToggleBtn){
12410 if (this.caret != false) {
12413 cls: 'fa fa-' + this.caret
12420 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12422 Roo.bootstrap.version == 3 ? caret : '',
12425 cls: 'combobox-clear',
12439 combobox.cls += ' roo-select2-container-multi';
12443 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12444 tooltip : 'This field is required'
12446 if (Roo.bootstrap.version == 4) {
12449 style : 'display:none'
12454 if (align ==='left' && this.fieldLabel.length) {
12456 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12463 cls : 'control-label',
12464 html : this.fieldLabel
12476 var labelCfg = cfg.cn[1];
12477 var contentCfg = cfg.cn[2];
12479 if(this.indicatorpos == 'right'){
12484 cls : 'control-label',
12488 html : this.fieldLabel
12502 labelCfg = cfg.cn[0];
12503 contentCfg = cfg.cn[1];
12506 if(this.labelWidth > 12){
12507 labelCfg.style = "width: " + this.labelWidth + 'px';
12510 if(this.labelWidth < 13 && this.labelmd == 0){
12511 this.labelmd = this.labelWidth;
12514 if(this.labellg > 0){
12515 labelCfg.cls += ' col-lg-' + this.labellg;
12516 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12519 if(this.labelmd > 0){
12520 labelCfg.cls += ' col-md-' + this.labelmd;
12521 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12524 if(this.labelsm > 0){
12525 labelCfg.cls += ' col-sm-' + this.labelsm;
12526 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12529 if(this.labelxs > 0){
12530 labelCfg.cls += ' col-xs-' + this.labelxs;
12531 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12534 } else if ( this.fieldLabel.length) {
12535 // Roo.log(" label");
12540 //cls : 'input-group-addon',
12541 html : this.fieldLabel
12549 if(this.indicatorpos == 'right'){
12557 html : this.fieldLabel
12571 // Roo.log(" no label && no align");
12578 ['xs','sm','md','lg'].map(function(size){
12579 if (settings[size]) {
12580 cfg.cls += ' col-' + size + '-' + settings[size];
12591 onResize : function(w, h){
12592 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12593 // if(typeof w == 'number'){
12594 // var x = w - this.trigger.getWidth();
12595 // this.inputEl().setWidth(this.adjustWidth('input', x));
12596 // this.trigger.setStyle('left', x+'px');
12601 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12604 getResizeEl : function(){
12605 return this.inputEl();
12609 getPositionEl : function(){
12610 return this.inputEl();
12614 alignErrorIcon : function(){
12615 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12619 initEvents : function(){
12623 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12624 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12625 if(!this.multiple && this.showToggleBtn){
12626 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12627 if(this.hideTrigger){
12628 this.trigger.setDisplayed(false);
12630 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12634 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12637 if(this.removable && !this.editable && !this.tickable){
12638 var close = this.closeTriggerEl();
12641 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12642 close.on('click', this.removeBtnClick, this, close);
12646 //this.trigger.addClassOnOver('x-form-trigger-over');
12647 //this.trigger.addClassOnClick('x-form-trigger-click');
12650 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12654 closeTriggerEl : function()
12656 var close = this.el.select('.roo-combo-removable-btn', true).first();
12657 return close ? close : false;
12660 removeBtnClick : function(e, h, el)
12662 e.preventDefault();
12664 if(this.fireEvent("remove", this) !== false){
12666 this.fireEvent("afterremove", this)
12670 createList : function()
12672 this.list = Roo.get(document.body).createChild({
12673 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12674 cls: 'typeahead typeahead-long dropdown-menu shadow',
12675 style: 'display:none'
12678 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12683 initTrigger : function(){
12688 onDestroy : function(){
12690 this.trigger.removeAllListeners();
12691 // this.trigger.remove();
12694 // this.wrap.remove();
12696 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12700 onFocus : function(){
12701 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12703 if(!this.mimicing){
12704 this.wrap.addClass('x-trigger-wrap-focus');
12705 this.mimicing = true;
12706 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12707 if(this.monitorTab){
12708 this.el.on("keydown", this.checkTab, this);
12715 checkTab : function(e){
12716 if(e.getKey() == e.TAB){
12717 this.triggerBlur();
12722 onBlur : function(){
12727 mimicBlur : function(e, t){
12729 if(!this.wrap.contains(t) && this.validateBlur()){
12730 this.triggerBlur();
12736 triggerBlur : function(){
12737 this.mimicing = false;
12738 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12739 if(this.monitorTab){
12740 this.el.un("keydown", this.checkTab, this);
12742 //this.wrap.removeClass('x-trigger-wrap-focus');
12743 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12747 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12748 validateBlur : function(e, t){
12753 onDisable : function(){
12754 this.inputEl().dom.disabled = true;
12755 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12757 // this.wrap.addClass('x-item-disabled');
12762 onEnable : function(){
12763 this.inputEl().dom.disabled = false;
12764 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12766 // this.el.removeClass('x-item-disabled');
12771 onShow : function(){
12772 var ae = this.getActionEl();
12775 ae.dom.style.display = '';
12776 ae.dom.style.visibility = 'visible';
12782 onHide : function(){
12783 var ae = this.getActionEl();
12784 ae.dom.style.display = 'none';
12788 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12789 * by an implementing function.
12791 * @param {EventObject} e
12793 onTriggerClick : Roo.emptyFn
12801 * @class Roo.bootstrap.CardUploader
12802 * @extends Roo.bootstrap.Button
12803 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12804 * @cfg {Number} errorTimeout default 3000
12805 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12806 * @cfg {Array} html The button text.
12810 * Create a new CardUploader
12811 * @param {Object} config The config object
12814 Roo.bootstrap.CardUploader = function(config){
12818 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12821 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12829 * When a image is clicked on - and needs to display a slideshow or similar..
12830 * @param {Roo.bootstrap.Card} this
12831 * @param {Object} The image information data
12837 * When a the download link is clicked
12838 * @param {Roo.bootstrap.Card} this
12839 * @param {Object} The image information data contains
12846 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12849 errorTimeout : 3000,
12853 fileCollection : false,
12856 getAutoCreate : function()
12860 cls :'form-group' ,
12865 //cls : 'input-group-addon',
12866 html : this.fieldLabel
12874 value : this.value,
12875 cls : 'd-none form-control'
12880 multiple : 'multiple',
12882 cls : 'd-none roo-card-upload-selector'
12886 cls : 'roo-card-uploader-button-container w-100 mb-2'
12889 cls : 'card-columns roo-card-uploader-container'
12899 getChildContainer : function() /// what children are added to.
12901 return this.containerEl;
12904 getButtonContainer : function() /// what children are added to.
12906 return this.el.select(".roo-card-uploader-button-container").first();
12909 initEvents : function()
12912 Roo.bootstrap.Input.prototype.initEvents.call(this);
12916 xns: Roo.bootstrap,
12919 container_method : 'getButtonContainer' ,
12920 html : this.html, // fix changable?
12923 'click' : function(btn, e) {
12932 this.urlAPI = (window.createObjectURL && window) ||
12933 (window.URL && URL.revokeObjectURL && URL) ||
12934 (window.webkitURL && webkitURL);
12939 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12941 this.selectorEl.on('change', this.onFileSelected, this);
12944 this.images.forEach(function(img) {
12947 this.images = false;
12949 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12955 onClick : function(e)
12957 e.preventDefault();
12959 this.selectorEl.dom.click();
12963 onFileSelected : function(e)
12965 e.preventDefault();
12967 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12971 Roo.each(this.selectorEl.dom.files, function(file){
12972 this.addFile(file);
12981 addFile : function(file)
12984 if(typeof(file) === 'string'){
12985 throw "Add file by name?"; // should not happen
12989 if(!file || !this.urlAPI){
12999 var url = _this.urlAPI.createObjectURL( file);
13002 id : Roo.bootstrap.CardUploader.ID--,
13003 is_uploaded : false,
13007 mimetype : file.type,
13015 * addCard - add an Attachment to the uploader
13016 * @param data - the data about the image to upload
13020 title : "Title of file",
13021 is_uploaded : false,
13022 src : "http://.....",
13023 srcfile : { the File upload object },
13024 mimetype : file.type,
13027 .. any other data...
13033 addCard : function (data)
13035 // hidden input element?
13036 // if the file is not an image...
13037 //then we need to use something other that and header_image
13042 xns : Roo.bootstrap,
13043 xtype : 'CardFooter',
13046 xns : Roo.bootstrap,
13052 xns : Roo.bootstrap,
13054 html : String.format("<small>{0}</small>", data.title),
13055 cls : 'col-10 text-left',
13060 click : function() {
13062 t.fireEvent( "download", t, data );
13068 xns : Roo.bootstrap,
13070 style: 'max-height: 28px; ',
13076 click : function() {
13077 t.removeCard(data.id)
13089 var cn = this.addxtype(
13092 xns : Roo.bootstrap,
13095 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13096 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13097 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13102 initEvents : function() {
13103 Roo.bootstrap.Card.prototype.initEvents.call(this);
13105 this.imgEl = this.el.select('.card-img-top').first();
13107 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13108 this.imgEl.set({ 'pointer' : 'cursor' });
13111 this.getCardFooter().addClass('p-1');
13118 // dont' really need ot update items.
13119 // this.items.push(cn);
13120 this.fileCollection.add(cn);
13122 if (!data.srcfile) {
13123 this.updateInput();
13128 var reader = new FileReader();
13129 reader.addEventListener("load", function() {
13130 data.srcdata = reader.result;
13133 reader.readAsDataURL(data.srcfile);
13138 removeCard : function(id)
13141 var card = this.fileCollection.get(id);
13142 card.data.is_deleted = 1;
13143 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13144 //this.fileCollection.remove(card);
13145 //this.items = this.items.filter(function(e) { return e != card });
13146 // dont' really need ot update items.
13147 card.el.dom.parentNode.removeChild(card.el.dom);
13148 this.updateInput();
13154 this.fileCollection.each(function(card) {
13155 if (card.el.dom && card.el.dom.parentNode) {
13156 card.el.dom.parentNode.removeChild(card.el.dom);
13159 this.fileCollection.clear();
13160 this.updateInput();
13163 updateInput : function()
13166 this.fileCollection.each(function(e) {
13170 this.inputEl().dom.value = JSON.stringify(data);
13180 Roo.bootstrap.CardUploader.ID = -1;/*
13182 * Ext JS Library 1.1.1
13183 * Copyright(c) 2006-2007, Ext JS, LLC.
13185 * Originally Released Under LGPL - original licence link has changed is not relivant.
13188 * <script type="text/javascript">
13193 * @class Roo.data.SortTypes
13195 * Defines the default sorting (casting?) comparison functions used when sorting data.
13197 Roo.data.SortTypes = {
13199 * Default sort that does nothing
13200 * @param {Mixed} s The value being converted
13201 * @return {Mixed} The comparison value
13203 none : function(s){
13208 * The regular expression used to strip tags
13212 stripTagsRE : /<\/?[^>]+>/gi,
13215 * Strips all HTML tags to sort on text only
13216 * @param {Mixed} s The value being converted
13217 * @return {String} The comparison value
13219 asText : function(s){
13220 return String(s).replace(this.stripTagsRE, "");
13224 * Strips all HTML tags to sort on text only - Case insensitive
13225 * @param {Mixed} s The value being converted
13226 * @return {String} The comparison value
13228 asUCText : function(s){
13229 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13233 * Case insensitive string
13234 * @param {Mixed} s The value being converted
13235 * @return {String} The comparison value
13237 asUCString : function(s) {
13238 return String(s).toUpperCase();
13243 * @param {Mixed} s The value being converted
13244 * @return {Number} The comparison value
13246 asDate : function(s) {
13250 if(s instanceof Date){
13251 return s.getTime();
13253 return Date.parse(String(s));
13258 * @param {Mixed} s The value being converted
13259 * @return {Float} The comparison value
13261 asFloat : function(s) {
13262 var val = parseFloat(String(s).replace(/,/g, ""));
13271 * @param {Mixed} s The value being converted
13272 * @return {Number} The comparison value
13274 asInt : function(s) {
13275 var val = parseInt(String(s).replace(/,/g, ""));
13283 * Ext JS Library 1.1.1
13284 * Copyright(c) 2006-2007, Ext JS, LLC.
13286 * Originally Released Under LGPL - original licence link has changed is not relivant.
13289 * <script type="text/javascript">
13293 * @class Roo.data.Record
13294 * Instances of this class encapsulate both record <em>definition</em> information, and record
13295 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13296 * to access Records cached in an {@link Roo.data.Store} object.<br>
13298 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13299 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13302 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13304 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13305 * {@link #create}. The parameters are the same.
13306 * @param {Array} data An associative Array of data values keyed by the field name.
13307 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13308 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13309 * not specified an integer id is generated.
13311 Roo.data.Record = function(data, id){
13312 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13317 * Generate a constructor for a specific record layout.
13318 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13319 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13320 * Each field definition object may contain the following properties: <ul>
13321 * <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,
13322 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13323 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13324 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13325 * is being used, then this is a string containing the javascript expression to reference the data relative to
13326 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13327 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13328 * this may be omitted.</p></li>
13329 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13330 * <ul><li>auto (Default, implies no conversion)</li>
13335 * <li>date</li></ul></p></li>
13336 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13337 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13338 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13339 * by the Reader into an object that will be stored in the Record. It is passed the
13340 * following parameters:<ul>
13341 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13343 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13345 * <br>usage:<br><pre><code>
13346 var TopicRecord = Roo.data.Record.create(
13347 {name: 'title', mapping: 'topic_title'},
13348 {name: 'author', mapping: 'username'},
13349 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13350 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13351 {name: 'lastPoster', mapping: 'user2'},
13352 {name: 'excerpt', mapping: 'post_text'}
13355 var myNewRecord = new TopicRecord({
13356 title: 'Do my job please',
13359 lastPost: new Date(),
13360 lastPoster: 'Animal',
13361 excerpt: 'No way dude!'
13363 myStore.add(myNewRecord);
13368 Roo.data.Record.create = function(o){
13369 var f = function(){
13370 f.superclass.constructor.apply(this, arguments);
13372 Roo.extend(f, Roo.data.Record);
13373 var p = f.prototype;
13374 p.fields = new Roo.util.MixedCollection(false, function(field){
13377 for(var i = 0, len = o.length; i < len; i++){
13378 p.fields.add(new Roo.data.Field(o[i]));
13380 f.getField = function(name){
13381 return p.fields.get(name);
13386 Roo.data.Record.AUTO_ID = 1000;
13387 Roo.data.Record.EDIT = 'edit';
13388 Roo.data.Record.REJECT = 'reject';
13389 Roo.data.Record.COMMIT = 'commit';
13391 Roo.data.Record.prototype = {
13393 * Readonly flag - true if this record has been modified.
13402 join : function(store){
13403 this.store = store;
13407 * Set the named field to the specified value.
13408 * @param {String} name The name of the field to set.
13409 * @param {Object} value The value to set the field to.
13411 set : function(name, value){
13412 if(this.data[name] == value){
13416 if(!this.modified){
13417 this.modified = {};
13419 if(typeof this.modified[name] == 'undefined'){
13420 this.modified[name] = this.data[name];
13422 this.data[name] = value;
13423 if(!this.editing && this.store){
13424 this.store.afterEdit(this);
13429 * Get the value of the named field.
13430 * @param {String} name The name of the field to get the value of.
13431 * @return {Object} The value of the field.
13433 get : function(name){
13434 return this.data[name];
13438 beginEdit : function(){
13439 this.editing = true;
13440 this.modified = {};
13444 cancelEdit : function(){
13445 this.editing = false;
13446 delete this.modified;
13450 endEdit : function(){
13451 this.editing = false;
13452 if(this.dirty && this.store){
13453 this.store.afterEdit(this);
13458 * Usually called by the {@link Roo.data.Store} which owns the Record.
13459 * Rejects all changes made to the Record since either creation, or the last commit operation.
13460 * Modified fields are reverted to their original values.
13462 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13463 * of reject operations.
13465 reject : function(){
13466 var m = this.modified;
13468 if(typeof m[n] != "function"){
13469 this.data[n] = m[n];
13472 this.dirty = false;
13473 delete this.modified;
13474 this.editing = false;
13476 this.store.afterReject(this);
13481 * Usually called by the {@link Roo.data.Store} which owns the Record.
13482 * Commits all changes made to the Record since either creation, or the last commit operation.
13484 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13485 * of commit operations.
13487 commit : function(){
13488 this.dirty = false;
13489 delete this.modified;
13490 this.editing = false;
13492 this.store.afterCommit(this);
13497 hasError : function(){
13498 return this.error != null;
13502 clearError : function(){
13507 * Creates a copy of this record.
13508 * @param {String} id (optional) A new record id if you don't want to use this record's id
13511 copy : function(newId) {
13512 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13516 * Ext JS Library 1.1.1
13517 * Copyright(c) 2006-2007, Ext JS, LLC.
13519 * Originally Released Under LGPL - original licence link has changed is not relivant.
13522 * <script type="text/javascript">
13528 * @class Roo.data.Store
13529 * @extends Roo.util.Observable
13530 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13531 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13533 * 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
13534 * has no knowledge of the format of the data returned by the Proxy.<br>
13536 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13537 * instances from the data object. These records are cached and made available through accessor functions.
13539 * Creates a new Store.
13540 * @param {Object} config A config object containing the objects needed for the Store to access data,
13541 * and read the data into Records.
13543 Roo.data.Store = function(config){
13544 this.data = new Roo.util.MixedCollection(false);
13545 this.data.getKey = function(o){
13548 this.baseParams = {};
13550 this.paramNames = {
13555 "multisort" : "_multisort"
13558 if(config && config.data){
13559 this.inlineData = config.data;
13560 delete config.data;
13563 Roo.apply(this, config);
13565 if(this.reader){ // reader passed
13566 this.reader = Roo.factory(this.reader, Roo.data);
13567 this.reader.xmodule = this.xmodule || false;
13568 if(!this.recordType){
13569 this.recordType = this.reader.recordType;
13571 if(this.reader.onMetaChange){
13572 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13576 if(this.recordType){
13577 this.fields = this.recordType.prototype.fields;
13579 this.modified = [];
13583 * @event datachanged
13584 * Fires when the data cache has changed, and a widget which is using this Store
13585 * as a Record cache should refresh its view.
13586 * @param {Store} this
13588 datachanged : true,
13590 * @event metachange
13591 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13592 * @param {Store} this
13593 * @param {Object} meta The JSON metadata
13598 * Fires when Records have been added to the Store
13599 * @param {Store} this
13600 * @param {Roo.data.Record[]} records The array of Records added
13601 * @param {Number} index The index at which the record(s) were added
13606 * Fires when a Record has been removed from the Store
13607 * @param {Store} this
13608 * @param {Roo.data.Record} record The Record that was removed
13609 * @param {Number} index The index at which the record was removed
13614 * Fires when a Record has been updated
13615 * @param {Store} this
13616 * @param {Roo.data.Record} record The Record that was updated
13617 * @param {String} operation The update operation being performed. Value may be one of:
13619 Roo.data.Record.EDIT
13620 Roo.data.Record.REJECT
13621 Roo.data.Record.COMMIT
13627 * Fires when the data cache has been cleared.
13628 * @param {Store} this
13632 * @event beforeload
13633 * Fires before a request is made for a new data object. If the beforeload handler returns false
13634 * the load action will be canceled.
13635 * @param {Store} this
13636 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13640 * @event beforeloadadd
13641 * Fires after a new set of Records has been loaded.
13642 * @param {Store} this
13643 * @param {Roo.data.Record[]} records The Records that were loaded
13644 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13646 beforeloadadd : true,
13649 * Fires after a new set of Records has been loaded, before they are added to the store.
13650 * @param {Store} this
13651 * @param {Roo.data.Record[]} records The Records that were loaded
13652 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13653 * @params {Object} return from reader
13657 * @event loadexception
13658 * Fires if an exception occurs in the Proxy during loading.
13659 * Called with the signature of the Proxy's "loadexception" event.
13660 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13663 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13664 * @param {Object} load options
13665 * @param {Object} jsonData from your request (normally this contains the Exception)
13667 loadexception : true
13671 this.proxy = Roo.factory(this.proxy, Roo.data);
13672 this.proxy.xmodule = this.xmodule || false;
13673 this.relayEvents(this.proxy, ["loadexception"]);
13675 this.sortToggle = {};
13676 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13678 Roo.data.Store.superclass.constructor.call(this);
13680 if(this.inlineData){
13681 this.loadData(this.inlineData);
13682 delete this.inlineData;
13686 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13688 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13689 * without a remote query - used by combo/forms at present.
13693 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13696 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13699 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13700 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13703 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13704 * on any HTTP request
13707 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13710 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13714 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13715 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13717 remoteSort : false,
13720 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13721 * loaded or when a record is removed. (defaults to false).
13723 pruneModifiedRecords : false,
13726 lastOptions : null,
13729 * Add Records to the Store and fires the add event.
13730 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13732 add : function(records){
13733 records = [].concat(records);
13734 for(var i = 0, len = records.length; i < len; i++){
13735 records[i].join(this);
13737 var index = this.data.length;
13738 this.data.addAll(records);
13739 this.fireEvent("add", this, records, index);
13743 * Remove a Record from the Store and fires the remove event.
13744 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13746 remove : function(record){
13747 var index = this.data.indexOf(record);
13748 this.data.removeAt(index);
13750 if(this.pruneModifiedRecords){
13751 this.modified.remove(record);
13753 this.fireEvent("remove", this, record, index);
13757 * Remove all Records from the Store and fires the clear event.
13759 removeAll : function(){
13761 if(this.pruneModifiedRecords){
13762 this.modified = [];
13764 this.fireEvent("clear", this);
13768 * Inserts Records to the Store at the given index and fires the add event.
13769 * @param {Number} index The start index at which to insert the passed Records.
13770 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13772 insert : function(index, records){
13773 records = [].concat(records);
13774 for(var i = 0, len = records.length; i < len; i++){
13775 this.data.insert(index, records[i]);
13776 records[i].join(this);
13778 this.fireEvent("add", this, records, index);
13782 * Get the index within the cache of the passed Record.
13783 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13784 * @return {Number} The index of the passed Record. Returns -1 if not found.
13786 indexOf : function(record){
13787 return this.data.indexOf(record);
13791 * Get the index within the cache of the Record with the passed id.
13792 * @param {String} id The id of the Record to find.
13793 * @return {Number} The index of the Record. Returns -1 if not found.
13795 indexOfId : function(id){
13796 return this.data.indexOfKey(id);
13800 * Get the Record with the specified id.
13801 * @param {String} id The id of the Record to find.
13802 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13804 getById : function(id){
13805 return this.data.key(id);
13809 * Get the Record at the specified index.
13810 * @param {Number} index The index of the Record to find.
13811 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13813 getAt : function(index){
13814 return this.data.itemAt(index);
13818 * Returns a range of Records between specified indices.
13819 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13820 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13821 * @return {Roo.data.Record[]} An array of Records
13823 getRange : function(start, end){
13824 return this.data.getRange(start, end);
13828 storeOptions : function(o){
13829 o = Roo.apply({}, o);
13832 this.lastOptions = o;
13836 * Loads the Record cache from the configured Proxy using the configured Reader.
13838 * If using remote paging, then the first load call must specify the <em>start</em>
13839 * and <em>limit</em> properties in the options.params property to establish the initial
13840 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13842 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13843 * and this call will return before the new data has been loaded. Perform any post-processing
13844 * in a callback function, or in a "load" event handler.</strong>
13846 * @param {Object} options An object containing properties which control loading options:<ul>
13847 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13848 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13849 * passed the following arguments:<ul>
13850 * <li>r : Roo.data.Record[]</li>
13851 * <li>options: Options object from the load call</li>
13852 * <li>success: Boolean success indicator</li></ul></li>
13853 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13854 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13857 load : function(options){
13858 options = options || {};
13859 if(this.fireEvent("beforeload", this, options) !== false){
13860 this.storeOptions(options);
13861 var p = Roo.apply(options.params || {}, this.baseParams);
13862 // if meta was not loaded from remote source.. try requesting it.
13863 if (!this.reader.metaFromRemote) {
13864 p._requestMeta = 1;
13866 if(this.sortInfo && this.remoteSort){
13867 var pn = this.paramNames;
13868 p[pn["sort"]] = this.sortInfo.field;
13869 p[pn["dir"]] = this.sortInfo.direction;
13871 if (this.multiSort) {
13872 var pn = this.paramNames;
13873 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13876 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13881 * Reloads the Record cache from the configured Proxy using the configured Reader and
13882 * the options from the last load operation performed.
13883 * @param {Object} options (optional) An object containing properties which may override the options
13884 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13885 * the most recently used options are reused).
13887 reload : function(options){
13888 this.load(Roo.applyIf(options||{}, this.lastOptions));
13892 // Called as a callback by the Reader during a load operation.
13893 loadRecords : function(o, options, success){
13894 if(!o || success === false){
13895 if(success !== false){
13896 this.fireEvent("load", this, [], options, o);
13898 if(options.callback){
13899 options.callback.call(options.scope || this, [], options, false);
13903 // if data returned failure - throw an exception.
13904 if (o.success === false) {
13905 // show a message if no listener is registered.
13906 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13907 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13909 // loadmask wil be hooked into this..
13910 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13913 var r = o.records, t = o.totalRecords || r.length;
13915 this.fireEvent("beforeloadadd", this, r, options, o);
13917 if(!options || options.add !== true){
13918 if(this.pruneModifiedRecords){
13919 this.modified = [];
13921 for(var i = 0, len = r.length; i < len; i++){
13925 this.data = this.snapshot;
13926 delete this.snapshot;
13929 this.data.addAll(r);
13930 this.totalLength = t;
13932 this.fireEvent("datachanged", this);
13934 this.totalLength = Math.max(t, this.data.length+r.length);
13938 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13940 var e = new Roo.data.Record({});
13942 e.set(this.parent.displayField, this.parent.emptyTitle);
13943 e.set(this.parent.valueField, '');
13948 this.fireEvent("load", this, r, options, o);
13949 if(options.callback){
13950 options.callback.call(options.scope || this, r, options, true);
13956 * Loads data from a passed data block. A Reader which understands the format of the data
13957 * must have been configured in the constructor.
13958 * @param {Object} data The data block from which to read the Records. The format of the data expected
13959 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13960 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13962 loadData : function(o, append){
13963 var r = this.reader.readRecords(o);
13964 this.loadRecords(r, {add: append}, true);
13968 * using 'cn' the nested child reader read the child array into it's child stores.
13969 * @param {Object} rec The record with a 'children array
13971 loadDataFromChildren : function(rec)
13973 this.loadData(this.reader.toLoadData(rec));
13978 * Gets the number of cached records.
13980 * <em>If using paging, this may not be the total size of the dataset. If the data object
13981 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13982 * the data set size</em>
13984 getCount : function(){
13985 return this.data.length || 0;
13989 * Gets the total number of records in the dataset as returned by the server.
13991 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13992 * the dataset size</em>
13994 getTotalCount : function(){
13995 return this.totalLength || 0;
13999 * Returns the sort state of the Store as an object with two properties:
14001 field {String} The name of the field by which the Records are sorted
14002 direction {String} The sort order, "ASC" or "DESC"
14005 getSortState : function(){
14006 return this.sortInfo;
14010 applySort : function(){
14011 if(this.sortInfo && !this.remoteSort){
14012 var s = this.sortInfo, f = s.field;
14013 var st = this.fields.get(f).sortType;
14014 var fn = function(r1, r2){
14015 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14016 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14018 this.data.sort(s.direction, fn);
14019 if(this.snapshot && this.snapshot != this.data){
14020 this.snapshot.sort(s.direction, fn);
14026 * Sets the default sort column and order to be used by the next load operation.
14027 * @param {String} fieldName The name of the field to sort by.
14028 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14030 setDefaultSort : function(field, dir){
14031 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14035 * Sort the Records.
14036 * If remote sorting is used, the sort is performed on the server, and the cache is
14037 * reloaded. If local sorting is used, the cache is sorted internally.
14038 * @param {String} fieldName The name of the field to sort by.
14039 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14041 sort : function(fieldName, dir){
14042 var f = this.fields.get(fieldName);
14044 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14046 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14047 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14052 this.sortToggle[f.name] = dir;
14053 this.sortInfo = {field: f.name, direction: dir};
14054 if(!this.remoteSort){
14056 this.fireEvent("datachanged", this);
14058 this.load(this.lastOptions);
14063 * Calls the specified function for each of the Records in the cache.
14064 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14065 * Returning <em>false</em> aborts and exits the iteration.
14066 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14068 each : function(fn, scope){
14069 this.data.each(fn, scope);
14073 * Gets all records modified since the last commit. Modified records are persisted across load operations
14074 * (e.g., during paging).
14075 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14077 getModifiedRecords : function(){
14078 return this.modified;
14082 createFilterFn : function(property, value, anyMatch){
14083 if(!value.exec){ // not a regex
14084 value = String(value);
14085 if(value.length == 0){
14088 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14090 return function(r){
14091 return value.test(r.data[property]);
14096 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14097 * @param {String} property A field on your records
14098 * @param {Number} start The record index to start at (defaults to 0)
14099 * @param {Number} end The last record index to include (defaults to length - 1)
14100 * @return {Number} The sum
14102 sum : function(property, start, end){
14103 var rs = this.data.items, v = 0;
14104 start = start || 0;
14105 end = (end || end === 0) ? end : rs.length-1;
14107 for(var i = start; i <= end; i++){
14108 v += (rs[i].data[property] || 0);
14114 * Filter the records by a specified property.
14115 * @param {String} field A field on your records
14116 * @param {String/RegExp} value Either a string that the field
14117 * should start with or a RegExp to test against the field
14118 * @param {Boolean} anyMatch True to match any part not just the beginning
14120 filter : function(property, value, anyMatch){
14121 var fn = this.createFilterFn(property, value, anyMatch);
14122 return fn ? this.filterBy(fn) : this.clearFilter();
14126 * Filter by a function. The specified function will be called with each
14127 * record in this data source. If the function returns true the record is included,
14128 * otherwise it is filtered.
14129 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14130 * @param {Object} scope (optional) The scope of the function (defaults to this)
14132 filterBy : function(fn, scope){
14133 this.snapshot = this.snapshot || this.data;
14134 this.data = this.queryBy(fn, scope||this);
14135 this.fireEvent("datachanged", this);
14139 * Query the records by a specified property.
14140 * @param {String} field A field on your records
14141 * @param {String/RegExp} value Either a string that the field
14142 * should start with or a RegExp to test against the field
14143 * @param {Boolean} anyMatch True to match any part not just the beginning
14144 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14146 query : function(property, value, anyMatch){
14147 var fn = this.createFilterFn(property, value, anyMatch);
14148 return fn ? this.queryBy(fn) : this.data.clone();
14152 * Query by a function. The specified function will be called with each
14153 * record in this data source. If the function returns true the record is included
14155 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14156 * @param {Object} scope (optional) The scope of the function (defaults to this)
14157 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14159 queryBy : function(fn, scope){
14160 var data = this.snapshot || this.data;
14161 return data.filterBy(fn, scope||this);
14165 * Collects unique values for a particular dataIndex from this store.
14166 * @param {String} dataIndex The property to collect
14167 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14168 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14169 * @return {Array} An array of the unique values
14171 collect : function(dataIndex, allowNull, bypassFilter){
14172 var d = (bypassFilter === true && this.snapshot) ?
14173 this.snapshot.items : this.data.items;
14174 var v, sv, r = [], l = {};
14175 for(var i = 0, len = d.length; i < len; i++){
14176 v = d[i].data[dataIndex];
14178 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14187 * Revert to a view of the Record cache with no filtering applied.
14188 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14190 clearFilter : function(suppressEvent){
14191 if(this.snapshot && this.snapshot != this.data){
14192 this.data = this.snapshot;
14193 delete this.snapshot;
14194 if(suppressEvent !== true){
14195 this.fireEvent("datachanged", this);
14201 afterEdit : function(record){
14202 if(this.modified.indexOf(record) == -1){
14203 this.modified.push(record);
14205 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14209 afterReject : function(record){
14210 this.modified.remove(record);
14211 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14215 afterCommit : function(record){
14216 this.modified.remove(record);
14217 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14221 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14222 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14224 commitChanges : function(){
14225 var m = this.modified.slice(0);
14226 this.modified = [];
14227 for(var i = 0, len = m.length; i < len; i++){
14233 * Cancel outstanding changes on all changed records.
14235 rejectChanges : function(){
14236 var m = this.modified.slice(0);
14237 this.modified = [];
14238 for(var i = 0, len = m.length; i < len; i++){
14243 onMetaChange : function(meta, rtype, o){
14244 this.recordType = rtype;
14245 this.fields = rtype.prototype.fields;
14246 delete this.snapshot;
14247 this.sortInfo = meta.sortInfo || this.sortInfo;
14248 this.modified = [];
14249 this.fireEvent('metachange', this, this.reader.meta);
14252 moveIndex : function(data, type)
14254 var index = this.indexOf(data);
14256 var newIndex = index + type;
14260 this.insert(newIndex, data);
14265 * Ext JS Library 1.1.1
14266 * Copyright(c) 2006-2007, Ext JS, LLC.
14268 * Originally Released Under LGPL - original licence link has changed is not relivant.
14271 * <script type="text/javascript">
14275 * @class Roo.data.SimpleStore
14276 * @extends Roo.data.Store
14277 * Small helper class to make creating Stores from Array data easier.
14278 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14279 * @cfg {Array} fields An array of field definition objects, or field name strings.
14280 * @cfg {Object} an existing reader (eg. copied from another store)
14281 * @cfg {Array} data The multi-dimensional array of data
14283 * @param {Object} config
14285 Roo.data.SimpleStore = function(config)
14287 Roo.data.SimpleStore.superclass.constructor.call(this, {
14289 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14292 Roo.data.Record.create(config.fields)
14294 proxy : new Roo.data.MemoryProxy(config.data)
14298 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14300 * Ext JS Library 1.1.1
14301 * Copyright(c) 2006-2007, Ext JS, LLC.
14303 * Originally Released Under LGPL - original licence link has changed is not relivant.
14306 * <script type="text/javascript">
14311 * @extends Roo.data.Store
14312 * @class Roo.data.JsonStore
14313 * Small helper class to make creating Stores for JSON data easier. <br/>
14315 var store = new Roo.data.JsonStore({
14316 url: 'get-images.php',
14318 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14321 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14322 * JsonReader and HttpProxy (unless inline data is provided).</b>
14323 * @cfg {Array} fields An array of field definition objects, or field name strings.
14325 * @param {Object} config
14327 Roo.data.JsonStore = function(c){
14328 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14329 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14330 reader: new Roo.data.JsonReader(c, c.fields)
14333 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14335 * Ext JS Library 1.1.1
14336 * Copyright(c) 2006-2007, Ext JS, LLC.
14338 * Originally Released Under LGPL - original licence link has changed is not relivant.
14341 * <script type="text/javascript">
14345 Roo.data.Field = function(config){
14346 if(typeof config == "string"){
14347 config = {name: config};
14349 Roo.apply(this, config);
14352 this.type = "auto";
14355 var st = Roo.data.SortTypes;
14356 // named sortTypes are supported, here we look them up
14357 if(typeof this.sortType == "string"){
14358 this.sortType = st[this.sortType];
14361 // set default sortType for strings and dates
14362 if(!this.sortType){
14365 this.sortType = st.asUCString;
14368 this.sortType = st.asDate;
14371 this.sortType = st.none;
14376 var stripRe = /[\$,%]/g;
14378 // prebuilt conversion function for this field, instead of
14379 // switching every time we're reading a value
14381 var cv, dateFormat = this.dateFormat;
14386 cv = function(v){ return v; };
14389 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14393 return v !== undefined && v !== null && v !== '' ?
14394 parseInt(String(v).replace(stripRe, ""), 10) : '';
14399 return v !== undefined && v !== null && v !== '' ?
14400 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14405 cv = function(v){ return v === true || v === "true" || v == 1; };
14412 if(v instanceof Date){
14416 if(dateFormat == "timestamp"){
14417 return new Date(v*1000);
14419 return Date.parseDate(v, dateFormat);
14421 var parsed = Date.parse(v);
14422 return parsed ? new Date(parsed) : null;
14431 Roo.data.Field.prototype = {
14439 * Ext JS Library 1.1.1
14440 * Copyright(c) 2006-2007, Ext JS, LLC.
14442 * Originally Released Under LGPL - original licence link has changed is not relivant.
14445 * <script type="text/javascript">
14448 // Base class for reading structured data from a data source. This class is intended to be
14449 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14452 * @class Roo.data.DataReader
14453 * Base class for reading structured data from a data source. This class is intended to be
14454 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14457 Roo.data.DataReader = function(meta, recordType){
14461 this.recordType = recordType instanceof Array ?
14462 Roo.data.Record.create(recordType) : recordType;
14465 Roo.data.DataReader.prototype = {
14468 readerType : 'Data',
14470 * Create an empty record
14471 * @param {Object} data (optional) - overlay some values
14472 * @return {Roo.data.Record} record created.
14474 newRow : function(d) {
14476 this.recordType.prototype.fields.each(function(c) {
14478 case 'int' : da[c.name] = 0; break;
14479 case 'date' : da[c.name] = new Date(); break;
14480 case 'float' : da[c.name] = 0.0; break;
14481 case 'boolean' : da[c.name] = false; break;
14482 default : da[c.name] = ""; break;
14486 return new this.recordType(Roo.apply(da, d));
14492 * Ext JS Library 1.1.1
14493 * Copyright(c) 2006-2007, Ext JS, LLC.
14495 * Originally Released Under LGPL - original licence link has changed is not relivant.
14498 * <script type="text/javascript">
14502 * @class Roo.data.DataProxy
14503 * @extends Roo.data.Observable
14504 * This class is an abstract base class for implementations which provide retrieval of
14505 * unformatted data objects.<br>
14507 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14508 * (of the appropriate type which knows how to parse the data object) to provide a block of
14509 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14511 * Custom implementations must implement the load method as described in
14512 * {@link Roo.data.HttpProxy#load}.
14514 Roo.data.DataProxy = function(){
14517 * @event beforeload
14518 * Fires before a network request is made to retrieve a data object.
14519 * @param {Object} This DataProxy object.
14520 * @param {Object} params The params parameter to the load function.
14525 * Fires before the load method's callback is called.
14526 * @param {Object} This DataProxy object.
14527 * @param {Object} o The data object.
14528 * @param {Object} arg The callback argument object passed to the load function.
14532 * @event loadexception
14533 * Fires if an Exception occurs during data retrieval.
14534 * @param {Object} This DataProxy object.
14535 * @param {Object} o The data object.
14536 * @param {Object} arg The callback argument object passed to the load function.
14537 * @param {Object} e The Exception.
14539 loadexception : true
14541 Roo.data.DataProxy.superclass.constructor.call(this);
14544 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14547 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14551 * Ext JS Library 1.1.1
14552 * Copyright(c) 2006-2007, Ext JS, LLC.
14554 * Originally Released Under LGPL - original licence link has changed is not relivant.
14557 * <script type="text/javascript">
14560 * @class Roo.data.MemoryProxy
14561 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14562 * to the Reader when its load method is called.
14564 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14566 Roo.data.MemoryProxy = function(data){
14570 Roo.data.MemoryProxy.superclass.constructor.call(this);
14574 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14577 * Load data from the requested source (in this case an in-memory
14578 * data object passed to the constructor), read the data object into
14579 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14580 * process that block using the passed callback.
14581 * @param {Object} params This parameter is not used by the MemoryProxy class.
14582 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14583 * object into a block of Roo.data.Records.
14584 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14585 * The function must be passed <ul>
14586 * <li>The Record block object</li>
14587 * <li>The "arg" argument from the load function</li>
14588 * <li>A boolean success indicator</li>
14590 * @param {Object} scope The scope in which to call the callback
14591 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14593 load : function(params, reader, callback, scope, arg){
14594 params = params || {};
14597 result = reader.readRecords(params.data ? params.data :this.data);
14599 this.fireEvent("loadexception", this, arg, null, e);
14600 callback.call(scope, null, arg, false);
14603 callback.call(scope, result, arg, true);
14607 update : function(params, records){
14612 * Ext JS Library 1.1.1
14613 * Copyright(c) 2006-2007, Ext JS, LLC.
14615 * Originally Released Under LGPL - original licence link has changed is not relivant.
14618 * <script type="text/javascript">
14621 * @class Roo.data.HttpProxy
14622 * @extends Roo.data.DataProxy
14623 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14624 * configured to reference a certain URL.<br><br>
14626 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14627 * from which the running page was served.<br><br>
14629 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14631 * Be aware that to enable the browser to parse an XML document, the server must set
14632 * the Content-Type header in the HTTP response to "text/xml".
14634 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14635 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14636 * will be used to make the request.
14638 Roo.data.HttpProxy = function(conn){
14639 Roo.data.HttpProxy.superclass.constructor.call(this);
14640 // is conn a conn config or a real conn?
14642 this.useAjax = !conn || !conn.events;
14646 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14647 // thse are take from connection...
14650 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14653 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14654 * extra parameters to each request made by this object. (defaults to undefined)
14657 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14658 * to each request made by this object. (defaults to undefined)
14661 * @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)
14664 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14667 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14673 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14677 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14678 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14679 * a finer-grained basis than the DataProxy events.
14681 getConnection : function(){
14682 return this.useAjax ? Roo.Ajax : this.conn;
14686 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14687 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14688 * process that block using the passed callback.
14689 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14690 * for the request to the remote server.
14691 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14692 * object into a block of Roo.data.Records.
14693 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14694 * The function must be passed <ul>
14695 * <li>The Record block object</li>
14696 * <li>The "arg" argument from the load function</li>
14697 * <li>A boolean success indicator</li>
14699 * @param {Object} scope The scope in which to call the callback
14700 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14702 load : function(params, reader, callback, scope, arg){
14703 if(this.fireEvent("beforeload", this, params) !== false){
14705 params : params || {},
14707 callback : callback,
14712 callback : this.loadResponse,
14716 Roo.applyIf(o, this.conn);
14717 if(this.activeRequest){
14718 Roo.Ajax.abort(this.activeRequest);
14720 this.activeRequest = Roo.Ajax.request(o);
14722 this.conn.request(o);
14725 callback.call(scope||this, null, arg, false);
14730 loadResponse : function(o, success, response){
14731 delete this.activeRequest;
14733 this.fireEvent("loadexception", this, o, response);
14734 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14739 result = o.reader.read(response);
14741 this.fireEvent("loadexception", this, o, response, e);
14742 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14746 this.fireEvent("load", this, o, o.request.arg);
14747 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14751 update : function(dataSet){
14756 updateResponse : function(dataSet){
14761 * Ext JS Library 1.1.1
14762 * Copyright(c) 2006-2007, Ext JS, LLC.
14764 * Originally Released Under LGPL - original licence link has changed is not relivant.
14767 * <script type="text/javascript">
14771 * @class Roo.data.ScriptTagProxy
14772 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14773 * other than the originating domain of the running page.<br><br>
14775 * <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
14776 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14778 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14779 * source code that is used as the source inside a <script> tag.<br><br>
14781 * In order for the browser to process the returned data, the server must wrap the data object
14782 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14783 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14784 * depending on whether the callback name was passed:
14787 boolean scriptTag = false;
14788 String cb = request.getParameter("callback");
14791 response.setContentType("text/javascript");
14793 response.setContentType("application/x-json");
14795 Writer out = response.getWriter();
14797 out.write(cb + "(");
14799 out.print(dataBlock.toJsonString());
14806 * @param {Object} config A configuration object.
14808 Roo.data.ScriptTagProxy = function(config){
14809 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14810 Roo.apply(this, config);
14811 this.head = document.getElementsByTagName("head")[0];
14814 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14816 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14818 * @cfg {String} url The URL from which to request the data object.
14821 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14825 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14826 * the server the name of the callback function set up by the load call to process the returned data object.
14827 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14828 * javascript output which calls this named function passing the data object as its only parameter.
14830 callbackParam : "callback",
14832 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14833 * name to the request.
14838 * Load data from the configured URL, read the data object into
14839 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14840 * process that block using the passed callback.
14841 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14842 * for the request to the remote server.
14843 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14844 * object into a block of Roo.data.Records.
14845 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14846 * The function must be passed <ul>
14847 * <li>The Record block object</li>
14848 * <li>The "arg" argument from the load function</li>
14849 * <li>A boolean success indicator</li>
14851 * @param {Object} scope The scope in which to call the callback
14852 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14854 load : function(params, reader, callback, scope, arg){
14855 if(this.fireEvent("beforeload", this, params) !== false){
14857 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14859 var url = this.url;
14860 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14862 url += "&_dc=" + (new Date().getTime());
14864 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14867 cb : "stcCallback"+transId,
14868 scriptId : "stcScript"+transId,
14872 callback : callback,
14878 window[trans.cb] = function(o){
14879 conn.handleResponse(o, trans);
14882 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14884 if(this.autoAbort !== false){
14888 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14890 var script = document.createElement("script");
14891 script.setAttribute("src", url);
14892 script.setAttribute("type", "text/javascript");
14893 script.setAttribute("id", trans.scriptId);
14894 this.head.appendChild(script);
14896 this.trans = trans;
14898 callback.call(scope||this, null, arg, false);
14903 isLoading : function(){
14904 return this.trans ? true : false;
14908 * Abort the current server request.
14910 abort : function(){
14911 if(this.isLoading()){
14912 this.destroyTrans(this.trans);
14917 destroyTrans : function(trans, isLoaded){
14918 this.head.removeChild(document.getElementById(trans.scriptId));
14919 clearTimeout(trans.timeoutId);
14921 window[trans.cb] = undefined;
14923 delete window[trans.cb];
14926 // if hasn't been loaded, wait for load to remove it to prevent script error
14927 window[trans.cb] = function(){
14928 window[trans.cb] = undefined;
14930 delete window[trans.cb];
14937 handleResponse : function(o, trans){
14938 this.trans = false;
14939 this.destroyTrans(trans, true);
14942 result = trans.reader.readRecords(o);
14944 this.fireEvent("loadexception", this, o, trans.arg, e);
14945 trans.callback.call(trans.scope||window, null, trans.arg, false);
14948 this.fireEvent("load", this, o, trans.arg);
14949 trans.callback.call(trans.scope||window, result, trans.arg, true);
14953 handleFailure : function(trans){
14954 this.trans = false;
14955 this.destroyTrans(trans, false);
14956 this.fireEvent("loadexception", this, null, trans.arg);
14957 trans.callback.call(trans.scope||window, null, trans.arg, false);
14961 * Ext JS Library 1.1.1
14962 * Copyright(c) 2006-2007, Ext JS, LLC.
14964 * Originally Released Under LGPL - original licence link has changed is not relivant.
14967 * <script type="text/javascript">
14971 * @class Roo.data.JsonReader
14972 * @extends Roo.data.DataReader
14973 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14974 * based on mappings in a provided Roo.data.Record constructor.
14976 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14977 * in the reply previously.
14982 var RecordDef = Roo.data.Record.create([
14983 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14984 {name: 'occupation'} // This field will use "occupation" as the mapping.
14986 var myReader = new Roo.data.JsonReader({
14987 totalProperty: "results", // The property which contains the total dataset size (optional)
14988 root: "rows", // The property which contains an Array of row objects
14989 id: "id" // The property within each row object that provides an ID for the record (optional)
14993 * This would consume a JSON file like this:
14995 { 'results': 2, 'rows': [
14996 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14997 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15000 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15001 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15002 * paged from the remote server.
15003 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15004 * @cfg {String} root name of the property which contains the Array of row objects.
15005 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15006 * @cfg {Array} fields Array of field definition objects
15008 * Create a new JsonReader
15009 * @param {Object} meta Metadata configuration options
15010 * @param {Object} recordType Either an Array of field definition objects,
15011 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15013 Roo.data.JsonReader = function(meta, recordType){
15016 // set some defaults:
15017 Roo.applyIf(meta, {
15018 totalProperty: 'total',
15019 successProperty : 'success',
15024 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15026 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15028 readerType : 'Json',
15031 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15032 * Used by Store query builder to append _requestMeta to params.
15035 metaFromRemote : false,
15037 * This method is only used by a DataProxy which has retrieved data from a remote server.
15038 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15039 * @return {Object} data A data block which is used by an Roo.data.Store object as
15040 * a cache of Roo.data.Records.
15042 read : function(response){
15043 var json = response.responseText;
15045 var o = /* eval:var:o */ eval("("+json+")");
15047 throw {message: "JsonReader.read: Json object not found"};
15053 this.metaFromRemote = true;
15054 this.meta = o.metaData;
15055 this.recordType = Roo.data.Record.create(o.metaData.fields);
15056 this.onMetaChange(this.meta, this.recordType, o);
15058 return this.readRecords(o);
15061 // private function a store will implement
15062 onMetaChange : function(meta, recordType, o){
15069 simpleAccess: function(obj, subsc) {
15076 getJsonAccessor: function(){
15078 return function(expr) {
15080 return(re.test(expr))
15081 ? new Function("obj", "return obj." + expr)
15086 return Roo.emptyFn;
15091 * Create a data block containing Roo.data.Records from an XML document.
15092 * @param {Object} o An object which contains an Array of row objects in the property specified
15093 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15094 * which contains the total size of the dataset.
15095 * @return {Object} data A data block which is used by an Roo.data.Store object as
15096 * a cache of Roo.data.Records.
15098 readRecords : function(o){
15100 * After any data loads, the raw JSON data is available for further custom processing.
15104 var s = this.meta, Record = this.recordType,
15105 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15107 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15109 if(s.totalProperty) {
15110 this.getTotal = this.getJsonAccessor(s.totalProperty);
15112 if(s.successProperty) {
15113 this.getSuccess = this.getJsonAccessor(s.successProperty);
15115 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15117 var g = this.getJsonAccessor(s.id);
15118 this.getId = function(rec) {
15120 return (r === undefined || r === "") ? null : r;
15123 this.getId = function(){return null;};
15126 for(var jj = 0; jj < fl; jj++){
15128 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15129 this.ef[jj] = this.getJsonAccessor(map);
15133 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15134 if(s.totalProperty){
15135 var vt = parseInt(this.getTotal(o), 10);
15140 if(s.successProperty){
15141 var vs = this.getSuccess(o);
15142 if(vs === false || vs === 'false'){
15147 for(var i = 0; i < c; i++){
15150 var id = this.getId(n);
15151 for(var j = 0; j < fl; j++){
15153 var v = this.ef[j](n);
15155 Roo.log('missing convert for ' + f.name);
15159 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15161 var record = new Record(values, id);
15163 records[i] = record;
15169 totalRecords : totalRecords
15172 // used when loading children.. @see loadDataFromChildren
15173 toLoadData: function(rec)
15175 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15176 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15177 return { data : data, total : data.length };
15182 * Ext JS Library 1.1.1
15183 * Copyright(c) 2006-2007, Ext JS, LLC.
15185 * Originally Released Under LGPL - original licence link has changed is not relivant.
15188 * <script type="text/javascript">
15192 * @class Roo.data.ArrayReader
15193 * @extends Roo.data.DataReader
15194 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15195 * Each element of that Array represents a row of data fields. The
15196 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15197 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15201 var RecordDef = Roo.data.Record.create([
15202 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15203 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15205 var myReader = new Roo.data.ArrayReader({
15206 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15210 * This would consume an Array like this:
15212 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15216 * Create a new JsonReader
15217 * @param {Object} meta Metadata configuration options.
15218 * @param {Object|Array} recordType Either an Array of field definition objects
15220 * @cfg {Array} fields Array of field definition objects
15221 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15222 * as specified to {@link Roo.data.Record#create},
15223 * or an {@link Roo.data.Record} object
15226 * created using {@link Roo.data.Record#create}.
15228 Roo.data.ArrayReader = function(meta, recordType)
15230 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15233 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15236 * Create a data block containing Roo.data.Records from an XML document.
15237 * @param {Object} o An Array of row objects which represents the dataset.
15238 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15239 * a cache of Roo.data.Records.
15241 readRecords : function(o)
15243 var sid = this.meta ? this.meta.id : null;
15244 var recordType = this.recordType, fields = recordType.prototype.fields;
15247 for(var i = 0; i < root.length; i++){
15250 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15251 for(var j = 0, jlen = fields.length; j < jlen; j++){
15252 var f = fields.items[j];
15253 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15254 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15256 values[f.name] = v;
15258 var record = new recordType(values, id);
15260 records[records.length] = record;
15264 totalRecords : records.length
15267 // used when loading children.. @see loadDataFromChildren
15268 toLoadData: function(rec)
15270 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15271 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15282 * @class Roo.bootstrap.ComboBox
15283 * @extends Roo.bootstrap.TriggerField
15284 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15285 * @cfg {Boolean} append (true|false) default false
15286 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15287 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15288 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15289 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15290 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15291 * @cfg {Boolean} animate default true
15292 * @cfg {Boolean} emptyResultText only for touch device
15293 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15294 * @cfg {String} emptyTitle default ''
15295 * @cfg {Number} width fixed with? experimental
15297 * Create a new ComboBox.
15298 * @param {Object} config Configuration options
15300 Roo.bootstrap.ComboBox = function(config){
15301 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15305 * Fires when the dropdown list is expanded
15306 * @param {Roo.bootstrap.ComboBox} combo This combo box
15311 * Fires when the dropdown list is collapsed
15312 * @param {Roo.bootstrap.ComboBox} combo This combo box
15316 * @event beforeselect
15317 * Fires before a list item is selected. Return false to cancel the selection.
15318 * @param {Roo.bootstrap.ComboBox} combo This combo box
15319 * @param {Roo.data.Record} record The data record returned from the underlying store
15320 * @param {Number} index The index of the selected item in the dropdown list
15322 'beforeselect' : true,
15325 * Fires when a list item is selected
15326 * @param {Roo.bootstrap.ComboBox} combo This combo box
15327 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15328 * @param {Number} index The index of the selected item in the dropdown list
15332 * @event beforequery
15333 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15334 * The event object passed has these properties:
15335 * @param {Roo.bootstrap.ComboBox} combo This combo box
15336 * @param {String} query The query
15337 * @param {Boolean} forceAll true to force "all" query
15338 * @param {Boolean} cancel true to cancel the query
15339 * @param {Object} e The query event object
15341 'beforequery': true,
15344 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15345 * @param {Roo.bootstrap.ComboBox} combo This combo box
15350 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15351 * @param {Roo.bootstrap.ComboBox} combo This combo box
15352 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15357 * Fires when the remove value from the combobox array
15358 * @param {Roo.bootstrap.ComboBox} combo This combo box
15362 * @event afterremove
15363 * Fires when the remove value from the combobox array
15364 * @param {Roo.bootstrap.ComboBox} combo This combo box
15366 'afterremove' : true,
15368 * @event specialfilter
15369 * Fires when specialfilter
15370 * @param {Roo.bootstrap.ComboBox} combo This combo box
15372 'specialfilter' : true,
15375 * Fires when tick the element
15376 * @param {Roo.bootstrap.ComboBox} combo This combo box
15380 * @event touchviewdisplay
15381 * Fires when touch view require special display (default is using displayField)
15382 * @param {Roo.bootstrap.ComboBox} combo This combo box
15383 * @param {Object} cfg set html .
15385 'touchviewdisplay' : true
15390 this.tickItems = [];
15392 this.selectedIndex = -1;
15393 if(this.mode == 'local'){
15394 if(config.queryDelay === undefined){
15395 this.queryDelay = 10;
15397 if(config.minChars === undefined){
15403 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15406 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15407 * rendering into an Roo.Editor, defaults to false)
15410 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15411 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15414 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15417 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15418 * the dropdown list (defaults to undefined, with no header element)
15422 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15426 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15428 listWidth: undefined,
15430 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15431 * mode = 'remote' or 'text' if mode = 'local')
15433 displayField: undefined,
15436 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15437 * mode = 'remote' or 'value' if mode = 'local').
15438 * Note: use of a valueField requires the user make a selection
15439 * in order for a value to be mapped.
15441 valueField: undefined,
15443 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15448 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15449 * field's data value (defaults to the underlying DOM element's name)
15451 hiddenName: undefined,
15453 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15457 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15459 selectedClass: 'active',
15462 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15466 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15467 * anchor positions (defaults to 'tl-bl')
15469 listAlign: 'tl-bl?',
15471 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15475 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15476 * query specified by the allQuery config option (defaults to 'query')
15478 triggerAction: 'query',
15480 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15481 * (defaults to 4, does not apply if editable = false)
15485 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15486 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15490 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15491 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15495 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15496 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15500 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15501 * when editable = true (defaults to false)
15503 selectOnFocus:false,
15505 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15507 queryParam: 'query',
15509 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15510 * when mode = 'remote' (defaults to 'Loading...')
15512 loadingText: 'Loading...',
15514 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15518 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15522 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15523 * traditional select (defaults to true)
15527 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15531 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15535 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15536 * listWidth has a higher value)
15540 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15541 * allow the user to set arbitrary text into the field (defaults to false)
15543 forceSelection:false,
15545 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15546 * if typeAhead = true (defaults to 250)
15548 typeAheadDelay : 250,
15550 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15551 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15553 valueNotFoundText : undefined,
15555 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15557 blockFocus : false,
15560 * @cfg {Boolean} disableClear Disable showing of clear button.
15562 disableClear : false,
15564 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15566 alwaysQuery : false,
15569 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15574 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15576 invalidClass : "has-warning",
15579 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15581 validClass : "has-success",
15584 * @cfg {Boolean} specialFilter (true|false) special filter default false
15586 specialFilter : false,
15589 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15591 mobileTouchView : true,
15594 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15596 useNativeIOS : false,
15599 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15601 mobile_restrict_height : false,
15603 ios_options : false,
15615 btnPosition : 'right',
15616 triggerList : true,
15617 showToggleBtn : true,
15619 emptyResultText: 'Empty',
15620 triggerText : 'Select',
15624 // element that contains real text value.. (when hidden is used..)
15626 getAutoCreate : function()
15631 * Render classic select for iso
15634 if(Roo.isIOS && this.useNativeIOS){
15635 cfg = this.getAutoCreateNativeIOS();
15643 if(Roo.isTouch && this.mobileTouchView){
15644 cfg = this.getAutoCreateTouchView();
15651 if(!this.tickable){
15652 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15657 * ComboBox with tickable selections
15660 var align = this.labelAlign || this.parentLabelAlign();
15663 cls : 'form-group roo-combobox-tickable' //input-group
15666 var btn_text_select = '';
15667 var btn_text_done = '';
15668 var btn_text_cancel = '';
15670 if (this.btn_text_show) {
15671 btn_text_select = 'Select';
15672 btn_text_done = 'Done';
15673 btn_text_cancel = 'Cancel';
15678 cls : 'tickable-buttons',
15683 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15684 //html : this.triggerText
15685 html: btn_text_select
15691 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15693 html: btn_text_done
15699 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15701 html: btn_text_cancel
15707 buttons.cn.unshift({
15709 cls: 'roo-select2-search-field-input'
15715 Roo.each(buttons.cn, function(c){
15717 c.cls += ' btn-' + _this.size;
15720 if (_this.disabled) {
15727 style : 'display: contents',
15732 cls: 'form-hidden-field'
15736 cls: 'roo-select2-choices',
15740 cls: 'roo-select2-search-field',
15751 cls: 'roo-select2-container input-group roo-select2-container-multi',
15757 // cls: 'typeahead typeahead-long dropdown-menu',
15758 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15763 if(this.hasFeedback && !this.allowBlank){
15767 cls: 'glyphicon form-control-feedback'
15770 combobox.cn.push(feedback);
15777 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15778 tooltip : 'This field is required'
15780 if (Roo.bootstrap.version == 4) {
15783 style : 'display:none'
15786 if (align ==='left' && this.fieldLabel.length) {
15788 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15795 cls : 'control-label col-form-label',
15796 html : this.fieldLabel
15808 var labelCfg = cfg.cn[1];
15809 var contentCfg = cfg.cn[2];
15812 if(this.indicatorpos == 'right'){
15818 cls : 'control-label col-form-label',
15822 html : this.fieldLabel
15838 labelCfg = cfg.cn[0];
15839 contentCfg = cfg.cn[1];
15843 if(this.labelWidth > 12){
15844 labelCfg.style = "width: " + this.labelWidth + 'px';
15846 if(this.width * 1 > 0){
15847 contentCfg.style = "width: " + this.width + 'px';
15849 if(this.labelWidth < 13 && this.labelmd == 0){
15850 this.labelmd = this.labelWidth;
15853 if(this.labellg > 0){
15854 labelCfg.cls += ' col-lg-' + this.labellg;
15855 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15858 if(this.labelmd > 0){
15859 labelCfg.cls += ' col-md-' + this.labelmd;
15860 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15863 if(this.labelsm > 0){
15864 labelCfg.cls += ' col-sm-' + this.labelsm;
15865 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15868 if(this.labelxs > 0){
15869 labelCfg.cls += ' col-xs-' + this.labelxs;
15870 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15874 } else if ( this.fieldLabel.length) {
15875 // Roo.log(" label");
15880 //cls : 'input-group-addon',
15881 html : this.fieldLabel
15886 if(this.indicatorpos == 'right'){
15890 //cls : 'input-group-addon',
15891 html : this.fieldLabel
15901 // Roo.log(" no label && no align");
15908 ['xs','sm','md','lg'].map(function(size){
15909 if (settings[size]) {
15910 cfg.cls += ' col-' + size + '-' + settings[size];
15918 _initEventsCalled : false,
15921 initEvents: function()
15923 if (this._initEventsCalled) { // as we call render... prevent looping...
15926 this._initEventsCalled = true;
15929 throw "can not find store for combo";
15932 this.indicator = this.indicatorEl();
15934 this.store = Roo.factory(this.store, Roo.data);
15935 this.store.parent = this;
15937 // if we are building from html. then this element is so complex, that we can not really
15938 // use the rendered HTML.
15939 // so we have to trash and replace the previous code.
15940 if (Roo.XComponent.build_from_html) {
15941 // remove this element....
15942 var e = this.el.dom, k=0;
15943 while (e ) { e = e.previousSibling; ++k;}
15948 this.rendered = false;
15950 this.render(this.parent().getChildContainer(true), k);
15953 if(Roo.isIOS && this.useNativeIOS){
15954 this.initIOSView();
15962 if(Roo.isTouch && this.mobileTouchView){
15963 this.initTouchView();
15968 this.initTickableEvents();
15972 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15974 if(this.hiddenName){
15976 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15978 this.hiddenField.dom.value =
15979 this.hiddenValue !== undefined ? this.hiddenValue :
15980 this.value !== undefined ? this.value : '';
15982 // prevent input submission
15983 this.el.dom.removeAttribute('name');
15984 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15989 // this.el.dom.setAttribute('autocomplete', 'off');
15992 var cls = 'x-combo-list';
15994 //this.list = new Roo.Layer({
15995 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16001 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16002 _this.list.setWidth(lw);
16005 this.list.on('mouseover', this.onViewOver, this);
16006 this.list.on('mousemove', this.onViewMove, this);
16007 this.list.on('scroll', this.onViewScroll, this);
16010 this.list.swallowEvent('mousewheel');
16011 this.assetHeight = 0;
16014 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16015 this.assetHeight += this.header.getHeight();
16018 this.innerList = this.list.createChild({cls:cls+'-inner'});
16019 this.innerList.on('mouseover', this.onViewOver, this);
16020 this.innerList.on('mousemove', this.onViewMove, this);
16021 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16023 if(this.allowBlank && !this.pageSize && !this.disableClear){
16024 this.footer = this.list.createChild({cls:cls+'-ft'});
16025 this.pageTb = new Roo.Toolbar(this.footer);
16029 this.footer = this.list.createChild({cls:cls+'-ft'});
16030 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16031 {pageSize: this.pageSize});
16035 if (this.pageTb && this.allowBlank && !this.disableClear) {
16037 this.pageTb.add(new Roo.Toolbar.Fill(), {
16038 cls: 'x-btn-icon x-btn-clear',
16040 handler: function()
16043 _this.clearValue();
16044 _this.onSelect(false, -1);
16049 this.assetHeight += this.footer.getHeight();
16054 this.tpl = Roo.bootstrap.version == 4 ?
16055 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16056 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16059 this.view = new Roo.View(this.list, this.tpl, {
16060 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16062 //this.view.wrapEl.setDisplayed(false);
16063 this.view.on('click', this.onViewClick, this);
16066 this.store.on('beforeload', this.onBeforeLoad, this);
16067 this.store.on('load', this.onLoad, this);
16068 this.store.on('loadexception', this.onLoadException, this);
16070 if(this.resizable){
16071 this.resizer = new Roo.Resizable(this.list, {
16072 pinned:true, handles:'se'
16074 this.resizer.on('resize', function(r, w, h){
16075 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16076 this.listWidth = w;
16077 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16078 this.restrictHeight();
16080 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16083 if(!this.editable){
16084 this.editable = true;
16085 this.setEditable(false);
16090 if (typeof(this.events.add.listeners) != 'undefined') {
16092 this.addicon = this.wrap.createChild(
16093 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16095 this.addicon.on('click', function(e) {
16096 this.fireEvent('add', this);
16099 if (typeof(this.events.edit.listeners) != 'undefined') {
16101 this.editicon = this.wrap.createChild(
16102 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16103 if (this.addicon) {
16104 this.editicon.setStyle('margin-left', '40px');
16106 this.editicon.on('click', function(e) {
16108 // we fire even if inothing is selected..
16109 this.fireEvent('edit', this, this.lastData );
16115 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16116 "up" : function(e){
16117 this.inKeyMode = true;
16121 "down" : function(e){
16122 if(!this.isExpanded()){
16123 this.onTriggerClick();
16125 this.inKeyMode = true;
16130 "enter" : function(e){
16131 // this.onViewClick();
16135 if(this.fireEvent("specialkey", this, e)){
16136 this.onViewClick(false);
16142 "esc" : function(e){
16146 "tab" : function(e){
16149 if(this.fireEvent("specialkey", this, e)){
16150 this.onViewClick(false);
16158 doRelay : function(foo, bar, hname){
16159 if(hname == 'down' || this.scope.isExpanded()){
16160 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16169 this.queryDelay = Math.max(this.queryDelay || 10,
16170 this.mode == 'local' ? 10 : 250);
16173 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16175 if(this.typeAhead){
16176 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16178 if(this.editable !== false){
16179 this.inputEl().on("keyup", this.onKeyUp, this);
16181 if(this.forceSelection){
16182 this.inputEl().on('blur', this.doForce, this);
16186 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16187 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16191 initTickableEvents: function()
16195 if(this.hiddenName){
16197 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16199 this.hiddenField.dom.value =
16200 this.hiddenValue !== undefined ? this.hiddenValue :
16201 this.value !== undefined ? this.value : '';
16203 // prevent input submission
16204 this.el.dom.removeAttribute('name');
16205 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16210 // this.list = this.el.select('ul.dropdown-menu',true).first();
16212 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16213 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16214 if(this.triggerList){
16215 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16218 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16219 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16221 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16222 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16224 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16225 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16227 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16228 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16229 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16232 this.cancelBtn.hide();
16237 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16238 _this.list.setWidth(lw);
16241 this.list.on('mouseover', this.onViewOver, this);
16242 this.list.on('mousemove', this.onViewMove, this);
16244 this.list.on('scroll', this.onViewScroll, this);
16247 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16248 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16251 this.view = new Roo.View(this.list, this.tpl, {
16256 selectedClass: this.selectedClass
16259 //this.view.wrapEl.setDisplayed(false);
16260 this.view.on('click', this.onViewClick, this);
16264 this.store.on('beforeload', this.onBeforeLoad, this);
16265 this.store.on('load', this.onLoad, this);
16266 this.store.on('loadexception', this.onLoadException, this);
16269 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16270 "up" : function(e){
16271 this.inKeyMode = true;
16275 "down" : function(e){
16276 this.inKeyMode = true;
16280 "enter" : function(e){
16281 if(this.fireEvent("specialkey", this, e)){
16282 this.onViewClick(false);
16288 "esc" : function(e){
16289 this.onTickableFooterButtonClick(e, false, false);
16292 "tab" : function(e){
16293 this.fireEvent("specialkey", this, e);
16295 this.onTickableFooterButtonClick(e, false, false);
16302 doRelay : function(e, fn, key){
16303 if(this.scope.isExpanded()){
16304 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16313 this.queryDelay = Math.max(this.queryDelay || 10,
16314 this.mode == 'local' ? 10 : 250);
16317 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16319 if(this.typeAhead){
16320 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16323 if(this.editable !== false){
16324 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16327 this.indicator = this.indicatorEl();
16329 if(this.indicator){
16330 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16331 this.indicator.hide();
16336 onDestroy : function(){
16338 this.view.setStore(null);
16339 this.view.el.removeAllListeners();
16340 this.view.el.remove();
16341 this.view.purgeListeners();
16344 this.list.dom.innerHTML = '';
16348 this.store.un('beforeload', this.onBeforeLoad, this);
16349 this.store.un('load', this.onLoad, this);
16350 this.store.un('loadexception', this.onLoadException, this);
16352 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16356 fireKey : function(e){
16357 if(e.isNavKeyPress() && !this.list.isVisible()){
16358 this.fireEvent("specialkey", this, e);
16363 onResize: function(w, h)
16367 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16369 // if(typeof w != 'number'){
16370 // // we do not handle it!?!?
16373 // var tw = this.trigger.getWidth();
16374 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16375 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16377 // this.inputEl().setWidth( this.adjustWidth('input', x));
16379 // //this.trigger.setStyle('left', x+'px');
16381 // if(this.list && this.listWidth === undefined){
16382 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16383 // this.list.setWidth(lw);
16384 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16392 * Allow or prevent the user from directly editing the field text. If false is passed,
16393 * the user will only be able to select from the items defined in the dropdown list. This method
16394 * is the runtime equivalent of setting the 'editable' config option at config time.
16395 * @param {Boolean} value True to allow the user to directly edit the field text
16397 setEditable : function(value){
16398 if(value == this.editable){
16401 this.editable = value;
16403 this.inputEl().dom.setAttribute('readOnly', true);
16404 this.inputEl().on('mousedown', this.onTriggerClick, this);
16405 this.inputEl().addClass('x-combo-noedit');
16407 this.inputEl().dom.removeAttribute('readOnly');
16408 this.inputEl().un('mousedown', this.onTriggerClick, this);
16409 this.inputEl().removeClass('x-combo-noedit');
16415 onBeforeLoad : function(combo,opts){
16416 if(!this.hasFocus){
16420 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16422 this.restrictHeight();
16423 this.selectedIndex = -1;
16427 onLoad : function(){
16429 this.hasQuery = false;
16431 if(!this.hasFocus){
16435 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16436 this.loading.hide();
16439 if(this.store.getCount() > 0){
16442 this.restrictHeight();
16443 if(this.lastQuery == this.allQuery){
16444 if(this.editable && !this.tickable){
16445 this.inputEl().dom.select();
16449 !this.selectByValue(this.value, true) &&
16452 !this.store.lastOptions ||
16453 typeof(this.store.lastOptions.add) == 'undefined' ||
16454 this.store.lastOptions.add != true
16457 this.select(0, true);
16460 if(this.autoFocus){
16463 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16464 this.taTask.delay(this.typeAheadDelay);
16468 this.onEmptyResults();
16474 onLoadException : function()
16476 this.hasQuery = false;
16478 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16479 this.loading.hide();
16482 if(this.tickable && this.editable){
16487 // only causes errors at present
16488 //Roo.log(this.store.reader.jsonData);
16489 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16491 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16497 onTypeAhead : function(){
16498 if(this.store.getCount() > 0){
16499 var r = this.store.getAt(0);
16500 var newValue = r.data[this.displayField];
16501 var len = newValue.length;
16502 var selStart = this.getRawValue().length;
16504 if(selStart != len){
16505 this.setRawValue(newValue);
16506 this.selectText(selStart, newValue.length);
16512 onSelect : function(record, index){
16514 if(this.fireEvent('beforeselect', this, record, index) !== false){
16516 this.setFromData(index > -1 ? record.data : false);
16519 this.fireEvent('select', this, record, index);
16524 * Returns the currently selected field value or empty string if no value is set.
16525 * @return {String} value The selected value
16527 getValue : function()
16529 if(Roo.isIOS && this.useNativeIOS){
16530 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16534 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16537 if(this.valueField){
16538 return typeof this.value != 'undefined' ? this.value : '';
16540 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16544 getRawValue : function()
16546 if(Roo.isIOS && this.useNativeIOS){
16547 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16550 var v = this.inputEl().getValue();
16556 * Clears any text/value currently set in the field
16558 clearValue : function(){
16560 if(this.hiddenField){
16561 this.hiddenField.dom.value = '';
16564 this.setRawValue('');
16565 this.lastSelectionText = '';
16566 this.lastData = false;
16568 var close = this.closeTriggerEl();
16579 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16580 * will be displayed in the field. If the value does not match the data value of an existing item,
16581 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16582 * Otherwise the field will be blank (although the value will still be set).
16583 * @param {String} value The value to match
16585 setValue : function(v)
16587 if(Roo.isIOS && this.useNativeIOS){
16588 this.setIOSValue(v);
16598 if(this.valueField){
16599 var r = this.findRecord(this.valueField, v);
16601 text = r.data[this.displayField];
16602 }else if(this.valueNotFoundText !== undefined){
16603 text = this.valueNotFoundText;
16606 this.lastSelectionText = text;
16607 if(this.hiddenField){
16608 this.hiddenField.dom.value = v;
16610 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16613 var close = this.closeTriggerEl();
16616 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16622 * @property {Object} the last set data for the element
16627 * Sets the value of the field based on a object which is related to the record format for the store.
16628 * @param {Object} value the value to set as. or false on reset?
16630 setFromData : function(o){
16637 var dv = ''; // display value
16638 var vv = ''; // value value..
16640 if (this.displayField) {
16641 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16643 // this is an error condition!!!
16644 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16647 if(this.valueField){
16648 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16651 var close = this.closeTriggerEl();
16654 if(dv.length || vv * 1 > 0){
16656 this.blockFocus=true;
16662 if(this.hiddenField){
16663 this.hiddenField.dom.value = vv;
16665 this.lastSelectionText = dv;
16666 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16670 // no hidden field.. - we store the value in 'value', but still display
16671 // display field!!!!
16672 this.lastSelectionText = dv;
16673 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16680 reset : function(){
16681 // overridden so that last data is reset..
16688 this.setValue(this.originalValue);
16689 //this.clearInvalid();
16690 this.lastData = false;
16692 this.view.clearSelections();
16698 findRecord : function(prop, value){
16700 if(this.store.getCount() > 0){
16701 this.store.each(function(r){
16702 if(r.data[prop] == value){
16712 getName: function()
16714 // returns hidden if it's set..
16715 if (!this.rendered) {return ''};
16716 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16720 onViewMove : function(e, t){
16721 this.inKeyMode = false;
16725 onViewOver : function(e, t){
16726 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16729 var item = this.view.findItemFromChild(t);
16732 var index = this.view.indexOf(item);
16733 this.select(index, false);
16738 onViewClick : function(view, doFocus, el, e)
16740 var index = this.view.getSelectedIndexes()[0];
16742 var r = this.store.getAt(index);
16746 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16753 Roo.each(this.tickItems, function(v,k){
16755 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16757 _this.tickItems.splice(k, 1);
16759 if(typeof(e) == 'undefined' && view == false){
16760 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16772 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16773 this.tickItems.push(r.data);
16776 if(typeof(e) == 'undefined' && view == false){
16777 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16784 this.onSelect(r, index);
16786 if(doFocus !== false && !this.blockFocus){
16787 this.inputEl().focus();
16792 restrictHeight : function(){
16793 //this.innerList.dom.style.height = '';
16794 //var inner = this.innerList.dom;
16795 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16796 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16797 //this.list.beginUpdate();
16798 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16799 this.list.alignTo(this.inputEl(), this.listAlign);
16800 this.list.alignTo(this.inputEl(), this.listAlign);
16801 //this.list.endUpdate();
16805 onEmptyResults : function(){
16807 if(this.tickable && this.editable){
16808 this.hasFocus = false;
16809 this.restrictHeight();
16817 * Returns true if the dropdown list is expanded, else false.
16819 isExpanded : function(){
16820 return this.list.isVisible();
16824 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16825 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16826 * @param {String} value The data value of the item to select
16827 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16828 * selected item if it is not currently in view (defaults to true)
16829 * @return {Boolean} True if the value matched an item in the list, else false
16831 selectByValue : function(v, scrollIntoView){
16832 if(v !== undefined && v !== null){
16833 var r = this.findRecord(this.valueField || this.displayField, v);
16835 this.select(this.store.indexOf(r), scrollIntoView);
16843 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16844 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16845 * @param {Number} index The zero-based index of the list item to select
16846 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16847 * selected item if it is not currently in view (defaults to true)
16849 select : function(index, scrollIntoView){
16850 this.selectedIndex = index;
16851 this.view.select(index);
16852 if(scrollIntoView !== false){
16853 var el = this.view.getNode(index);
16855 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16858 this.list.scrollChildIntoView(el, false);
16864 selectNext : function(){
16865 var ct = this.store.getCount();
16867 if(this.selectedIndex == -1){
16869 }else if(this.selectedIndex < ct-1){
16870 this.select(this.selectedIndex+1);
16876 selectPrev : function(){
16877 var ct = this.store.getCount();
16879 if(this.selectedIndex == -1){
16881 }else if(this.selectedIndex != 0){
16882 this.select(this.selectedIndex-1);
16888 onKeyUp : function(e){
16889 if(this.editable !== false && !e.isSpecialKey()){
16890 this.lastKey = e.getKey();
16891 this.dqTask.delay(this.queryDelay);
16896 validateBlur : function(){
16897 return !this.list || !this.list.isVisible();
16901 initQuery : function(){
16903 var v = this.getRawValue();
16905 if(this.tickable && this.editable){
16906 v = this.tickableInputEl().getValue();
16913 doForce : function(){
16914 if(this.inputEl().dom.value.length > 0){
16915 this.inputEl().dom.value =
16916 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16922 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16923 * query allowing the query action to be canceled if needed.
16924 * @param {String} query The SQL query to execute
16925 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16926 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16927 * saved in the current store (defaults to false)
16929 doQuery : function(q, forceAll){
16931 if(q === undefined || q === null){
16936 forceAll: forceAll,
16940 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16945 forceAll = qe.forceAll;
16946 if(forceAll === true || (q.length >= this.minChars)){
16948 this.hasQuery = true;
16950 if(this.lastQuery != q || this.alwaysQuery){
16951 this.lastQuery = q;
16952 if(this.mode == 'local'){
16953 this.selectedIndex = -1;
16955 this.store.clearFilter();
16958 if(this.specialFilter){
16959 this.fireEvent('specialfilter', this);
16964 this.store.filter(this.displayField, q);
16967 this.store.fireEvent("datachanged", this.store);
16974 this.store.baseParams[this.queryParam] = q;
16976 var options = {params : this.getParams(q)};
16979 options.add = true;
16980 options.params.start = this.page * this.pageSize;
16983 this.store.load(options);
16986 * this code will make the page width larger, at the beginning, the list not align correctly,
16987 * we should expand the list on onLoad
16988 * so command out it
16993 this.selectedIndex = -1;
16998 this.loadNext = false;
17002 getParams : function(q){
17004 //p[this.queryParam] = q;
17008 p.limit = this.pageSize;
17014 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17016 collapse : function(){
17017 if(!this.isExpanded()){
17023 this.hasFocus = false;
17027 this.cancelBtn.hide();
17028 this.trigger.show();
17031 this.tickableInputEl().dom.value = '';
17032 this.tickableInputEl().blur();
17037 Roo.get(document).un('mousedown', this.collapseIf, this);
17038 Roo.get(document).un('mousewheel', this.collapseIf, this);
17039 if (!this.editable) {
17040 Roo.get(document).un('keydown', this.listKeyPress, this);
17042 this.fireEvent('collapse', this);
17048 collapseIf : function(e){
17049 var in_combo = e.within(this.el);
17050 var in_list = e.within(this.list);
17051 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17053 if (in_combo || in_list || is_list) {
17054 //e.stopPropagation();
17059 this.onTickableFooterButtonClick(e, false, false);
17067 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17069 expand : function(){
17071 if(this.isExpanded() || !this.hasFocus){
17075 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17076 this.list.setWidth(lw);
17082 this.restrictHeight();
17086 this.tickItems = Roo.apply([], this.item);
17089 this.cancelBtn.show();
17090 this.trigger.hide();
17093 this.tickableInputEl().focus();
17098 Roo.get(document).on('mousedown', this.collapseIf, this);
17099 Roo.get(document).on('mousewheel', this.collapseIf, this);
17100 if (!this.editable) {
17101 Roo.get(document).on('keydown', this.listKeyPress, this);
17104 this.fireEvent('expand', this);
17108 // Implements the default empty TriggerField.onTriggerClick function
17109 onTriggerClick : function(e)
17111 Roo.log('trigger click');
17113 if(this.disabled || !this.triggerList){
17118 this.loadNext = false;
17120 if(this.isExpanded()){
17122 if (!this.blockFocus) {
17123 this.inputEl().focus();
17127 this.hasFocus = true;
17128 if(this.triggerAction == 'all') {
17129 this.doQuery(this.allQuery, true);
17131 this.doQuery(this.getRawValue());
17133 if (!this.blockFocus) {
17134 this.inputEl().focus();
17139 onTickableTriggerClick : function(e)
17146 this.loadNext = false;
17147 this.hasFocus = true;
17149 if(this.triggerAction == 'all') {
17150 this.doQuery(this.allQuery, true);
17152 this.doQuery(this.getRawValue());
17156 onSearchFieldClick : function(e)
17158 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17159 this.onTickableFooterButtonClick(e, false, false);
17163 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17168 this.loadNext = false;
17169 this.hasFocus = true;
17171 if(this.triggerAction == 'all') {
17172 this.doQuery(this.allQuery, true);
17174 this.doQuery(this.getRawValue());
17178 listKeyPress : function(e)
17180 //Roo.log('listkeypress');
17181 // scroll to first matching element based on key pres..
17182 if (e.isSpecialKey()) {
17185 var k = String.fromCharCode(e.getKey()).toUpperCase();
17188 var csel = this.view.getSelectedNodes();
17189 var cselitem = false;
17191 var ix = this.view.indexOf(csel[0]);
17192 cselitem = this.store.getAt(ix);
17193 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17199 this.store.each(function(v) {
17201 // start at existing selection.
17202 if (cselitem.id == v.id) {
17208 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17209 match = this.store.indexOf(v);
17215 if (match === false) {
17216 return true; // no more action?
17219 this.view.select(match);
17220 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17221 sn.scrollIntoView(sn.dom.parentNode, false);
17224 onViewScroll : function(e, t){
17226 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){
17230 this.hasQuery = true;
17232 this.loading = this.list.select('.loading', true).first();
17234 if(this.loading === null){
17235 this.list.createChild({
17237 cls: 'loading roo-select2-more-results roo-select2-active',
17238 html: 'Loading more results...'
17241 this.loading = this.list.select('.loading', true).first();
17243 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17245 this.loading.hide();
17248 this.loading.show();
17253 this.loadNext = true;
17255 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17260 addItem : function(o)
17262 var dv = ''; // display value
17264 if (this.displayField) {
17265 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17267 // this is an error condition!!!
17268 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17275 var choice = this.choices.createChild({
17277 cls: 'roo-select2-search-choice',
17286 cls: 'roo-select2-search-choice-close fa fa-times',
17291 }, this.searchField);
17293 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17295 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17303 this.inputEl().dom.value = '';
17308 onRemoveItem : function(e, _self, o)
17310 e.preventDefault();
17312 this.lastItem = Roo.apply([], this.item);
17314 var index = this.item.indexOf(o.data) * 1;
17317 Roo.log('not this item?!');
17321 this.item.splice(index, 1);
17326 this.fireEvent('remove', this, e);
17332 syncValue : function()
17334 if(!this.item.length){
17341 Roo.each(this.item, function(i){
17342 if(_this.valueField){
17343 value.push(i[_this.valueField]);
17350 this.value = value.join(',');
17352 if(this.hiddenField){
17353 this.hiddenField.dom.value = this.value;
17356 this.store.fireEvent("datachanged", this.store);
17361 clearItem : function()
17363 if(!this.multiple){
17369 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17377 if(this.tickable && !Roo.isTouch){
17378 this.view.refresh();
17382 inputEl: function ()
17384 if(Roo.isIOS && this.useNativeIOS){
17385 return this.el.select('select.roo-ios-select', true).first();
17388 if(Roo.isTouch && this.mobileTouchView){
17389 return this.el.select('input.form-control',true).first();
17393 return this.searchField;
17396 return this.el.select('input.form-control',true).first();
17399 onTickableFooterButtonClick : function(e, btn, el)
17401 e.preventDefault();
17403 this.lastItem = Roo.apply([], this.item);
17405 if(btn && btn.name == 'cancel'){
17406 this.tickItems = Roo.apply([], this.item);
17415 Roo.each(this.tickItems, function(o){
17423 validate : function()
17425 if(this.getVisibilityEl().hasClass('hidden')){
17429 var v = this.getRawValue();
17432 v = this.getValue();
17435 if(this.disabled || this.allowBlank || v.length){
17440 this.markInvalid();
17444 tickableInputEl : function()
17446 if(!this.tickable || !this.editable){
17447 return this.inputEl();
17450 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17454 getAutoCreateTouchView : function()
17459 cls: 'form-group' //input-group
17465 type : this.inputType,
17466 cls : 'form-control x-combo-noedit',
17467 autocomplete: 'new-password',
17468 placeholder : this.placeholder || '',
17473 input.name = this.name;
17477 input.cls += ' input-' + this.size;
17480 if (this.disabled) {
17481 input.disabled = true;
17485 cls : 'roo-combobox-wrap',
17492 inputblock.cls += ' input-group';
17494 inputblock.cn.unshift({
17496 cls : 'input-group-addon input-group-prepend input-group-text',
17501 if(this.removable && !this.multiple){
17502 inputblock.cls += ' roo-removable';
17504 inputblock.cn.push({
17507 cls : 'roo-combo-removable-btn close'
17511 if(this.hasFeedback && !this.allowBlank){
17513 inputblock.cls += ' has-feedback';
17515 inputblock.cn.push({
17517 cls: 'glyphicon form-control-feedback'
17524 inputblock.cls += (this.before) ? '' : ' input-group';
17526 inputblock.cn.push({
17528 cls : 'input-group-addon input-group-append input-group-text',
17534 var ibwrap = inputblock;
17539 cls: 'roo-select2-choices',
17543 cls: 'roo-select2-search-field',
17556 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17561 cls: 'form-hidden-field'
17567 if(!this.multiple && this.showToggleBtn){
17573 if (this.caret != false) {
17576 cls: 'fa fa-' + this.caret
17583 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17585 Roo.bootstrap.version == 3 ? caret : '',
17588 cls: 'combobox-clear',
17602 combobox.cls += ' roo-select2-container-multi';
17605 var required = this.allowBlank ? {
17607 style: 'display: none'
17610 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17611 tooltip : 'This field is required'
17614 var align = this.labelAlign || this.parentLabelAlign();
17616 if (align ==='left' && this.fieldLabel.length) {
17622 cls : 'control-label col-form-label',
17623 html : this.fieldLabel
17627 cls : 'roo-combobox-wrap ',
17634 var labelCfg = cfg.cn[1];
17635 var contentCfg = cfg.cn[2];
17638 if(this.indicatorpos == 'right'){
17643 cls : 'control-label col-form-label',
17647 html : this.fieldLabel
17653 cls : "roo-combobox-wrap ",
17661 labelCfg = cfg.cn[0];
17662 contentCfg = cfg.cn[1];
17667 if(this.labelWidth > 12){
17668 labelCfg.style = "width: " + this.labelWidth + 'px';
17671 if(this.labelWidth < 13 && this.labelmd == 0){
17672 this.labelmd = this.labelWidth;
17675 if(this.labellg > 0){
17676 labelCfg.cls += ' col-lg-' + this.labellg;
17677 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17680 if(this.labelmd > 0){
17681 labelCfg.cls += ' col-md-' + this.labelmd;
17682 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17685 if(this.labelsm > 0){
17686 labelCfg.cls += ' col-sm-' + this.labelsm;
17687 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17690 if(this.labelxs > 0){
17691 labelCfg.cls += ' col-xs-' + this.labelxs;
17692 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17696 } else if ( this.fieldLabel.length) {
17701 cls : 'control-label',
17702 html : this.fieldLabel
17713 if(this.indicatorpos == 'right'){
17717 cls : 'control-label',
17718 html : this.fieldLabel,
17736 var settings = this;
17738 ['xs','sm','md','lg'].map(function(size){
17739 if (settings[size]) {
17740 cfg.cls += ' col-' + size + '-' + settings[size];
17747 initTouchView : function()
17749 this.renderTouchView();
17751 this.touchViewEl.on('scroll', function(){
17752 this.el.dom.scrollTop = 0;
17755 this.originalValue = this.getValue();
17757 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17759 this.inputEl().on("click", this.showTouchView, this);
17760 if (this.triggerEl) {
17761 this.triggerEl.on("click", this.showTouchView, this);
17765 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17766 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17768 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17770 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17771 this.store.on('load', this.onTouchViewLoad, this);
17772 this.store.on('loadexception', this.onTouchViewLoadException, this);
17774 if(this.hiddenName){
17776 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17778 this.hiddenField.dom.value =
17779 this.hiddenValue !== undefined ? this.hiddenValue :
17780 this.value !== undefined ? this.value : '';
17782 this.el.dom.removeAttribute('name');
17783 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17787 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17788 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17791 if(this.removable && !this.multiple){
17792 var close = this.closeTriggerEl();
17794 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17795 close.on('click', this.removeBtnClick, this, close);
17799 * fix the bug in Safari iOS8
17801 this.inputEl().on("focus", function(e){
17802 document.activeElement.blur();
17805 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17812 renderTouchView : function()
17814 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17815 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17817 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17818 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17820 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17821 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17822 this.touchViewBodyEl.setStyle('overflow', 'auto');
17824 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17825 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17827 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17828 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17832 showTouchView : function()
17838 this.touchViewHeaderEl.hide();
17840 if(this.modalTitle.length){
17841 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17842 this.touchViewHeaderEl.show();
17845 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17846 this.touchViewEl.show();
17848 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17850 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17851 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17853 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17855 if(this.modalTitle.length){
17856 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17859 this.touchViewBodyEl.setHeight(bodyHeight);
17863 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17865 this.touchViewEl.addClass(['in','show']);
17868 if(this._touchViewMask){
17869 Roo.get(document.body).addClass("x-body-masked");
17870 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17871 this._touchViewMask.setStyle('z-index', 10000);
17872 this._touchViewMask.addClass('show');
17875 this.doTouchViewQuery();
17879 hideTouchView : function()
17881 this.touchViewEl.removeClass(['in','show']);
17885 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17887 this.touchViewEl.setStyle('display', 'none');
17890 if(this._touchViewMask){
17891 this._touchViewMask.removeClass('show');
17892 Roo.get(document.body).removeClass("x-body-masked");
17896 setTouchViewValue : function()
17903 Roo.each(this.tickItems, function(o){
17908 this.hideTouchView();
17911 doTouchViewQuery : function()
17920 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17924 if(!this.alwaysQuery || this.mode == 'local'){
17925 this.onTouchViewLoad();
17932 onTouchViewBeforeLoad : function(combo,opts)
17938 onTouchViewLoad : function()
17940 if(this.store.getCount() < 1){
17941 this.onTouchViewEmptyResults();
17945 this.clearTouchView();
17947 var rawValue = this.getRawValue();
17949 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17951 this.tickItems = [];
17953 this.store.data.each(function(d, rowIndex){
17954 var row = this.touchViewListGroup.createChild(template);
17956 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17957 row.addClass(d.data.cls);
17960 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17963 html : d.data[this.displayField]
17966 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17967 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17970 row.removeClass('selected');
17971 if(!this.multiple && this.valueField &&
17972 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17975 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17976 row.addClass('selected');
17979 if(this.multiple && this.valueField &&
17980 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17984 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17985 this.tickItems.push(d.data);
17988 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17992 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17994 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17996 if(this.modalTitle.length){
17997 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18000 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18002 if(this.mobile_restrict_height && listHeight < bodyHeight){
18003 this.touchViewBodyEl.setHeight(listHeight);
18008 if(firstChecked && listHeight > bodyHeight){
18009 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18014 onTouchViewLoadException : function()
18016 this.hideTouchView();
18019 onTouchViewEmptyResults : function()
18021 this.clearTouchView();
18023 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18025 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18029 clearTouchView : function()
18031 this.touchViewListGroup.dom.innerHTML = '';
18034 onTouchViewClick : function(e, el, o)
18036 e.preventDefault();
18039 var rowIndex = o.rowIndex;
18041 var r = this.store.getAt(rowIndex);
18043 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18045 if(!this.multiple){
18046 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18047 c.dom.removeAttribute('checked');
18050 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18052 this.setFromData(r.data);
18054 var close = this.closeTriggerEl();
18060 this.hideTouchView();
18062 this.fireEvent('select', this, r, rowIndex);
18067 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18068 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18069 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18073 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18074 this.addItem(r.data);
18075 this.tickItems.push(r.data);
18079 getAutoCreateNativeIOS : function()
18082 cls: 'form-group' //input-group,
18087 cls : 'roo-ios-select'
18091 combobox.name = this.name;
18094 if (this.disabled) {
18095 combobox.disabled = true;
18098 var settings = this;
18100 ['xs','sm','md','lg'].map(function(size){
18101 if (settings[size]) {
18102 cfg.cls += ' col-' + size + '-' + settings[size];
18112 initIOSView : function()
18114 this.store.on('load', this.onIOSViewLoad, this);
18119 onIOSViewLoad : function()
18121 if(this.store.getCount() < 1){
18125 this.clearIOSView();
18127 if(this.allowBlank) {
18129 var default_text = '-- SELECT --';
18131 if(this.placeholder.length){
18132 default_text = this.placeholder;
18135 if(this.emptyTitle.length){
18136 default_text += ' - ' + this.emptyTitle + ' -';
18139 var opt = this.inputEl().createChild({
18142 html : default_text
18146 o[this.valueField] = 0;
18147 o[this.displayField] = default_text;
18149 this.ios_options.push({
18156 this.store.data.each(function(d, rowIndex){
18160 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18161 html = d.data[this.displayField];
18166 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18167 value = d.data[this.valueField];
18176 if(this.value == d.data[this.valueField]){
18177 option['selected'] = true;
18180 var opt = this.inputEl().createChild(option);
18182 this.ios_options.push({
18189 this.inputEl().on('change', function(){
18190 this.fireEvent('select', this);
18195 clearIOSView: function()
18197 this.inputEl().dom.innerHTML = '';
18199 this.ios_options = [];
18202 setIOSValue: function(v)
18206 if(!this.ios_options){
18210 Roo.each(this.ios_options, function(opts){
18212 opts.el.dom.removeAttribute('selected');
18214 if(opts.data[this.valueField] != v){
18218 opts.el.dom.setAttribute('selected', true);
18224 * @cfg {Boolean} grow
18228 * @cfg {Number} growMin
18232 * @cfg {Number} growMax
18241 Roo.apply(Roo.bootstrap.ComboBox, {
18245 cls: 'modal-header',
18267 cls: 'list-group-item',
18271 cls: 'roo-combobox-list-group-item-value'
18275 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18289 listItemCheckbox : {
18291 cls: 'list-group-item',
18295 cls: 'roo-combobox-list-group-item-value'
18299 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18315 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18320 cls: 'modal-footer',
18328 cls: 'col-xs-6 text-left',
18331 cls: 'btn btn-danger roo-touch-view-cancel',
18337 cls: 'col-xs-6 text-right',
18340 cls: 'btn btn-success roo-touch-view-ok',
18351 Roo.apply(Roo.bootstrap.ComboBox, {
18353 touchViewTemplate : {
18355 cls: 'modal fade roo-combobox-touch-view',
18359 cls: 'modal-dialog',
18360 style : 'position:fixed', // we have to fix position....
18364 cls: 'modal-content',
18366 Roo.bootstrap.ComboBox.header,
18367 Roo.bootstrap.ComboBox.body,
18368 Roo.bootstrap.ComboBox.footer
18377 * Ext JS Library 1.1.1
18378 * Copyright(c) 2006-2007, Ext JS, LLC.
18380 * Originally Released Under LGPL - original licence link has changed is not relivant.
18383 * <script type="text/javascript">
18388 * @extends Roo.util.Observable
18389 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18390 * This class also supports single and multi selection modes. <br>
18391 * Create a data model bound view:
18393 var store = new Roo.data.Store(...);
18395 var view = new Roo.View({
18397 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18399 singleSelect: true,
18400 selectedClass: "ydataview-selected",
18404 // listen for node click?
18405 view.on("click", function(vw, index, node, e){
18406 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18410 dataModel.load("foobar.xml");
18412 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18414 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18415 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18417 * Note: old style constructor is still suported (container, template, config)
18420 * Create a new View
18421 * @param {Object} config The config object
18424 Roo.View = function(config, depreciated_tpl, depreciated_config){
18426 this.parent = false;
18428 if (typeof(depreciated_tpl) == 'undefined') {
18429 // new way.. - universal constructor.
18430 Roo.apply(this, config);
18431 this.el = Roo.get(this.el);
18434 this.el = Roo.get(config);
18435 this.tpl = depreciated_tpl;
18436 Roo.apply(this, depreciated_config);
18438 this.wrapEl = this.el.wrap().wrap();
18439 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18442 if(typeof(this.tpl) == "string"){
18443 this.tpl = new Roo.Template(this.tpl);
18445 // support xtype ctors..
18446 this.tpl = new Roo.factory(this.tpl, Roo);
18450 this.tpl.compile();
18455 * @event beforeclick
18456 * Fires before a click is processed. Returns false to cancel the default action.
18457 * @param {Roo.View} this
18458 * @param {Number} index The index of the target node
18459 * @param {HTMLElement} node The target node
18460 * @param {Roo.EventObject} e The raw event object
18462 "beforeclick" : true,
18465 * Fires when a template node is clicked.
18466 * @param {Roo.View} this
18467 * @param {Number} index The index of the target node
18468 * @param {HTMLElement} node The target node
18469 * @param {Roo.EventObject} e The raw event object
18474 * Fires when a template node is double clicked.
18475 * @param {Roo.View} this
18476 * @param {Number} index The index of the target node
18477 * @param {HTMLElement} node The target node
18478 * @param {Roo.EventObject} e The raw event object
18482 * @event contextmenu
18483 * Fires when a template node is right clicked.
18484 * @param {Roo.View} this
18485 * @param {Number} index The index of the target node
18486 * @param {HTMLElement} node The target node
18487 * @param {Roo.EventObject} e The raw event object
18489 "contextmenu" : true,
18491 * @event selectionchange
18492 * Fires when the selected nodes change.
18493 * @param {Roo.View} this
18494 * @param {Array} selections Array of the selected nodes
18496 "selectionchange" : true,
18499 * @event beforeselect
18500 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18501 * @param {Roo.View} this
18502 * @param {HTMLElement} node The node to be selected
18503 * @param {Array} selections Array of currently selected nodes
18505 "beforeselect" : true,
18507 * @event preparedata
18508 * Fires on every row to render, to allow you to change the data.
18509 * @param {Roo.View} this
18510 * @param {Object} data to be rendered (change this)
18512 "preparedata" : true
18520 "click": this.onClick,
18521 "dblclick": this.onDblClick,
18522 "contextmenu": this.onContextMenu,
18526 this.selections = [];
18528 this.cmp = new Roo.CompositeElementLite([]);
18530 this.store = Roo.factory(this.store, Roo.data);
18531 this.setStore(this.store, true);
18534 if ( this.footer && this.footer.xtype) {
18536 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18538 this.footer.dataSource = this.store;
18539 this.footer.container = fctr;
18540 this.footer = Roo.factory(this.footer, Roo);
18541 fctr.insertFirst(this.el);
18543 // this is a bit insane - as the paging toolbar seems to detach the el..
18544 // dom.parentNode.parentNode.parentNode
18545 // they get detached?
18549 Roo.View.superclass.constructor.call(this);
18554 Roo.extend(Roo.View, Roo.util.Observable, {
18557 * @cfg {Roo.data.Store} store Data store to load data from.
18562 * @cfg {String|Roo.Element} el The container element.
18567 * @cfg {String|Roo.Template} tpl The template used by this View
18571 * @cfg {String} dataName the named area of the template to use as the data area
18572 * Works with domtemplates roo-name="name"
18576 * @cfg {String} selectedClass The css class to add to selected nodes
18578 selectedClass : "x-view-selected",
18580 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18585 * @cfg {String} text to display on mask (default Loading)
18589 * @cfg {Boolean} multiSelect Allow multiple selection
18591 multiSelect : false,
18593 * @cfg {Boolean} singleSelect Allow single selection
18595 singleSelect: false,
18598 * @cfg {Boolean} toggleSelect - selecting
18600 toggleSelect : false,
18603 * @cfg {Boolean} tickable - selecting
18608 * Returns the element this view is bound to.
18609 * @return {Roo.Element}
18611 getEl : function(){
18612 return this.wrapEl;
18618 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18620 refresh : function(){
18621 //Roo.log('refresh');
18624 // if we are using something like 'domtemplate', then
18625 // the what gets used is:
18626 // t.applySubtemplate(NAME, data, wrapping data..)
18627 // the outer template then get' applied with
18628 // the store 'extra data'
18629 // and the body get's added to the
18630 // roo-name="data" node?
18631 // <span class='roo-tpl-{name}'></span> ?????
18635 this.clearSelections();
18636 this.el.update("");
18638 var records = this.store.getRange();
18639 if(records.length < 1) {
18641 // is this valid?? = should it render a template??
18643 this.el.update(this.emptyText);
18647 if (this.dataName) {
18648 this.el.update(t.apply(this.store.meta)); //????
18649 el = this.el.child('.roo-tpl-' + this.dataName);
18652 for(var i = 0, len = records.length; i < len; i++){
18653 var data = this.prepareData(records[i].data, i, records[i]);
18654 this.fireEvent("preparedata", this, data, i, records[i]);
18656 var d = Roo.apply({}, data);
18659 Roo.apply(d, {'roo-id' : Roo.id()});
18663 Roo.each(this.parent.item, function(item){
18664 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18667 Roo.apply(d, {'roo-data-checked' : 'checked'});
18671 html[html.length] = Roo.util.Format.trim(
18673 t.applySubtemplate(this.dataName, d, this.store.meta) :
18680 el.update(html.join(""));
18681 this.nodes = el.dom.childNodes;
18682 this.updateIndexes(0);
18687 * Function to override to reformat the data that is sent to
18688 * the template for each node.
18689 * DEPRICATED - use the preparedata event handler.
18690 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18691 * a JSON object for an UpdateManager bound view).
18693 prepareData : function(data, index, record)
18695 this.fireEvent("preparedata", this, data, index, record);
18699 onUpdate : function(ds, record){
18700 // Roo.log('on update');
18701 this.clearSelections();
18702 var index = this.store.indexOf(record);
18703 var n = this.nodes[index];
18704 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18705 n.parentNode.removeChild(n);
18706 this.updateIndexes(index, index);
18712 onAdd : function(ds, records, index)
18714 //Roo.log(['on Add', ds, records, index] );
18715 this.clearSelections();
18716 if(this.nodes.length == 0){
18720 var n = this.nodes[index];
18721 for(var i = 0, len = records.length; i < len; i++){
18722 var d = this.prepareData(records[i].data, i, records[i]);
18724 this.tpl.insertBefore(n, d);
18727 this.tpl.append(this.el, d);
18730 this.updateIndexes(index);
18733 onRemove : function(ds, record, index){
18734 // Roo.log('onRemove');
18735 this.clearSelections();
18736 var el = this.dataName ?
18737 this.el.child('.roo-tpl-' + this.dataName) :
18740 el.dom.removeChild(this.nodes[index]);
18741 this.updateIndexes(index);
18745 * Refresh an individual node.
18746 * @param {Number} index
18748 refreshNode : function(index){
18749 this.onUpdate(this.store, this.store.getAt(index));
18752 updateIndexes : function(startIndex, endIndex){
18753 var ns = this.nodes;
18754 startIndex = startIndex || 0;
18755 endIndex = endIndex || ns.length - 1;
18756 for(var i = startIndex; i <= endIndex; i++){
18757 ns[i].nodeIndex = i;
18762 * Changes the data store this view uses and refresh the view.
18763 * @param {Store} store
18765 setStore : function(store, initial){
18766 if(!initial && this.store){
18767 this.store.un("datachanged", this.refresh);
18768 this.store.un("add", this.onAdd);
18769 this.store.un("remove", this.onRemove);
18770 this.store.un("update", this.onUpdate);
18771 this.store.un("clear", this.refresh);
18772 this.store.un("beforeload", this.onBeforeLoad);
18773 this.store.un("load", this.onLoad);
18774 this.store.un("loadexception", this.onLoad);
18778 store.on("datachanged", this.refresh, this);
18779 store.on("add", this.onAdd, this);
18780 store.on("remove", this.onRemove, this);
18781 store.on("update", this.onUpdate, this);
18782 store.on("clear", this.refresh, this);
18783 store.on("beforeload", this.onBeforeLoad, this);
18784 store.on("load", this.onLoad, this);
18785 store.on("loadexception", this.onLoad, this);
18793 * onbeforeLoad - masks the loading area.
18796 onBeforeLoad : function(store,opts)
18798 //Roo.log('onBeforeLoad');
18800 this.el.update("");
18802 this.el.mask(this.mask ? this.mask : "Loading" );
18804 onLoad : function ()
18811 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18812 * @param {HTMLElement} node
18813 * @return {HTMLElement} The template node
18815 findItemFromChild : function(node){
18816 var el = this.dataName ?
18817 this.el.child('.roo-tpl-' + this.dataName,true) :
18820 if(!node || node.parentNode == el){
18823 var p = node.parentNode;
18824 while(p && p != el){
18825 if(p.parentNode == el){
18834 onClick : function(e){
18835 var item = this.findItemFromChild(e.getTarget());
18837 var index = this.indexOf(item);
18838 if(this.onItemClick(item, index, e) !== false){
18839 this.fireEvent("click", this, index, item, e);
18842 this.clearSelections();
18847 onContextMenu : function(e){
18848 var item = this.findItemFromChild(e.getTarget());
18850 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18855 onDblClick : function(e){
18856 var item = this.findItemFromChild(e.getTarget());
18858 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18862 onItemClick : function(item, index, e)
18864 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18867 if (this.toggleSelect) {
18868 var m = this.isSelected(item) ? 'unselect' : 'select';
18871 _t[m](item, true, false);
18874 if(this.multiSelect || this.singleSelect){
18875 if(this.multiSelect && e.shiftKey && this.lastSelection){
18876 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18878 this.select(item, this.multiSelect && e.ctrlKey);
18879 this.lastSelection = item;
18882 if(!this.tickable){
18883 e.preventDefault();
18891 * Get the number of selected nodes.
18894 getSelectionCount : function(){
18895 return this.selections.length;
18899 * Get the currently selected nodes.
18900 * @return {Array} An array of HTMLElements
18902 getSelectedNodes : function(){
18903 return this.selections;
18907 * Get the indexes of the selected nodes.
18910 getSelectedIndexes : function(){
18911 var indexes = [], s = this.selections;
18912 for(var i = 0, len = s.length; i < len; i++){
18913 indexes.push(s[i].nodeIndex);
18919 * Clear all selections
18920 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18922 clearSelections : function(suppressEvent){
18923 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18924 this.cmp.elements = this.selections;
18925 this.cmp.removeClass(this.selectedClass);
18926 this.selections = [];
18927 if(!suppressEvent){
18928 this.fireEvent("selectionchange", this, this.selections);
18934 * Returns true if the passed node is selected
18935 * @param {HTMLElement/Number} node The node or node index
18936 * @return {Boolean}
18938 isSelected : function(node){
18939 var s = this.selections;
18943 node = this.getNode(node);
18944 return s.indexOf(node) !== -1;
18949 * @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
18950 * @param {Boolean} keepExisting (optional) true to keep existing selections
18951 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18953 select : function(nodeInfo, keepExisting, suppressEvent){
18954 if(nodeInfo instanceof Array){
18956 this.clearSelections(true);
18958 for(var i = 0, len = nodeInfo.length; i < len; i++){
18959 this.select(nodeInfo[i], true, true);
18963 var node = this.getNode(nodeInfo);
18964 if(!node || this.isSelected(node)){
18965 return; // already selected.
18968 this.clearSelections(true);
18971 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18972 Roo.fly(node).addClass(this.selectedClass);
18973 this.selections.push(node);
18974 if(!suppressEvent){
18975 this.fireEvent("selectionchange", this, this.selections);
18983 * @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
18984 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18985 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18987 unselect : function(nodeInfo, keepExisting, suppressEvent)
18989 if(nodeInfo instanceof Array){
18990 Roo.each(this.selections, function(s) {
18991 this.unselect(s, nodeInfo);
18995 var node = this.getNode(nodeInfo);
18996 if(!node || !this.isSelected(node)){
18997 //Roo.log("not selected");
18998 return; // not selected.
19002 Roo.each(this.selections, function(s) {
19004 Roo.fly(node).removeClass(this.selectedClass);
19011 this.selections= ns;
19012 this.fireEvent("selectionchange", this, this.selections);
19016 * Gets a template node.
19017 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19018 * @return {HTMLElement} The node or null if it wasn't found
19020 getNode : function(nodeInfo){
19021 if(typeof nodeInfo == "string"){
19022 return document.getElementById(nodeInfo);
19023 }else if(typeof nodeInfo == "number"){
19024 return this.nodes[nodeInfo];
19030 * Gets a range template nodes.
19031 * @param {Number} startIndex
19032 * @param {Number} endIndex
19033 * @return {Array} An array of nodes
19035 getNodes : function(start, end){
19036 var ns = this.nodes;
19037 start = start || 0;
19038 end = typeof end == "undefined" ? ns.length - 1 : end;
19041 for(var i = start; i <= end; i++){
19045 for(var i = start; i >= end; i--){
19053 * Finds the index of the passed node
19054 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19055 * @return {Number} The index of the node or -1
19057 indexOf : function(node){
19058 node = this.getNode(node);
19059 if(typeof node.nodeIndex == "number"){
19060 return node.nodeIndex;
19062 var ns = this.nodes;
19063 for(var i = 0, len = ns.length; i < len; i++){
19074 * based on jquery fullcalendar
19078 Roo.bootstrap = Roo.bootstrap || {};
19080 * @class Roo.bootstrap.Calendar
19081 * @extends Roo.bootstrap.Component
19082 * Bootstrap Calendar class
19083 * @cfg {Boolean} loadMask (true|false) default false
19084 * @cfg {Object} header generate the user specific header of the calendar, default false
19087 * Create a new Container
19088 * @param {Object} config The config object
19093 Roo.bootstrap.Calendar = function(config){
19094 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19098 * Fires when a date is selected
19099 * @param {DatePicker} this
19100 * @param {Date} date The selected date
19104 * @event monthchange
19105 * Fires when the displayed month changes
19106 * @param {DatePicker} this
19107 * @param {Date} date The selected month
19109 'monthchange': true,
19111 * @event evententer
19112 * Fires when mouse over an event
19113 * @param {Calendar} this
19114 * @param {event} Event
19116 'evententer': true,
19118 * @event eventleave
19119 * Fires when the mouse leaves an
19120 * @param {Calendar} this
19123 'eventleave': true,
19125 * @event eventclick
19126 * Fires when the mouse click an
19127 * @param {Calendar} this
19136 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19139 * @cfg {Number} startDay
19140 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19148 getAutoCreate : function(){
19151 var fc_button = function(name, corner, style, content ) {
19152 return Roo.apply({},{
19154 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19156 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19159 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19170 style : 'width:100%',
19177 cls : 'fc-header-left',
19179 fc_button('prev', 'left', 'arrow', '‹' ),
19180 fc_button('next', 'right', 'arrow', '›' ),
19181 { tag: 'span', cls: 'fc-header-space' },
19182 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19190 cls : 'fc-header-center',
19194 cls: 'fc-header-title',
19197 html : 'month / year'
19205 cls : 'fc-header-right',
19207 /* fc_button('month', 'left', '', 'month' ),
19208 fc_button('week', '', '', 'week' ),
19209 fc_button('day', 'right', '', 'day' )
19221 header = this.header;
19224 var cal_heads = function() {
19226 // fixme - handle this.
19228 for (var i =0; i < Date.dayNames.length; i++) {
19229 var d = Date.dayNames[i];
19232 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19233 html : d.substring(0,3)
19237 ret[0].cls += ' fc-first';
19238 ret[6].cls += ' fc-last';
19241 var cal_cell = function(n) {
19244 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19249 cls: 'fc-day-number',
19253 cls: 'fc-day-content',
19257 style: 'position: relative;' // height: 17px;
19269 var cal_rows = function() {
19272 for (var r = 0; r < 6; r++) {
19279 for (var i =0; i < Date.dayNames.length; i++) {
19280 var d = Date.dayNames[i];
19281 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19284 row.cn[0].cls+=' fc-first';
19285 row.cn[0].cn[0].style = 'min-height:90px';
19286 row.cn[6].cls+=' fc-last';
19290 ret[0].cls += ' fc-first';
19291 ret[4].cls += ' fc-prev-last';
19292 ret[5].cls += ' fc-last';
19299 cls: 'fc-border-separate',
19300 style : 'width:100%',
19308 cls : 'fc-first fc-last',
19326 cls : 'fc-content',
19327 style : "position: relative;",
19330 cls : 'fc-view fc-view-month fc-grid',
19331 style : 'position: relative',
19332 unselectable : 'on',
19335 cls : 'fc-event-container',
19336 style : 'position:absolute;z-index:8;top:0;left:0;'
19354 initEvents : function()
19357 throw "can not find store for calendar";
19363 style: "text-align:center",
19367 style: "background-color:white;width:50%;margin:250 auto",
19371 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19382 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19384 var size = this.el.select('.fc-content', true).first().getSize();
19385 this.maskEl.setSize(size.width, size.height);
19386 this.maskEl.enableDisplayMode("block");
19387 if(!this.loadMask){
19388 this.maskEl.hide();
19391 this.store = Roo.factory(this.store, Roo.data);
19392 this.store.on('load', this.onLoad, this);
19393 this.store.on('beforeload', this.onBeforeLoad, this);
19397 this.cells = this.el.select('.fc-day',true);
19398 //Roo.log(this.cells);
19399 this.textNodes = this.el.query('.fc-day-number');
19400 this.cells.addClassOnOver('fc-state-hover');
19402 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19403 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19404 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19405 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19407 this.on('monthchange', this.onMonthChange, this);
19409 this.update(new Date().clearTime());
19412 resize : function() {
19413 var sz = this.el.getSize();
19415 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19416 this.el.select('.fc-day-content div',true).setHeight(34);
19421 showPrevMonth : function(e){
19422 this.update(this.activeDate.add("mo", -1));
19424 showToday : function(e){
19425 this.update(new Date().clearTime());
19428 showNextMonth : function(e){
19429 this.update(this.activeDate.add("mo", 1));
19433 showPrevYear : function(){
19434 this.update(this.activeDate.add("y", -1));
19438 showNextYear : function(){
19439 this.update(this.activeDate.add("y", 1));
19444 update : function(date)
19446 var vd = this.activeDate;
19447 this.activeDate = date;
19448 // if(vd && this.el){
19449 // var t = date.getTime();
19450 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19451 // Roo.log('using add remove');
19453 // this.fireEvent('monthchange', this, date);
19455 // this.cells.removeClass("fc-state-highlight");
19456 // this.cells.each(function(c){
19457 // if(c.dateValue == t){
19458 // c.addClass("fc-state-highlight");
19459 // setTimeout(function(){
19460 // try{c.dom.firstChild.focus();}catch(e){}
19470 var days = date.getDaysInMonth();
19472 var firstOfMonth = date.getFirstDateOfMonth();
19473 var startingPos = firstOfMonth.getDay()-this.startDay;
19475 if(startingPos < this.startDay){
19479 var pm = date.add(Date.MONTH, -1);
19480 var prevStart = pm.getDaysInMonth()-startingPos;
19482 this.cells = this.el.select('.fc-day',true);
19483 this.textNodes = this.el.query('.fc-day-number');
19484 this.cells.addClassOnOver('fc-state-hover');
19486 var cells = this.cells.elements;
19487 var textEls = this.textNodes;
19489 Roo.each(cells, function(cell){
19490 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19493 days += startingPos;
19495 // convert everything to numbers so it's fast
19496 var day = 86400000;
19497 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19500 //Roo.log(prevStart);
19502 var today = new Date().clearTime().getTime();
19503 var sel = date.clearTime().getTime();
19504 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19505 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19506 var ddMatch = this.disabledDatesRE;
19507 var ddText = this.disabledDatesText;
19508 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19509 var ddaysText = this.disabledDaysText;
19510 var format = this.format;
19512 var setCellClass = function(cal, cell){
19516 //Roo.log('set Cell Class');
19518 var t = d.getTime();
19522 cell.dateValue = t;
19524 cell.className += " fc-today";
19525 cell.className += " fc-state-highlight";
19526 cell.title = cal.todayText;
19529 // disable highlight in other month..
19530 //cell.className += " fc-state-highlight";
19535 cell.className = " fc-state-disabled";
19536 cell.title = cal.minText;
19540 cell.className = " fc-state-disabled";
19541 cell.title = cal.maxText;
19545 if(ddays.indexOf(d.getDay()) != -1){
19546 cell.title = ddaysText;
19547 cell.className = " fc-state-disabled";
19550 if(ddMatch && format){
19551 var fvalue = d.dateFormat(format);
19552 if(ddMatch.test(fvalue)){
19553 cell.title = ddText.replace("%0", fvalue);
19554 cell.className = " fc-state-disabled";
19558 if (!cell.initialClassName) {
19559 cell.initialClassName = cell.dom.className;
19562 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19567 for(; i < startingPos; i++) {
19568 textEls[i].innerHTML = (++prevStart);
19569 d.setDate(d.getDate()+1);
19571 cells[i].className = "fc-past fc-other-month";
19572 setCellClass(this, cells[i]);
19577 for(; i < days; i++){
19578 intDay = i - startingPos + 1;
19579 textEls[i].innerHTML = (intDay);
19580 d.setDate(d.getDate()+1);
19582 cells[i].className = ''; // "x-date-active";
19583 setCellClass(this, cells[i]);
19587 for(; i < 42; i++) {
19588 textEls[i].innerHTML = (++extraDays);
19589 d.setDate(d.getDate()+1);
19591 cells[i].className = "fc-future fc-other-month";
19592 setCellClass(this, cells[i]);
19595 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19597 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19599 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19600 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19602 if(totalRows != 6){
19603 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19604 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19607 this.fireEvent('monthchange', this, date);
19611 if(!this.internalRender){
19612 var main = this.el.dom.firstChild;
19613 var w = main.offsetWidth;
19614 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19615 Roo.fly(main).setWidth(w);
19616 this.internalRender = true;
19617 // opera does not respect the auto grow header center column
19618 // then, after it gets a width opera refuses to recalculate
19619 // without a second pass
19620 if(Roo.isOpera && !this.secondPass){
19621 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19622 this.secondPass = true;
19623 this.update.defer(10, this, [date]);
19630 findCell : function(dt) {
19631 dt = dt.clearTime().getTime();
19633 this.cells.each(function(c){
19634 //Roo.log("check " +c.dateValue + '?=' + dt);
19635 if(c.dateValue == dt){
19645 findCells : function(ev) {
19646 var s = ev.start.clone().clearTime().getTime();
19648 var e= ev.end.clone().clearTime().getTime();
19651 this.cells.each(function(c){
19652 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19654 if(c.dateValue > e){
19657 if(c.dateValue < s){
19666 // findBestRow: function(cells)
19670 // for (var i =0 ; i < cells.length;i++) {
19671 // ret = Math.max(cells[i].rows || 0,ret);
19678 addItem : function(ev)
19680 // look for vertical location slot in
19681 var cells = this.findCells(ev);
19683 // ev.row = this.findBestRow(cells);
19685 // work out the location.
19689 for(var i =0; i < cells.length; i++) {
19691 cells[i].row = cells[0].row;
19694 cells[i].row = cells[i].row + 1;
19704 if (crow.start.getY() == cells[i].getY()) {
19706 crow.end = cells[i];
19723 cells[0].events.push(ev);
19725 this.calevents.push(ev);
19728 clearEvents: function() {
19730 if(!this.calevents){
19734 Roo.each(this.cells.elements, function(c){
19740 Roo.each(this.calevents, function(e) {
19741 Roo.each(e.els, function(el) {
19742 el.un('mouseenter' ,this.onEventEnter, this);
19743 el.un('mouseleave' ,this.onEventLeave, this);
19748 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19754 renderEvents: function()
19758 this.cells.each(function(c) {
19767 if(c.row != c.events.length){
19768 r = 4 - (4 - (c.row - c.events.length));
19771 c.events = ev.slice(0, r);
19772 c.more = ev.slice(r);
19774 if(c.more.length && c.more.length == 1){
19775 c.events.push(c.more.pop());
19778 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19782 this.cells.each(function(c) {
19784 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19787 for (var e = 0; e < c.events.length; e++){
19788 var ev = c.events[e];
19789 var rows = ev.rows;
19791 for(var i = 0; i < rows.length; i++) {
19793 // how many rows should it span..
19796 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19797 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19799 unselectable : "on",
19802 cls: 'fc-event-inner',
19806 // cls: 'fc-event-time',
19807 // html : cells.length > 1 ? '' : ev.time
19811 cls: 'fc-event-title',
19812 html : String.format('{0}', ev.title)
19819 cls: 'ui-resizable-handle ui-resizable-e',
19820 html : '  '
19827 cfg.cls += ' fc-event-start';
19829 if ((i+1) == rows.length) {
19830 cfg.cls += ' fc-event-end';
19833 var ctr = _this.el.select('.fc-event-container',true).first();
19834 var cg = ctr.createChild(cfg);
19836 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19837 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19839 var r = (c.more.length) ? 1 : 0;
19840 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19841 cg.setWidth(ebox.right - sbox.x -2);
19843 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19844 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19845 cg.on('click', _this.onEventClick, _this, ev);
19856 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19857 style : 'position: absolute',
19858 unselectable : "on",
19861 cls: 'fc-event-inner',
19865 cls: 'fc-event-title',
19873 cls: 'ui-resizable-handle ui-resizable-e',
19874 html : '  '
19880 var ctr = _this.el.select('.fc-event-container',true).first();
19881 var cg = ctr.createChild(cfg);
19883 var sbox = c.select('.fc-day-content',true).first().getBox();
19884 var ebox = c.select('.fc-day-content',true).first().getBox();
19886 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19887 cg.setWidth(ebox.right - sbox.x -2);
19889 cg.on('click', _this.onMoreEventClick, _this, c.more);
19899 onEventEnter: function (e, el,event,d) {
19900 this.fireEvent('evententer', this, el, event);
19903 onEventLeave: function (e, el,event,d) {
19904 this.fireEvent('eventleave', this, el, event);
19907 onEventClick: function (e, el,event,d) {
19908 this.fireEvent('eventclick', this, el, event);
19911 onMonthChange: function () {
19915 onMoreEventClick: function(e, el, more)
19919 this.calpopover.placement = 'right';
19920 this.calpopover.setTitle('More');
19922 this.calpopover.setContent('');
19924 var ctr = this.calpopover.el.select('.popover-content', true).first();
19926 Roo.each(more, function(m){
19928 cls : 'fc-event-hori fc-event-draggable',
19931 var cg = ctr.createChild(cfg);
19933 cg.on('click', _this.onEventClick, _this, m);
19936 this.calpopover.show(el);
19941 onLoad: function ()
19943 this.calevents = [];
19946 if(this.store.getCount() > 0){
19947 this.store.data.each(function(d){
19950 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19951 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19952 time : d.data.start_time,
19953 title : d.data.title,
19954 description : d.data.description,
19955 venue : d.data.venue
19960 this.renderEvents();
19962 if(this.calevents.length && this.loadMask){
19963 this.maskEl.hide();
19967 onBeforeLoad: function()
19969 this.clearEvents();
19971 this.maskEl.show();
19985 * @class Roo.bootstrap.Popover
19986 * @extends Roo.bootstrap.Component
19987 * Bootstrap Popover class
19988 * @cfg {String} html contents of the popover (or false to use children..)
19989 * @cfg {String} title of popover (or false to hide)
19990 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19991 * @cfg {String} trigger click || hover (or false to trigger manually)
19992 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19993 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19994 * - if false and it has a 'parent' then it will be automatically added to that element
19995 * - if string - Roo.get will be called
19996 * @cfg {Number} delay - delay before showing
19999 * Create a new Popover
20000 * @param {Object} config The config object
20003 Roo.bootstrap.Popover = function(config){
20004 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20010 * After the popover show
20012 * @param {Roo.bootstrap.Popover} this
20017 * After the popover hide
20019 * @param {Roo.bootstrap.Popover} this
20025 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20030 placement : 'right',
20031 trigger : 'hover', // hover
20037 can_build_overlaid : false,
20039 maskEl : false, // the mask element
20042 alignEl : false, // when show is called with an element - this get's stored.
20044 getChildContainer : function()
20046 return this.contentEl;
20049 getPopoverHeader : function()
20051 this.title = true; // flag not to hide it..
20052 this.headerEl.addClass('p-0');
20053 return this.headerEl
20057 getAutoCreate : function(){
20060 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20061 style: 'display:block',
20067 cls : 'popover-inner ',
20071 cls: 'popover-title popover-header',
20072 html : this.title === false ? '' : this.title
20075 cls : 'popover-content popover-body ' + (this.cls || ''),
20076 html : this.html || ''
20087 * @param {string} the title
20089 setTitle: function(str)
20093 this.headerEl.dom.innerHTML = str;
20098 * @param {string} the body content
20100 setContent: function(str)
20103 if (this.contentEl) {
20104 this.contentEl.dom.innerHTML = str;
20108 // as it get's added to the bottom of the page.
20109 onRender : function(ct, position)
20111 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20116 var cfg = Roo.apply({}, this.getAutoCreate());
20120 cfg.cls += ' ' + this.cls;
20123 cfg.style = this.style;
20125 //Roo.log("adding to ");
20126 this.el = Roo.get(document.body).createChild(cfg, position);
20127 // Roo.log(this.el);
20130 this.contentEl = this.el.select('.popover-content',true).first();
20131 this.headerEl = this.el.select('.popover-title',true).first();
20134 if(typeof(this.items) != 'undefined'){
20135 var items = this.items;
20138 for(var i =0;i < items.length;i++) {
20139 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20143 this.items = nitems;
20145 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20146 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20153 resizeMask : function()
20155 this.maskEl.setSize(
20156 Roo.lib.Dom.getViewWidth(true),
20157 Roo.lib.Dom.getViewHeight(true)
20161 initEvents : function()
20165 Roo.bootstrap.Popover.register(this);
20168 this.arrowEl = this.el.select('.arrow',true).first();
20169 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20170 this.el.enableDisplayMode('block');
20174 if (this.over === false && !this.parent()) {
20177 if (this.triggers === false) {
20182 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20183 var triggers = this.trigger ? this.trigger.split(' ') : [];
20184 Roo.each(triggers, function(trigger) {
20186 if (trigger == 'click') {
20187 on_el.on('click', this.toggle, this);
20188 } else if (trigger != 'manual') {
20189 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20190 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20192 on_el.on(eventIn ,this.enter, this);
20193 on_el.on(eventOut, this.leave, this);
20203 toggle : function () {
20204 this.hoverState == 'in' ? this.leave() : this.enter();
20207 enter : function () {
20209 clearTimeout(this.timeout);
20211 this.hoverState = 'in';
20213 if (!this.delay || !this.delay.show) {
20218 this.timeout = setTimeout(function () {
20219 if (_t.hoverState == 'in') {
20222 }, this.delay.show)
20225 leave : function() {
20226 clearTimeout(this.timeout);
20228 this.hoverState = 'out';
20230 if (!this.delay || !this.delay.hide) {
20235 this.timeout = setTimeout(function () {
20236 if (_t.hoverState == 'out') {
20239 }, this.delay.hide)
20243 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20244 * @param {string} (left|right|top|bottom) position
20246 show : function (on_el, placement)
20248 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20249 on_el = on_el || false; // default to false
20252 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20253 on_el = this.parent().el;
20254 } else if (this.over) {
20255 on_el = Roo.get(this.over);
20260 this.alignEl = Roo.get( on_el );
20263 this.render(document.body);
20269 if (this.title === false) {
20270 this.headerEl.hide();
20275 this.el.dom.style.display = 'block';
20278 if (this.alignEl) {
20279 this.updatePosition(this.placement, true);
20282 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20283 var es = this.el.getSize();
20284 var x = Roo.lib.Dom.getViewWidth()/2;
20285 var y = Roo.lib.Dom.getViewHeight()/2;
20286 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20291 //var arrow = this.el.select('.arrow',true).first();
20292 //arrow.set(align[2],
20294 this.el.addClass('in');
20298 this.hoverState = 'in';
20301 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20302 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20303 this.maskEl.dom.style.display = 'block';
20304 this.maskEl.addClass('show');
20306 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20308 this.fireEvent('show', this);
20312 * fire this manually after loading a grid in the table for example
20313 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20314 * @param {Boolean} try and move it if we cant get right position.
20316 updatePosition : function(placement, try_move)
20318 // allow for calling with no parameters
20319 placement = placement ? placement : this.placement;
20320 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20322 this.el.removeClass([
20323 'fade','top','bottom', 'left', 'right','in',
20324 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20326 this.el.addClass(placement + ' bs-popover-' + placement);
20328 if (!this.alignEl ) {
20332 switch (placement) {
20334 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20335 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20336 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20337 //normal display... or moved up/down.
20338 this.el.setXY(offset);
20339 var xy = this.alignEl.getAnchorXY('tr', false);
20341 this.arrowEl.setXY(xy);
20344 // continue through...
20345 return this.updatePosition('left', false);
20349 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20350 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20351 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20352 //normal display... or moved up/down.
20353 this.el.setXY(offset);
20354 var xy = this.alignEl.getAnchorXY('tl', false);
20355 xy[0]-=10;xy[1]+=5; // << fix me
20356 this.arrowEl.setXY(xy);
20360 return this.updatePosition('right', false);
20363 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20364 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20365 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20366 //normal display... or moved up/down.
20367 this.el.setXY(offset);
20368 var xy = this.alignEl.getAnchorXY('t', false);
20369 xy[1]-=10; // << fix me
20370 this.arrowEl.setXY(xy);
20374 return this.updatePosition('bottom', false);
20377 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20378 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20379 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20380 //normal display... or moved up/down.
20381 this.el.setXY(offset);
20382 var xy = this.alignEl.getAnchorXY('b', false);
20383 xy[1]+=2; // << fix me
20384 this.arrowEl.setXY(xy);
20388 return this.updatePosition('top', false);
20399 this.el.setXY([0,0]);
20400 this.el.removeClass('in');
20402 this.hoverState = null;
20403 this.maskEl.hide(); // always..
20404 this.fireEvent('hide', this);
20410 Roo.apply(Roo.bootstrap.Popover, {
20413 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20414 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20415 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20416 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20421 clickHander : false,
20425 onMouseDown : function(e)
20427 if (this.popups.length && !e.getTarget(".roo-popover")) {
20428 /// what is nothing is showing..
20437 register : function(popup)
20439 if (!Roo.bootstrap.Popover.clickHandler) {
20440 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20442 // hide other popups.
20443 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20444 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20445 this.hideAll(); //<< why?
20446 //this.popups.push(popup);
20448 hideAll : function()
20450 this.popups.forEach(function(p) {
20454 onShow : function() {
20455 Roo.bootstrap.Popover.popups.push(this);
20457 onHide : function() {
20458 Roo.bootstrap.Popover.popups.remove(this);
20464 * Card header - holder for the card header elements.
20469 * @class Roo.bootstrap.PopoverNav
20470 * @extends Roo.bootstrap.NavGroup
20471 * Bootstrap Popover header navigation class
20473 * Create a new Popover Header Navigation
20474 * @param {Object} config The config object
20477 Roo.bootstrap.PopoverNav = function(config){
20478 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20481 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20484 container_method : 'getPopoverHeader'
20502 * @class Roo.bootstrap.Progress
20503 * @extends Roo.bootstrap.Component
20504 * Bootstrap Progress class
20505 * @cfg {Boolean} striped striped of the progress bar
20506 * @cfg {Boolean} active animated of the progress bar
20510 * Create a new Progress
20511 * @param {Object} config The config object
20514 Roo.bootstrap.Progress = function(config){
20515 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20518 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20523 getAutoCreate : function(){
20531 cfg.cls += ' progress-striped';
20535 cfg.cls += ' active';
20554 * @class Roo.bootstrap.ProgressBar
20555 * @extends Roo.bootstrap.Component
20556 * Bootstrap ProgressBar class
20557 * @cfg {Number} aria_valuenow aria-value now
20558 * @cfg {Number} aria_valuemin aria-value min
20559 * @cfg {Number} aria_valuemax aria-value max
20560 * @cfg {String} label label for the progress bar
20561 * @cfg {String} panel (success | info | warning | danger )
20562 * @cfg {String} role role of the progress bar
20563 * @cfg {String} sr_only text
20567 * Create a new ProgressBar
20568 * @param {Object} config The config object
20571 Roo.bootstrap.ProgressBar = function(config){
20572 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20575 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20579 aria_valuemax : 100,
20585 getAutoCreate : function()
20590 cls: 'progress-bar',
20591 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20603 cfg.role = this.role;
20606 if(this.aria_valuenow){
20607 cfg['aria-valuenow'] = this.aria_valuenow;
20610 if(this.aria_valuemin){
20611 cfg['aria-valuemin'] = this.aria_valuemin;
20614 if(this.aria_valuemax){
20615 cfg['aria-valuemax'] = this.aria_valuemax;
20618 if(this.label && !this.sr_only){
20619 cfg.html = this.label;
20623 cfg.cls += ' progress-bar-' + this.panel;
20629 update : function(aria_valuenow)
20631 this.aria_valuenow = aria_valuenow;
20633 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20648 * @class Roo.bootstrap.TabGroup
20649 * @extends Roo.bootstrap.Column
20650 * Bootstrap Column class
20651 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20652 * @cfg {Boolean} carousel true to make the group behave like a carousel
20653 * @cfg {Boolean} bullets show bullets for the panels
20654 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20655 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20656 * @cfg {Boolean} showarrow (true|false) show arrow default true
20659 * Create a new TabGroup
20660 * @param {Object} config The config object
20663 Roo.bootstrap.TabGroup = function(config){
20664 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20666 this.navId = Roo.id();
20669 Roo.bootstrap.TabGroup.register(this);
20673 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20676 transition : false,
20681 slideOnTouch : false,
20684 getAutoCreate : function()
20686 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20688 cfg.cls += ' tab-content';
20690 if (this.carousel) {
20691 cfg.cls += ' carousel slide';
20694 cls : 'carousel-inner',
20698 if(this.bullets && !Roo.isTouch){
20701 cls : 'carousel-bullets',
20705 if(this.bullets_cls){
20706 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20713 cfg.cn[0].cn.push(bullets);
20716 if(this.showarrow){
20717 cfg.cn[0].cn.push({
20719 class : 'carousel-arrow',
20723 class : 'carousel-prev',
20727 class : 'fa fa-chevron-left'
20733 class : 'carousel-next',
20737 class : 'fa fa-chevron-right'
20750 initEvents: function()
20752 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20753 // this.el.on("touchstart", this.onTouchStart, this);
20756 if(this.autoslide){
20759 this.slideFn = window.setInterval(function() {
20760 _this.showPanelNext();
20764 if(this.showarrow){
20765 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20766 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20772 // onTouchStart : function(e, el, o)
20774 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20778 // this.showPanelNext();
20782 getChildContainer : function()
20784 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20788 * register a Navigation item
20789 * @param {Roo.bootstrap.NavItem} the navitem to add
20791 register : function(item)
20793 this.tabs.push( item);
20794 item.navId = this.navId; // not really needed..
20799 getActivePanel : function()
20802 Roo.each(this.tabs, function(t) {
20812 getPanelByName : function(n)
20815 Roo.each(this.tabs, function(t) {
20816 if (t.tabId == n) {
20824 indexOfPanel : function(p)
20827 Roo.each(this.tabs, function(t,i) {
20828 if (t.tabId == p.tabId) {
20837 * show a specific panel
20838 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20839 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20841 showPanel : function (pan)
20843 if(this.transition || typeof(pan) == 'undefined'){
20844 Roo.log("waiting for the transitionend");
20848 if (typeof(pan) == 'number') {
20849 pan = this.tabs[pan];
20852 if (typeof(pan) == 'string') {
20853 pan = this.getPanelByName(pan);
20856 var cur = this.getActivePanel();
20859 Roo.log('pan or acitve pan is undefined');
20863 if (pan.tabId == this.getActivePanel().tabId) {
20867 if (false === cur.fireEvent('beforedeactivate')) {
20871 if(this.bullets > 0 && !Roo.isTouch){
20872 this.setActiveBullet(this.indexOfPanel(pan));
20875 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20877 //class="carousel-item carousel-item-next carousel-item-left"
20879 this.transition = true;
20880 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20881 var lr = dir == 'next' ? 'left' : 'right';
20882 pan.el.addClass(dir); // or prev
20883 pan.el.addClass('carousel-item-' + dir); // or prev
20884 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20885 cur.el.addClass(lr); // or right
20886 pan.el.addClass(lr);
20887 cur.el.addClass('carousel-item-' +lr); // or right
20888 pan.el.addClass('carousel-item-' +lr);
20892 cur.el.on('transitionend', function() {
20893 Roo.log("trans end?");
20895 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20896 pan.setActive(true);
20898 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20899 cur.setActive(false);
20901 _this.transition = false;
20903 }, this, { single: true } );
20908 cur.setActive(false);
20909 pan.setActive(true);
20914 showPanelNext : function()
20916 var i = this.indexOfPanel(this.getActivePanel());
20918 if (i >= this.tabs.length - 1 && !this.autoslide) {
20922 if (i >= this.tabs.length - 1 && this.autoslide) {
20926 this.showPanel(this.tabs[i+1]);
20929 showPanelPrev : function()
20931 var i = this.indexOfPanel(this.getActivePanel());
20933 if (i < 1 && !this.autoslide) {
20937 if (i < 1 && this.autoslide) {
20938 i = this.tabs.length;
20941 this.showPanel(this.tabs[i-1]);
20945 addBullet: function()
20947 if(!this.bullets || Roo.isTouch){
20950 var ctr = this.el.select('.carousel-bullets',true).first();
20951 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20952 var bullet = ctr.createChild({
20953 cls : 'bullet bullet-' + i
20954 },ctr.dom.lastChild);
20959 bullet.on('click', (function(e, el, o, ii, t){
20961 e.preventDefault();
20963 this.showPanel(ii);
20965 if(this.autoslide && this.slideFn){
20966 clearInterval(this.slideFn);
20967 this.slideFn = window.setInterval(function() {
20968 _this.showPanelNext();
20972 }).createDelegate(this, [i, bullet], true));
20977 setActiveBullet : function(i)
20983 Roo.each(this.el.select('.bullet', true).elements, function(el){
20984 el.removeClass('selected');
20987 var bullet = this.el.select('.bullet-' + i, true).first();
20993 bullet.addClass('selected');
21004 Roo.apply(Roo.bootstrap.TabGroup, {
21008 * register a Navigation Group
21009 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21011 register : function(navgrp)
21013 this.groups[navgrp.navId] = navgrp;
21017 * fetch a Navigation Group based on the navigation ID
21018 * if one does not exist , it will get created.
21019 * @param {string} the navgroup to add
21020 * @returns {Roo.bootstrap.NavGroup} the navgroup
21022 get: function(navId) {
21023 if (typeof(this.groups[navId]) == 'undefined') {
21024 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21026 return this.groups[navId] ;
21041 * @class Roo.bootstrap.TabPanel
21042 * @extends Roo.bootstrap.Component
21043 * Bootstrap TabPanel class
21044 * @cfg {Boolean} active panel active
21045 * @cfg {String} html panel content
21046 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21047 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21048 * @cfg {String} href click to link..
21049 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21053 * Create a new TabPanel
21054 * @param {Object} config The config object
21057 Roo.bootstrap.TabPanel = function(config){
21058 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21062 * Fires when the active status changes
21063 * @param {Roo.bootstrap.TabPanel} this
21064 * @param {Boolean} state the new state
21069 * @event beforedeactivate
21070 * Fires before a tab is de-activated - can be used to do validation on a form.
21071 * @param {Roo.bootstrap.TabPanel} this
21072 * @return {Boolean} false if there is an error
21075 'beforedeactivate': true
21078 this.tabId = this.tabId || Roo.id();
21082 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21089 touchSlide : false,
21090 getAutoCreate : function(){
21095 // item is needed for carousel - not sure if it has any effect otherwise
21096 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21097 html: this.html || ''
21101 cfg.cls += ' active';
21105 cfg.tabId = this.tabId;
21113 initEvents: function()
21115 var p = this.parent();
21117 this.navId = this.navId || p.navId;
21119 if (typeof(this.navId) != 'undefined') {
21120 // not really needed.. but just in case.. parent should be a NavGroup.
21121 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21125 var i = tg.tabs.length - 1;
21127 if(this.active && tg.bullets > 0 && i < tg.bullets){
21128 tg.setActiveBullet(i);
21132 this.el.on('click', this.onClick, this);
21134 if(Roo.isTouch && this.touchSlide){
21135 this.el.on("touchstart", this.onTouchStart, this);
21136 this.el.on("touchmove", this.onTouchMove, this);
21137 this.el.on("touchend", this.onTouchEnd, this);
21142 onRender : function(ct, position)
21144 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21147 setActive : function(state)
21149 Roo.log("panel - set active " + this.tabId + "=" + state);
21151 this.active = state;
21153 this.el.removeClass('active');
21155 } else if (!this.el.hasClass('active')) {
21156 this.el.addClass('active');
21159 this.fireEvent('changed', this, state);
21162 onClick : function(e)
21164 e.preventDefault();
21166 if(!this.href.length){
21170 window.location.href = this.href;
21179 onTouchStart : function(e)
21181 this.swiping = false;
21183 this.startX = e.browserEvent.touches[0].clientX;
21184 this.startY = e.browserEvent.touches[0].clientY;
21187 onTouchMove : function(e)
21189 this.swiping = true;
21191 this.endX = e.browserEvent.touches[0].clientX;
21192 this.endY = e.browserEvent.touches[0].clientY;
21195 onTouchEnd : function(e)
21202 var tabGroup = this.parent();
21204 if(this.endX > this.startX){ // swiping right
21205 tabGroup.showPanelPrev();
21209 if(this.startX > this.endX){ // swiping left
21210 tabGroup.showPanelNext();
21229 * @class Roo.bootstrap.DateField
21230 * @extends Roo.bootstrap.Input
21231 * Bootstrap DateField class
21232 * @cfg {Number} weekStart default 0
21233 * @cfg {String} viewMode default empty, (months|years)
21234 * @cfg {String} minViewMode default empty, (months|years)
21235 * @cfg {Number} startDate default -Infinity
21236 * @cfg {Number} endDate default Infinity
21237 * @cfg {Boolean} todayHighlight default false
21238 * @cfg {Boolean} todayBtn default false
21239 * @cfg {Boolean} calendarWeeks default false
21240 * @cfg {Object} daysOfWeekDisabled default empty
21241 * @cfg {Boolean} singleMode default false (true | false)
21243 * @cfg {Boolean} keyboardNavigation default true
21244 * @cfg {String} language default en
21247 * Create a new DateField
21248 * @param {Object} config The config object
21251 Roo.bootstrap.DateField = function(config){
21252 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21256 * Fires when this field show.
21257 * @param {Roo.bootstrap.DateField} this
21258 * @param {Mixed} date The date value
21263 * Fires when this field hide.
21264 * @param {Roo.bootstrap.DateField} this
21265 * @param {Mixed} date The date value
21270 * Fires when select a date.
21271 * @param {Roo.bootstrap.DateField} this
21272 * @param {Mixed} date The date value
21276 * @event beforeselect
21277 * Fires when before select a date.
21278 * @param {Roo.bootstrap.DateField} this
21279 * @param {Mixed} date The date value
21281 beforeselect : true
21285 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21288 * @cfg {String} format
21289 * The default date format string which can be overriden for localization support. The format must be
21290 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21294 * @cfg {String} altFormats
21295 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21296 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21298 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21306 todayHighlight : false,
21312 keyboardNavigation: true,
21314 calendarWeeks: false,
21316 startDate: -Infinity,
21320 daysOfWeekDisabled: [],
21324 singleMode : false,
21326 UTCDate: function()
21328 return new Date(Date.UTC.apply(Date, arguments));
21331 UTCToday: function()
21333 var today = new Date();
21334 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21337 getDate: function() {
21338 var d = this.getUTCDate();
21339 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21342 getUTCDate: function() {
21346 setDate: function(d) {
21347 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21350 setUTCDate: function(d) {
21352 this.setValue(this.formatDate(this.date));
21355 onRender: function(ct, position)
21358 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21360 this.language = this.language || 'en';
21361 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21362 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21364 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21365 this.format = this.format || 'm/d/y';
21366 this.isInline = false;
21367 this.isInput = true;
21368 this.component = this.el.select('.add-on', true).first() || false;
21369 this.component = (this.component && this.component.length === 0) ? false : this.component;
21370 this.hasInput = this.component && this.inputEl().length;
21372 if (typeof(this.minViewMode === 'string')) {
21373 switch (this.minViewMode) {
21375 this.minViewMode = 1;
21378 this.minViewMode = 2;
21381 this.minViewMode = 0;
21386 if (typeof(this.viewMode === 'string')) {
21387 switch (this.viewMode) {
21400 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21402 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21404 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21406 this.picker().on('mousedown', this.onMousedown, this);
21407 this.picker().on('click', this.onClick, this);
21409 this.picker().addClass('datepicker-dropdown');
21411 this.startViewMode = this.viewMode;
21413 if(this.singleMode){
21414 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21415 v.setVisibilityMode(Roo.Element.DISPLAY);
21419 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21420 v.setStyle('width', '189px');
21424 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21425 if(!this.calendarWeeks){
21430 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21431 v.attr('colspan', function(i, val){
21432 return parseInt(val) + 1;
21437 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21439 this.setStartDate(this.startDate);
21440 this.setEndDate(this.endDate);
21442 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21449 if(this.isInline) {
21454 picker : function()
21456 return this.pickerEl;
21457 // return this.el.select('.datepicker', true).first();
21460 fillDow: function()
21462 var dowCnt = this.weekStart;
21471 if(this.calendarWeeks){
21479 while (dowCnt < this.weekStart + 7) {
21483 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21487 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21490 fillMonths: function()
21493 var months = this.picker().select('>.datepicker-months td', true).first();
21495 months.dom.innerHTML = '';
21501 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21504 months.createChild(month);
21511 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;
21513 if (this.date < this.startDate) {
21514 this.viewDate = new Date(this.startDate);
21515 } else if (this.date > this.endDate) {
21516 this.viewDate = new Date(this.endDate);
21518 this.viewDate = new Date(this.date);
21526 var d = new Date(this.viewDate),
21527 year = d.getUTCFullYear(),
21528 month = d.getUTCMonth(),
21529 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21530 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21531 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21532 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21533 currentDate = this.date && this.date.valueOf(),
21534 today = this.UTCToday();
21536 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21538 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21540 // this.picker.select('>tfoot th.today').
21541 // .text(dates[this.language].today)
21542 // .toggle(this.todayBtn !== false);
21544 this.updateNavArrows();
21547 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21549 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21551 prevMonth.setUTCDate(day);
21553 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21555 var nextMonth = new Date(prevMonth);
21557 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21559 nextMonth = nextMonth.valueOf();
21561 var fillMonths = false;
21563 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21565 while(prevMonth.valueOf() <= nextMonth) {
21568 if (prevMonth.getUTCDay() === this.weekStart) {
21570 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21578 if(this.calendarWeeks){
21579 // ISO 8601: First week contains first thursday.
21580 // ISO also states week starts on Monday, but we can be more abstract here.
21582 // Start of current week: based on weekstart/current date
21583 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21584 // Thursday of this week
21585 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21586 // First Thursday of year, year from thursday
21587 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21588 // Calendar week: ms between thursdays, div ms per day, div 7 days
21589 calWeek = (th - yth) / 864e5 / 7 + 1;
21591 fillMonths.cn.push({
21599 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21601 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21604 if (this.todayHighlight &&
21605 prevMonth.getUTCFullYear() == today.getFullYear() &&
21606 prevMonth.getUTCMonth() == today.getMonth() &&
21607 prevMonth.getUTCDate() == today.getDate()) {
21608 clsName += ' today';
21611 if (currentDate && prevMonth.valueOf() === currentDate) {
21612 clsName += ' active';
21615 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21616 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21617 clsName += ' disabled';
21620 fillMonths.cn.push({
21622 cls: 'day ' + clsName,
21623 html: prevMonth.getDate()
21626 prevMonth.setDate(prevMonth.getDate()+1);
21629 var currentYear = this.date && this.date.getUTCFullYear();
21630 var currentMonth = this.date && this.date.getUTCMonth();
21632 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21634 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21635 v.removeClass('active');
21637 if(currentYear === year && k === currentMonth){
21638 v.addClass('active');
21641 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21642 v.addClass('disabled');
21648 year = parseInt(year/10, 10) * 10;
21650 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21652 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21655 for (var i = -1; i < 11; i++) {
21656 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21658 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21666 showMode: function(dir)
21669 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21672 Roo.each(this.picker().select('>div',true).elements, function(v){
21673 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21676 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21681 if(this.isInline) {
21685 this.picker().removeClass(['bottom', 'top']);
21687 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21689 * place to the top of element!
21693 this.picker().addClass('top');
21694 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21699 this.picker().addClass('bottom');
21701 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21704 parseDate : function(value)
21706 if(!value || value instanceof Date){
21709 var v = Date.parseDate(value, this.format);
21710 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21711 v = Date.parseDate(value, 'Y-m-d');
21713 if(!v && this.altFormats){
21714 if(!this.altFormatsArray){
21715 this.altFormatsArray = this.altFormats.split("|");
21717 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21718 v = Date.parseDate(value, this.altFormatsArray[i]);
21724 formatDate : function(date, fmt)
21726 return (!date || !(date instanceof Date)) ?
21727 date : date.dateFormat(fmt || this.format);
21730 onFocus : function()
21732 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21736 onBlur : function()
21738 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21740 var d = this.inputEl().getValue();
21747 showPopup : function()
21749 this.picker().show();
21753 this.fireEvent('showpopup', this, this.date);
21756 hidePopup : function()
21758 if(this.isInline) {
21761 this.picker().hide();
21762 this.viewMode = this.startViewMode;
21765 this.fireEvent('hidepopup', this, this.date);
21769 onMousedown: function(e)
21771 e.stopPropagation();
21772 e.preventDefault();
21777 Roo.bootstrap.DateField.superclass.keyup.call(this);
21781 setValue: function(v)
21783 if(this.fireEvent('beforeselect', this, v) !== false){
21784 var d = new Date(this.parseDate(v) ).clearTime();
21786 if(isNaN(d.getTime())){
21787 this.date = this.viewDate = '';
21788 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21792 v = this.formatDate(d);
21794 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21796 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21800 this.fireEvent('select', this, this.date);
21804 getValue: function()
21806 return this.formatDate(this.date);
21809 fireKey: function(e)
21811 if (!this.picker().isVisible()){
21812 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21818 var dateChanged = false,
21820 newDate, newViewDate;
21825 e.preventDefault();
21829 if (!this.keyboardNavigation) {
21832 dir = e.keyCode == 37 ? -1 : 1;
21835 newDate = this.moveYear(this.date, dir);
21836 newViewDate = this.moveYear(this.viewDate, dir);
21837 } else if (e.shiftKey){
21838 newDate = this.moveMonth(this.date, dir);
21839 newViewDate = this.moveMonth(this.viewDate, dir);
21841 newDate = new Date(this.date);
21842 newDate.setUTCDate(this.date.getUTCDate() + dir);
21843 newViewDate = new Date(this.viewDate);
21844 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21846 if (this.dateWithinRange(newDate)){
21847 this.date = newDate;
21848 this.viewDate = newViewDate;
21849 this.setValue(this.formatDate(this.date));
21851 e.preventDefault();
21852 dateChanged = true;
21857 if (!this.keyboardNavigation) {
21860 dir = e.keyCode == 38 ? -1 : 1;
21862 newDate = this.moveYear(this.date, dir);
21863 newViewDate = this.moveYear(this.viewDate, dir);
21864 } else if (e.shiftKey){
21865 newDate = this.moveMonth(this.date, dir);
21866 newViewDate = this.moveMonth(this.viewDate, dir);
21868 newDate = new Date(this.date);
21869 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21870 newViewDate = new Date(this.viewDate);
21871 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21873 if (this.dateWithinRange(newDate)){
21874 this.date = newDate;
21875 this.viewDate = newViewDate;
21876 this.setValue(this.formatDate(this.date));
21878 e.preventDefault();
21879 dateChanged = true;
21883 this.setValue(this.formatDate(this.date));
21885 e.preventDefault();
21888 this.setValue(this.formatDate(this.date));
21902 onClick: function(e)
21904 e.stopPropagation();
21905 e.preventDefault();
21907 var target = e.getTarget();
21909 if(target.nodeName.toLowerCase() === 'i'){
21910 target = Roo.get(target).dom.parentNode;
21913 var nodeName = target.nodeName;
21914 var className = target.className;
21915 var html = target.innerHTML;
21916 //Roo.log(nodeName);
21918 switch(nodeName.toLowerCase()) {
21920 switch(className) {
21926 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21927 switch(this.viewMode){
21929 this.viewDate = this.moveMonth(this.viewDate, dir);
21933 this.viewDate = this.moveYear(this.viewDate, dir);
21939 var date = new Date();
21940 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21942 this.setValue(this.formatDate(this.date));
21949 if (className.indexOf('disabled') < 0) {
21950 this.viewDate.setUTCDate(1);
21951 if (className.indexOf('month') > -1) {
21952 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21954 var year = parseInt(html, 10) || 0;
21955 this.viewDate.setUTCFullYear(year);
21959 if(this.singleMode){
21960 this.setValue(this.formatDate(this.viewDate));
21971 //Roo.log(className);
21972 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21973 var day = parseInt(html, 10) || 1;
21974 var year = (this.viewDate || new Date()).getUTCFullYear(),
21975 month = (this.viewDate || new Date()).getUTCMonth();
21977 if (className.indexOf('old') > -1) {
21984 } else if (className.indexOf('new') > -1) {
21992 //Roo.log([year,month,day]);
21993 this.date = this.UTCDate(year, month, day,0,0,0,0);
21994 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21996 //Roo.log(this.formatDate(this.date));
21997 this.setValue(this.formatDate(this.date));
22004 setStartDate: function(startDate)
22006 this.startDate = startDate || -Infinity;
22007 if (this.startDate !== -Infinity) {
22008 this.startDate = this.parseDate(this.startDate);
22011 this.updateNavArrows();
22014 setEndDate: function(endDate)
22016 this.endDate = endDate || Infinity;
22017 if (this.endDate !== Infinity) {
22018 this.endDate = this.parseDate(this.endDate);
22021 this.updateNavArrows();
22024 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22026 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22027 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22028 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22030 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22031 return parseInt(d, 10);
22034 this.updateNavArrows();
22037 updateNavArrows: function()
22039 if(this.singleMode){
22043 var d = new Date(this.viewDate),
22044 year = d.getUTCFullYear(),
22045 month = d.getUTCMonth();
22047 Roo.each(this.picker().select('.prev', true).elements, function(v){
22049 switch (this.viewMode) {
22052 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22058 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22065 Roo.each(this.picker().select('.next', true).elements, function(v){
22067 switch (this.viewMode) {
22070 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22076 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22084 moveMonth: function(date, dir)
22089 var new_date = new Date(date.valueOf()),
22090 day = new_date.getUTCDate(),
22091 month = new_date.getUTCMonth(),
22092 mag = Math.abs(dir),
22094 dir = dir > 0 ? 1 : -1;
22097 // If going back one month, make sure month is not current month
22098 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22100 return new_date.getUTCMonth() == month;
22102 // If going forward one month, make sure month is as expected
22103 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22105 return new_date.getUTCMonth() != new_month;
22107 new_month = month + dir;
22108 new_date.setUTCMonth(new_month);
22109 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22110 if (new_month < 0 || new_month > 11) {
22111 new_month = (new_month + 12) % 12;
22114 // For magnitudes >1, move one month at a time...
22115 for (var i=0; i<mag; i++) {
22116 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22117 new_date = this.moveMonth(new_date, dir);
22119 // ...then reset the day, keeping it in the new month
22120 new_month = new_date.getUTCMonth();
22121 new_date.setUTCDate(day);
22123 return new_month != new_date.getUTCMonth();
22126 // Common date-resetting loop -- if date is beyond end of month, make it
22129 new_date.setUTCDate(--day);
22130 new_date.setUTCMonth(new_month);
22135 moveYear: function(date, dir)
22137 return this.moveMonth(date, dir*12);
22140 dateWithinRange: function(date)
22142 return date >= this.startDate && date <= this.endDate;
22148 this.picker().remove();
22151 validateValue : function(value)
22153 if(this.getVisibilityEl().hasClass('hidden')){
22157 if(value.length < 1) {
22158 if(this.allowBlank){
22164 if(value.length < this.minLength){
22167 if(value.length > this.maxLength){
22171 var vt = Roo.form.VTypes;
22172 if(!vt[this.vtype](value, this)){
22176 if(typeof this.validator == "function"){
22177 var msg = this.validator(value);
22183 if(this.regex && !this.regex.test(value)){
22187 if(typeof(this.parseDate(value)) == 'undefined'){
22191 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22195 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22205 this.date = this.viewDate = '';
22207 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22212 Roo.apply(Roo.bootstrap.DateField, {
22223 html: '<i class="fa fa-arrow-left"/>'
22233 html: '<i class="fa fa-arrow-right"/>'
22275 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22276 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22277 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22278 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22279 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22292 navFnc: 'FullYear',
22297 navFnc: 'FullYear',
22302 Roo.apply(Roo.bootstrap.DateField, {
22306 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22310 cls: 'datepicker-days',
22314 cls: 'table-condensed',
22316 Roo.bootstrap.DateField.head,
22320 Roo.bootstrap.DateField.footer
22327 cls: 'datepicker-months',
22331 cls: 'table-condensed',
22333 Roo.bootstrap.DateField.head,
22334 Roo.bootstrap.DateField.content,
22335 Roo.bootstrap.DateField.footer
22342 cls: 'datepicker-years',
22346 cls: 'table-condensed',
22348 Roo.bootstrap.DateField.head,
22349 Roo.bootstrap.DateField.content,
22350 Roo.bootstrap.DateField.footer
22369 * @class Roo.bootstrap.TimeField
22370 * @extends Roo.bootstrap.Input
22371 * Bootstrap DateField class
22375 * Create a new TimeField
22376 * @param {Object} config The config object
22379 Roo.bootstrap.TimeField = function(config){
22380 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22384 * Fires when this field show.
22385 * @param {Roo.bootstrap.DateField} thisthis
22386 * @param {Mixed} date The date value
22391 * Fires when this field hide.
22392 * @param {Roo.bootstrap.DateField} this
22393 * @param {Mixed} date The date value
22398 * Fires when select a date.
22399 * @param {Roo.bootstrap.DateField} this
22400 * @param {Mixed} date The date value
22406 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22409 * @cfg {String} format
22410 * The default time format string which can be overriden for localization support. The format must be
22411 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22415 getAutoCreate : function()
22417 this.after = '<i class="fa far fa-clock"></i>';
22418 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22422 onRender: function(ct, position)
22425 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22427 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22429 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22431 this.pop = this.picker().select('>.datepicker-time',true).first();
22432 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22434 this.picker().on('mousedown', this.onMousedown, this);
22435 this.picker().on('click', this.onClick, this);
22437 this.picker().addClass('datepicker-dropdown');
22442 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22443 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22444 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22445 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22446 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22447 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22451 fireKey: function(e){
22452 if (!this.picker().isVisible()){
22453 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22459 e.preventDefault();
22467 this.onTogglePeriod();
22470 this.onIncrementMinutes();
22473 this.onDecrementMinutes();
22482 onClick: function(e) {
22483 e.stopPropagation();
22484 e.preventDefault();
22487 picker : function()
22489 return this.pickerEl;
22492 fillTime: function()
22494 var time = this.pop.select('tbody', true).first();
22496 time.dom.innerHTML = '';
22511 cls: 'hours-up fa fas fa-chevron-up'
22531 cls: 'minutes-up fa fas fa-chevron-up'
22552 cls: 'timepicker-hour',
22567 cls: 'timepicker-minute',
22582 cls: 'btn btn-primary period',
22604 cls: 'hours-down fa fas fa-chevron-down'
22624 cls: 'minutes-down fa fas fa-chevron-down'
22642 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22649 var hours = this.time.getHours();
22650 var minutes = this.time.getMinutes();
22663 hours = hours - 12;
22667 hours = '0' + hours;
22671 minutes = '0' + minutes;
22674 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22675 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22676 this.pop.select('button', true).first().dom.innerHTML = period;
22682 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22684 var cls = ['bottom'];
22686 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22693 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22697 //this.picker().setXY(20000,20000);
22698 this.picker().addClass(cls.join('-'));
22702 Roo.each(cls, function(c){
22707 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22708 //_this.picker().setTop(_this.inputEl().getHeight());
22712 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22714 //_this.picker().setTop(0 - _this.picker().getHeight());
22719 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22723 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22731 onFocus : function()
22733 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22737 onBlur : function()
22739 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22745 this.picker().show();
22750 this.fireEvent('show', this, this.date);
22755 this.picker().hide();
22758 this.fireEvent('hide', this, this.date);
22761 setTime : function()
22764 this.setValue(this.time.format(this.format));
22766 this.fireEvent('select', this, this.date);
22771 onMousedown: function(e){
22772 e.stopPropagation();
22773 e.preventDefault();
22776 onIncrementHours: function()
22778 Roo.log('onIncrementHours');
22779 this.time = this.time.add(Date.HOUR, 1);
22784 onDecrementHours: function()
22786 Roo.log('onDecrementHours');
22787 this.time = this.time.add(Date.HOUR, -1);
22791 onIncrementMinutes: function()
22793 Roo.log('onIncrementMinutes');
22794 this.time = this.time.add(Date.MINUTE, 1);
22798 onDecrementMinutes: function()
22800 Roo.log('onDecrementMinutes');
22801 this.time = this.time.add(Date.MINUTE, -1);
22805 onTogglePeriod: function()
22807 Roo.log('onTogglePeriod');
22808 this.time = this.time.add(Date.HOUR, 12);
22816 Roo.apply(Roo.bootstrap.TimeField, {
22820 cls: 'datepicker dropdown-menu',
22824 cls: 'datepicker-time',
22828 cls: 'table-condensed',
22857 cls: 'btn btn-info ok',
22885 * @class Roo.bootstrap.MonthField
22886 * @extends Roo.bootstrap.Input
22887 * Bootstrap MonthField class
22889 * @cfg {String} language default en
22892 * Create a new MonthField
22893 * @param {Object} config The config object
22896 Roo.bootstrap.MonthField = function(config){
22897 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22902 * Fires when this field show.
22903 * @param {Roo.bootstrap.MonthField} this
22904 * @param {Mixed} date The date value
22909 * Fires when this field hide.
22910 * @param {Roo.bootstrap.MonthField} this
22911 * @param {Mixed} date The date value
22916 * Fires when select a date.
22917 * @param {Roo.bootstrap.MonthField} this
22918 * @param {String} oldvalue The old value
22919 * @param {String} newvalue The new value
22925 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22927 onRender: function(ct, position)
22930 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22932 this.language = this.language || 'en';
22933 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22934 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22936 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22937 this.isInline = false;
22938 this.isInput = true;
22939 this.component = this.el.select('.add-on', true).first() || false;
22940 this.component = (this.component && this.component.length === 0) ? false : this.component;
22941 this.hasInput = this.component && this.inputEL().length;
22943 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22945 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22947 this.picker().on('mousedown', this.onMousedown, this);
22948 this.picker().on('click', this.onClick, this);
22950 this.picker().addClass('datepicker-dropdown');
22952 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22953 v.setStyle('width', '189px');
22960 if(this.isInline) {
22966 setValue: function(v, suppressEvent)
22968 var o = this.getValue();
22970 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22974 if(suppressEvent !== true){
22975 this.fireEvent('select', this, o, v);
22980 getValue: function()
22985 onClick: function(e)
22987 e.stopPropagation();
22988 e.preventDefault();
22990 var target = e.getTarget();
22992 if(target.nodeName.toLowerCase() === 'i'){
22993 target = Roo.get(target).dom.parentNode;
22996 var nodeName = target.nodeName;
22997 var className = target.className;
22998 var html = target.innerHTML;
23000 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23004 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23006 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23012 picker : function()
23014 return this.pickerEl;
23017 fillMonths: function()
23020 var months = this.picker().select('>.datepicker-months td', true).first();
23022 months.dom.innerHTML = '';
23028 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23031 months.createChild(month);
23040 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23041 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23044 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23045 e.removeClass('active');
23047 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23048 e.addClass('active');
23055 if(this.isInline) {
23059 this.picker().removeClass(['bottom', 'top']);
23061 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23063 * place to the top of element!
23067 this.picker().addClass('top');
23068 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23073 this.picker().addClass('bottom');
23075 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23078 onFocus : function()
23080 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23084 onBlur : function()
23086 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23088 var d = this.inputEl().getValue();
23097 this.picker().show();
23098 this.picker().select('>.datepicker-months', true).first().show();
23102 this.fireEvent('show', this, this.date);
23107 if(this.isInline) {
23110 this.picker().hide();
23111 this.fireEvent('hide', this, this.date);
23115 onMousedown: function(e)
23117 e.stopPropagation();
23118 e.preventDefault();
23123 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23127 fireKey: function(e)
23129 if (!this.picker().isVisible()){
23130 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23141 e.preventDefault();
23145 dir = e.keyCode == 37 ? -1 : 1;
23147 this.vIndex = this.vIndex + dir;
23149 if(this.vIndex < 0){
23153 if(this.vIndex > 11){
23157 if(isNaN(this.vIndex)){
23161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23167 dir = e.keyCode == 38 ? -1 : 1;
23169 this.vIndex = this.vIndex + dir * 4;
23171 if(this.vIndex < 0){
23175 if(this.vIndex > 11){
23179 if(isNaN(this.vIndex)){
23183 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23188 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23189 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23193 e.preventDefault();
23196 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23197 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23213 this.picker().remove();
23218 Roo.apply(Roo.bootstrap.MonthField, {
23237 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23238 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23243 Roo.apply(Roo.bootstrap.MonthField, {
23247 cls: 'datepicker dropdown-menu roo-dynamic',
23251 cls: 'datepicker-months',
23255 cls: 'table-condensed',
23257 Roo.bootstrap.DateField.content
23277 * @class Roo.bootstrap.CheckBox
23278 * @extends Roo.bootstrap.Input
23279 * Bootstrap CheckBox class
23281 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23282 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23283 * @cfg {String} boxLabel The text that appears beside the checkbox
23284 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23285 * @cfg {Boolean} checked initnal the element
23286 * @cfg {Boolean} inline inline the element (default false)
23287 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23288 * @cfg {String} tooltip label tooltip
23291 * Create a new CheckBox
23292 * @param {Object} config The config object
23295 Roo.bootstrap.CheckBox = function(config){
23296 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23301 * Fires when the element is checked or unchecked.
23302 * @param {Roo.bootstrap.CheckBox} this This input
23303 * @param {Boolean} checked The new checked value
23308 * Fires when the element is click.
23309 * @param {Roo.bootstrap.CheckBox} this This input
23316 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23318 inputType: 'checkbox',
23327 // checkbox success does not make any sense really..
23332 getAutoCreate : function()
23334 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23340 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23343 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23349 type : this.inputType,
23350 value : this.inputValue,
23351 cls : 'roo-' + this.inputType, //'form-box',
23352 placeholder : this.placeholder || ''
23356 if(this.inputType != 'radio'){
23360 cls : 'roo-hidden-value',
23361 value : this.checked ? this.inputValue : this.valueOff
23366 if (this.weight) { // Validity check?
23367 cfg.cls += " " + this.inputType + "-" + this.weight;
23370 if (this.disabled) {
23371 input.disabled=true;
23375 input.checked = this.checked;
23380 input.name = this.name;
23382 if(this.inputType != 'radio'){
23383 hidden.name = this.name;
23384 input.name = '_hidden_' + this.name;
23389 input.cls += ' input-' + this.size;
23394 ['xs','sm','md','lg'].map(function(size){
23395 if (settings[size]) {
23396 cfg.cls += ' col-' + size + '-' + settings[size];
23400 var inputblock = input;
23402 if (this.before || this.after) {
23405 cls : 'input-group',
23410 inputblock.cn.push({
23412 cls : 'input-group-addon',
23417 inputblock.cn.push(input);
23419 if(this.inputType != 'radio'){
23420 inputblock.cn.push(hidden);
23424 inputblock.cn.push({
23426 cls : 'input-group-addon',
23432 var boxLabelCfg = false;
23438 //'for': id, // box label is handled by onclick - so no for...
23440 html: this.boxLabel
23443 boxLabelCfg.tooltip = this.tooltip;
23449 if (align ==='left' && this.fieldLabel.length) {
23450 // Roo.log("left and has label");
23455 cls : 'control-label',
23456 html : this.fieldLabel
23467 cfg.cn[1].cn.push(boxLabelCfg);
23470 if(this.labelWidth > 12){
23471 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23474 if(this.labelWidth < 13 && this.labelmd == 0){
23475 this.labelmd = this.labelWidth;
23478 if(this.labellg > 0){
23479 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23480 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23483 if(this.labelmd > 0){
23484 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23485 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23488 if(this.labelsm > 0){
23489 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23490 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23493 if(this.labelxs > 0){
23494 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23495 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23498 } else if ( this.fieldLabel.length) {
23499 // Roo.log(" label");
23503 tag: this.boxLabel ? 'span' : 'label',
23505 cls: 'control-label box-input-label',
23506 //cls : 'input-group-addon',
23507 html : this.fieldLabel
23514 cfg.cn.push(boxLabelCfg);
23519 // Roo.log(" no label && no align");
23520 cfg.cn = [ inputblock ] ;
23522 cfg.cn.push(boxLabelCfg);
23530 if(this.inputType != 'radio'){
23531 cfg.cn.push(hidden);
23539 * return the real input element.
23541 inputEl: function ()
23543 return this.el.select('input.roo-' + this.inputType,true).first();
23545 hiddenEl: function ()
23547 return this.el.select('input.roo-hidden-value',true).first();
23550 labelEl: function()
23552 return this.el.select('label.control-label',true).first();
23554 /* depricated... */
23558 return this.labelEl();
23561 boxLabelEl: function()
23563 return this.el.select('label.box-label',true).first();
23566 initEvents : function()
23568 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23570 this.inputEl().on('click', this.onClick, this);
23572 if (this.boxLabel) {
23573 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23576 this.startValue = this.getValue();
23579 Roo.bootstrap.CheckBox.register(this);
23583 onClick : function(e)
23585 if(this.fireEvent('click', this, e) !== false){
23586 this.setChecked(!this.checked);
23591 setChecked : function(state,suppressEvent)
23593 this.startValue = this.getValue();
23595 if(this.inputType == 'radio'){
23597 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23598 e.dom.checked = false;
23601 this.inputEl().dom.checked = true;
23603 this.inputEl().dom.value = this.inputValue;
23605 if(suppressEvent !== true){
23606 this.fireEvent('check', this, true);
23614 this.checked = state;
23616 this.inputEl().dom.checked = state;
23619 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23621 if(suppressEvent !== true){
23622 this.fireEvent('check', this, state);
23628 getValue : function()
23630 if(this.inputType == 'radio'){
23631 return this.getGroupValue();
23634 return this.hiddenEl().dom.value;
23638 getGroupValue : function()
23640 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23644 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23647 setValue : function(v,suppressEvent)
23649 if(this.inputType == 'radio'){
23650 this.setGroupValue(v, suppressEvent);
23654 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23659 setGroupValue : function(v, suppressEvent)
23661 this.startValue = this.getValue();
23663 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23664 e.dom.checked = false;
23666 if(e.dom.value == v){
23667 e.dom.checked = true;
23671 if(suppressEvent !== true){
23672 this.fireEvent('check', this, true);
23680 validate : function()
23682 if(this.getVisibilityEl().hasClass('hidden')){
23688 (this.inputType == 'radio' && this.validateRadio()) ||
23689 (this.inputType == 'checkbox' && this.validateCheckbox())
23695 this.markInvalid();
23699 validateRadio : function()
23701 if(this.getVisibilityEl().hasClass('hidden')){
23705 if(this.allowBlank){
23711 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23712 if(!e.dom.checked){
23724 validateCheckbox : function()
23727 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23728 //return (this.getValue() == this.inputValue) ? true : false;
23731 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23739 for(var i in group){
23740 if(group[i].el.isVisible(true)){
23748 for(var i in group){
23753 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23760 * Mark this field as valid
23762 markValid : function()
23766 this.fireEvent('valid', this);
23768 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23771 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23778 if(this.inputType == 'radio'){
23779 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23780 var fg = e.findParent('.form-group', false, true);
23781 if (Roo.bootstrap.version == 3) {
23782 fg.removeClass([_this.invalidClass, _this.validClass]);
23783 fg.addClass(_this.validClass);
23785 fg.removeClass(['is-valid', 'is-invalid']);
23786 fg.addClass('is-valid');
23794 var fg = this.el.findParent('.form-group', false, true);
23795 if (Roo.bootstrap.version == 3) {
23796 fg.removeClass([this.invalidClass, this.validClass]);
23797 fg.addClass(this.validClass);
23799 fg.removeClass(['is-valid', 'is-invalid']);
23800 fg.addClass('is-valid');
23805 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23811 for(var i in group){
23812 var fg = group[i].el.findParent('.form-group', false, true);
23813 if (Roo.bootstrap.version == 3) {
23814 fg.removeClass([this.invalidClass, this.validClass]);
23815 fg.addClass(this.validClass);
23817 fg.removeClass(['is-valid', 'is-invalid']);
23818 fg.addClass('is-valid');
23824 * Mark this field as invalid
23825 * @param {String} msg The validation message
23827 markInvalid : function(msg)
23829 if(this.allowBlank){
23835 this.fireEvent('invalid', this, msg);
23837 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23840 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23844 label.markInvalid();
23847 if(this.inputType == 'radio'){
23849 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23850 var fg = e.findParent('.form-group', false, true);
23851 if (Roo.bootstrap.version == 3) {
23852 fg.removeClass([_this.invalidClass, _this.validClass]);
23853 fg.addClass(_this.invalidClass);
23855 fg.removeClass(['is-invalid', 'is-valid']);
23856 fg.addClass('is-invalid');
23864 var fg = this.el.findParent('.form-group', false, true);
23865 if (Roo.bootstrap.version == 3) {
23866 fg.removeClass([_this.invalidClass, _this.validClass]);
23867 fg.addClass(_this.invalidClass);
23869 fg.removeClass(['is-invalid', 'is-valid']);
23870 fg.addClass('is-invalid');
23875 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23881 for(var i in group){
23882 var fg = group[i].el.findParent('.form-group', false, true);
23883 if (Roo.bootstrap.version == 3) {
23884 fg.removeClass([_this.invalidClass, _this.validClass]);
23885 fg.addClass(_this.invalidClass);
23887 fg.removeClass(['is-invalid', 'is-valid']);
23888 fg.addClass('is-invalid');
23894 clearInvalid : function()
23896 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23898 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23900 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23902 if (label && label.iconEl) {
23903 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23904 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23908 disable : function()
23910 if(this.inputType != 'radio'){
23911 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23918 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23919 _this.getActionEl().addClass(this.disabledClass);
23920 e.dom.disabled = true;
23924 this.disabled = true;
23925 this.fireEvent("disable", this);
23929 enable : function()
23931 if(this.inputType != 'radio'){
23932 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23939 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23940 _this.getActionEl().removeClass(this.disabledClass);
23941 e.dom.disabled = false;
23945 this.disabled = false;
23946 this.fireEvent("enable", this);
23950 setBoxLabel : function(v)
23955 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23961 Roo.apply(Roo.bootstrap.CheckBox, {
23966 * register a CheckBox Group
23967 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23969 register : function(checkbox)
23971 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23972 this.groups[checkbox.groupId] = {};
23975 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23979 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23983 * fetch a CheckBox Group based on the group ID
23984 * @param {string} the group ID
23985 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23987 get: function(groupId) {
23988 if (typeof(this.groups[groupId]) == 'undefined') {
23992 return this.groups[groupId] ;
24005 * @class Roo.bootstrap.Radio
24006 * @extends Roo.bootstrap.Component
24007 * Bootstrap Radio class
24008 * @cfg {String} boxLabel - the label associated
24009 * @cfg {String} value - the value of radio
24012 * Create a new Radio
24013 * @param {Object} config The config object
24015 Roo.bootstrap.Radio = function(config){
24016 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24020 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24026 getAutoCreate : function()
24030 cls : 'form-group radio',
24035 html : this.boxLabel
24043 initEvents : function()
24045 this.parent().register(this);
24047 this.el.on('click', this.onClick, this);
24051 onClick : function(e)
24053 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24054 this.setChecked(true);
24058 setChecked : function(state, suppressEvent)
24060 this.parent().setValue(this.value, suppressEvent);
24064 setBoxLabel : function(v)
24069 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24084 * @class Roo.bootstrap.SecurePass
24085 * @extends Roo.bootstrap.Input
24086 * Bootstrap SecurePass class
24090 * Create a new SecurePass
24091 * @param {Object} config The config object
24094 Roo.bootstrap.SecurePass = function (config) {
24095 // these go here, so the translation tool can replace them..
24097 PwdEmpty: "Please type a password, and then retype it to confirm.",
24098 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24099 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24100 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24101 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24102 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24103 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24104 TooWeak: "Your password is Too Weak."
24106 this.meterLabel = "Password strength:";
24107 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24108 this.meterClass = [
24109 "roo-password-meter-tooweak",
24110 "roo-password-meter-weak",
24111 "roo-password-meter-medium",
24112 "roo-password-meter-strong",
24113 "roo-password-meter-grey"
24118 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24121 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24123 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24125 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24126 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24127 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24128 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24129 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24130 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24131 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24141 * @cfg {String/Object} Label for the strength meter (defaults to
24142 * 'Password strength:')
24147 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24148 * ['Weak', 'Medium', 'Strong'])
24151 pwdStrengths: false,
24164 initEvents: function ()
24166 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24168 if (this.el.is('input[type=password]') && Roo.isSafari) {
24169 this.el.on('keydown', this.SafariOnKeyDown, this);
24172 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24175 onRender: function (ct, position)
24177 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24178 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24179 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24181 this.trigger.createChild({
24186 cls: 'roo-password-meter-grey col-xs-12',
24189 //width: this.meterWidth + 'px'
24193 cls: 'roo-password-meter-text'
24199 if (this.hideTrigger) {
24200 this.trigger.setDisplayed(false);
24202 this.setSize(this.width || '', this.height || '');
24205 onDestroy: function ()
24207 if (this.trigger) {
24208 this.trigger.removeAllListeners();
24209 this.trigger.remove();
24212 this.wrap.remove();
24214 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24217 checkStrength: function ()
24219 var pwd = this.inputEl().getValue();
24220 if (pwd == this._lastPwd) {
24225 if (this.ClientSideStrongPassword(pwd)) {
24227 } else if (this.ClientSideMediumPassword(pwd)) {
24229 } else if (this.ClientSideWeakPassword(pwd)) {
24235 Roo.log('strength1: ' + strength);
24237 //var pm = this.trigger.child('div/div/div').dom;
24238 var pm = this.trigger.child('div/div');
24239 pm.removeClass(this.meterClass);
24240 pm.addClass(this.meterClass[strength]);
24243 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24245 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24247 this._lastPwd = pwd;
24251 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24253 this._lastPwd = '';
24255 var pm = this.trigger.child('div/div');
24256 pm.removeClass(this.meterClass);
24257 pm.addClass('roo-password-meter-grey');
24260 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24263 this.inputEl().dom.type='password';
24266 validateValue: function (value)
24268 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24271 if (value.length == 0) {
24272 if (this.allowBlank) {
24273 this.clearInvalid();
24277 this.markInvalid(this.errors.PwdEmpty);
24278 this.errorMsg = this.errors.PwdEmpty;
24286 if (!value.match(/[\x21-\x7e]+/)) {
24287 this.markInvalid(this.errors.PwdBadChar);
24288 this.errorMsg = this.errors.PwdBadChar;
24291 if (value.length < 6) {
24292 this.markInvalid(this.errors.PwdShort);
24293 this.errorMsg = this.errors.PwdShort;
24296 if (value.length > 16) {
24297 this.markInvalid(this.errors.PwdLong);
24298 this.errorMsg = this.errors.PwdLong;
24302 if (this.ClientSideStrongPassword(value)) {
24304 } else if (this.ClientSideMediumPassword(value)) {
24306 } else if (this.ClientSideWeakPassword(value)) {
24313 if (strength < 2) {
24314 //this.markInvalid(this.errors.TooWeak);
24315 this.errorMsg = this.errors.TooWeak;
24320 console.log('strength2: ' + strength);
24322 //var pm = this.trigger.child('div/div/div').dom;
24324 var pm = this.trigger.child('div/div');
24325 pm.removeClass(this.meterClass);
24326 pm.addClass(this.meterClass[strength]);
24328 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24330 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24332 this.errorMsg = '';
24336 CharacterSetChecks: function (type)
24339 this.fResult = false;
24342 isctype: function (character, type)
24345 case this.kCapitalLetter:
24346 if (character >= 'A' && character <= 'Z') {
24351 case this.kSmallLetter:
24352 if (character >= 'a' && character <= 'z') {
24358 if (character >= '0' && character <= '9') {
24363 case this.kPunctuation:
24364 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24375 IsLongEnough: function (pwd, size)
24377 return !(pwd == null || isNaN(size) || pwd.length < size);
24380 SpansEnoughCharacterSets: function (word, nb)
24382 if (!this.IsLongEnough(word, nb))
24387 var characterSetChecks = new Array(
24388 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24389 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24392 for (var index = 0; index < word.length; ++index) {
24393 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24394 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24395 characterSetChecks[nCharSet].fResult = true;
24402 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24403 if (characterSetChecks[nCharSet].fResult) {
24408 if (nCharSets < nb) {
24414 ClientSideStrongPassword: function (pwd)
24416 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24419 ClientSideMediumPassword: function (pwd)
24421 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24424 ClientSideWeakPassword: function (pwd)
24426 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24429 })//<script type="text/javascript">
24432 * Based Ext JS Library 1.1.1
24433 * Copyright(c) 2006-2007, Ext JS, LLC.
24439 * @class Roo.HtmlEditorCore
24440 * @extends Roo.Component
24441 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24443 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24446 Roo.HtmlEditorCore = function(config){
24449 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24454 * @event initialize
24455 * Fires when the editor is fully initialized (including the iframe)
24456 * @param {Roo.HtmlEditorCore} this
24461 * Fires when the editor is first receives the focus. Any insertion must wait
24462 * until after this event.
24463 * @param {Roo.HtmlEditorCore} this
24467 * @event beforesync
24468 * Fires before the textarea is updated with content from the editor iframe. Return false
24469 * to cancel the sync.
24470 * @param {Roo.HtmlEditorCore} this
24471 * @param {String} html
24475 * @event beforepush
24476 * Fires before the iframe editor is updated with content from the textarea. Return false
24477 * to cancel the push.
24478 * @param {Roo.HtmlEditorCore} this
24479 * @param {String} html
24484 * Fires when the textarea is updated with content from the editor iframe.
24485 * @param {Roo.HtmlEditorCore} this
24486 * @param {String} html
24491 * Fires when the iframe editor is updated with content from the textarea.
24492 * @param {Roo.HtmlEditorCore} this
24493 * @param {String} html
24498 * @event editorevent
24499 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24500 * @param {Roo.HtmlEditorCore} this
24506 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24508 // defaults : white / black...
24509 this.applyBlacklists();
24516 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24520 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24526 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24531 * @cfg {Number} height (in pixels)
24535 * @cfg {Number} width (in pixels)
24540 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24543 stylesheets: false,
24548 // private properties
24549 validationEvent : false,
24551 initialized : false,
24553 sourceEditMode : false,
24554 onFocus : Roo.emptyFn,
24556 hideMode:'offsets',
24560 // blacklist + whitelisted elements..
24567 * Protected method that will not generally be called directly. It
24568 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24569 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24571 getDocMarkup : function(){
24575 // inherit styels from page...??
24576 if (this.stylesheets === false) {
24578 Roo.get(document.head).select('style').each(function(node) {
24579 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24582 Roo.get(document.head).select('link').each(function(node) {
24583 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24586 } else if (!this.stylesheets.length) {
24588 st = '<style type="text/css">' +
24589 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24592 for (var i in this.stylesheets) {
24593 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24598 st += '<style type="text/css">' +
24599 'IMG { cursor: pointer } ' +
24602 var cls = 'roo-htmleditor-body';
24604 if(this.bodyCls.length){
24605 cls += ' ' + this.bodyCls;
24608 return '<html><head>' + st +
24609 //<style type="text/css">' +
24610 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24612 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24616 onRender : function(ct, position)
24619 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24620 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24623 this.el.dom.style.border = '0 none';
24624 this.el.dom.setAttribute('tabIndex', -1);
24625 this.el.addClass('x-hidden hide');
24629 if(Roo.isIE){ // fix IE 1px bogus margin
24630 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24634 this.frameId = Roo.id();
24638 var iframe = this.owner.wrap.createChild({
24640 cls: 'form-control', // bootstrap..
24642 name: this.frameId,
24643 frameBorder : 'no',
24644 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24649 this.iframe = iframe.dom;
24651 this.assignDocWin();
24653 this.doc.designMode = 'on';
24656 this.doc.write(this.getDocMarkup());
24660 var task = { // must defer to wait for browser to be ready
24662 //console.log("run task?" + this.doc.readyState);
24663 this.assignDocWin();
24664 if(this.doc.body || this.doc.readyState == 'complete'){
24666 this.doc.designMode="on";
24670 Roo.TaskMgr.stop(task);
24671 this.initEditor.defer(10, this);
24678 Roo.TaskMgr.start(task);
24683 onResize : function(w, h)
24685 Roo.log('resize: ' +w + ',' + h );
24686 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24690 if(typeof w == 'number'){
24692 this.iframe.style.width = w + 'px';
24694 if(typeof h == 'number'){
24696 this.iframe.style.height = h + 'px';
24698 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24705 * Toggles the editor between standard and source edit mode.
24706 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24708 toggleSourceEdit : function(sourceEditMode){
24710 this.sourceEditMode = sourceEditMode === true;
24712 if(this.sourceEditMode){
24714 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24717 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24718 //this.iframe.className = '';
24721 //this.setSize(this.owner.wrap.getSize());
24722 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24729 * Protected method that will not generally be called directly. If you need/want
24730 * custom HTML cleanup, this is the method you should override.
24731 * @param {String} html The HTML to be cleaned
24732 * return {String} The cleaned HTML
24734 cleanHtml : function(html){
24735 html = String(html);
24736 if(html.length > 5){
24737 if(Roo.isSafari){ // strip safari nonsense
24738 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24741 if(html == ' '){
24748 * HTML Editor -> Textarea
24749 * Protected method that will not generally be called directly. Syncs the contents
24750 * of the editor iframe with the textarea.
24752 syncValue : function(){
24753 if(this.initialized){
24754 var bd = (this.doc.body || this.doc.documentElement);
24755 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24756 var html = bd.innerHTML;
24758 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24759 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24761 html = '<div style="'+m[0]+'">' + html + '</div>';
24764 html = this.cleanHtml(html);
24765 // fix up the special chars.. normaly like back quotes in word...
24766 // however we do not want to do this with chinese..
24767 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24769 var cc = match.charCodeAt();
24771 // Get the character value, handling surrogate pairs
24772 if (match.length == 2) {
24773 // It's a surrogate pair, calculate the Unicode code point
24774 var high = match.charCodeAt(0) - 0xD800;
24775 var low = match.charCodeAt(1) - 0xDC00;
24776 cc = (high * 0x400) + low + 0x10000;
24778 (cc >= 0x4E00 && cc < 0xA000 ) ||
24779 (cc >= 0x3400 && cc < 0x4E00 ) ||
24780 (cc >= 0xf900 && cc < 0xfb00 )
24785 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24786 return "&#" + cc + ";";
24793 if(this.owner.fireEvent('beforesync', this, html) !== false){
24794 this.el.dom.value = html;
24795 this.owner.fireEvent('sync', this, html);
24801 * Protected method that will not generally be called directly. Pushes the value of the textarea
24802 * into the iframe editor.
24804 pushValue : function(){
24805 if(this.initialized){
24806 var v = this.el.dom.value.trim();
24808 // if(v.length < 1){
24812 if(this.owner.fireEvent('beforepush', this, v) !== false){
24813 var d = (this.doc.body || this.doc.documentElement);
24815 this.cleanUpPaste();
24816 this.el.dom.value = d.innerHTML;
24817 this.owner.fireEvent('push', this, v);
24823 deferFocus : function(){
24824 this.focus.defer(10, this);
24828 focus : function(){
24829 if(this.win && !this.sourceEditMode){
24836 assignDocWin: function()
24838 var iframe = this.iframe;
24841 this.doc = iframe.contentWindow.document;
24842 this.win = iframe.contentWindow;
24844 // if (!Roo.get(this.frameId)) {
24847 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24848 // this.win = Roo.get(this.frameId).dom.contentWindow;
24850 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24854 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24855 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24860 initEditor : function(){
24861 //console.log("INIT EDITOR");
24862 this.assignDocWin();
24866 this.doc.designMode="on";
24868 this.doc.write(this.getDocMarkup());
24871 var dbody = (this.doc.body || this.doc.documentElement);
24872 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24873 // this copies styles from the containing element into thsi one..
24874 // not sure why we need all of this..
24875 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24877 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24878 //ss['background-attachment'] = 'fixed'; // w3c
24879 dbody.bgProperties = 'fixed'; // ie
24880 //Roo.DomHelper.applyStyles(dbody, ss);
24881 Roo.EventManager.on(this.doc, {
24882 //'mousedown': this.onEditorEvent,
24883 'mouseup': this.onEditorEvent,
24884 'dblclick': this.onEditorEvent,
24885 'click': this.onEditorEvent,
24886 'keyup': this.onEditorEvent,
24891 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24893 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24894 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24896 this.initialized = true;
24898 this.owner.fireEvent('initialize', this);
24903 onDestroy : function(){
24909 //for (var i =0; i < this.toolbars.length;i++) {
24910 // // fixme - ask toolbars for heights?
24911 // this.toolbars[i].onDestroy();
24914 //this.wrap.dom.innerHTML = '';
24915 //this.wrap.remove();
24920 onFirstFocus : function(){
24922 this.assignDocWin();
24925 this.activated = true;
24928 if(Roo.isGecko){ // prevent silly gecko errors
24930 var s = this.win.getSelection();
24931 if(!s.focusNode || s.focusNode.nodeType != 3){
24932 var r = s.getRangeAt(0);
24933 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24938 this.execCmd('useCSS', true);
24939 this.execCmd('styleWithCSS', false);
24942 this.owner.fireEvent('activate', this);
24946 adjustFont: function(btn){
24947 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24948 //if(Roo.isSafari){ // safari
24951 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24952 if(Roo.isSafari){ // safari
24953 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24954 v = (v < 10) ? 10 : v;
24955 v = (v > 48) ? 48 : v;
24956 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24961 v = Math.max(1, v+adjust);
24963 this.execCmd('FontSize', v );
24966 onEditorEvent : function(e)
24968 this.owner.fireEvent('editorevent', this, e);
24969 // this.updateToolbar();
24970 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24973 insertTag : function(tg)
24975 // could be a bit smarter... -> wrap the current selected tRoo..
24976 if (tg.toLowerCase() == 'span' ||
24977 tg.toLowerCase() == 'code' ||
24978 tg.toLowerCase() == 'sup' ||
24979 tg.toLowerCase() == 'sub'
24982 range = this.createRange(this.getSelection());
24983 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24984 wrappingNode.appendChild(range.extractContents());
24985 range.insertNode(wrappingNode);
24992 this.execCmd("formatblock", tg);
24996 insertText : function(txt)
25000 var range = this.createRange();
25001 range.deleteContents();
25002 //alert(Sender.getAttribute('label'));
25004 range.insertNode(this.doc.createTextNode(txt));
25010 * Executes a Midas editor command on the editor document and performs necessary focus and
25011 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25012 * @param {String} cmd The Midas command
25013 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25015 relayCmd : function(cmd, value){
25017 this.execCmd(cmd, value);
25018 this.owner.fireEvent('editorevent', this);
25019 //this.updateToolbar();
25020 this.owner.deferFocus();
25024 * Executes a Midas editor command directly on the editor document.
25025 * For visual commands, you should use {@link #relayCmd} instead.
25026 * <b>This should only be called after the editor is initialized.</b>
25027 * @param {String} cmd The Midas command
25028 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25030 execCmd : function(cmd, value){
25031 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25038 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25040 * @param {String} text | dom node..
25042 insertAtCursor : function(text)
25045 if(!this.activated){
25051 var r = this.doc.selection.createRange();
25062 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25066 // from jquery ui (MIT licenced)
25068 var win = this.win;
25070 if (win.getSelection && win.getSelection().getRangeAt) {
25071 range = win.getSelection().getRangeAt(0);
25072 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25073 range.insertNode(node);
25074 } else if (win.document.selection && win.document.selection.createRange) {
25075 // no firefox support
25076 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25077 win.document.selection.createRange().pasteHTML(txt);
25079 // no firefox support
25080 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25081 this.execCmd('InsertHTML', txt);
25090 mozKeyPress : function(e){
25092 var c = e.getCharCode(), cmd;
25095 c = String.fromCharCode(c).toLowerCase();
25109 this.cleanUpPaste.defer(100, this);
25117 e.preventDefault();
25125 fixKeys : function(){ // load time branching for fastest keydown performance
25127 return function(e){
25128 var k = e.getKey(), r;
25131 r = this.doc.selection.createRange();
25134 r.pasteHTML('    ');
25141 r = this.doc.selection.createRange();
25143 var target = r.parentElement();
25144 if(!target || target.tagName.toLowerCase() != 'li'){
25146 r.pasteHTML('<br />');
25152 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25153 this.cleanUpPaste.defer(100, this);
25159 }else if(Roo.isOpera){
25160 return function(e){
25161 var k = e.getKey();
25165 this.execCmd('InsertHTML','    ');
25168 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25169 this.cleanUpPaste.defer(100, this);
25174 }else if(Roo.isSafari){
25175 return function(e){
25176 var k = e.getKey();
25180 this.execCmd('InsertText','\t');
25184 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25185 this.cleanUpPaste.defer(100, this);
25193 getAllAncestors: function()
25195 var p = this.getSelectedNode();
25198 a.push(p); // push blank onto stack..
25199 p = this.getParentElement();
25203 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25207 a.push(this.doc.body);
25211 lastSelNode : false,
25214 getSelection : function()
25216 this.assignDocWin();
25217 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25220 getSelectedNode: function()
25222 // this may only work on Gecko!!!
25224 // should we cache this!!!!
25229 var range = this.createRange(this.getSelection()).cloneRange();
25232 var parent = range.parentElement();
25234 var testRange = range.duplicate();
25235 testRange.moveToElementText(parent);
25236 if (testRange.inRange(range)) {
25239 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25242 parent = parent.parentElement;
25247 // is ancestor a text element.
25248 var ac = range.commonAncestorContainer;
25249 if (ac.nodeType == 3) {
25250 ac = ac.parentNode;
25253 var ar = ac.childNodes;
25256 var other_nodes = [];
25257 var has_other_nodes = false;
25258 for (var i=0;i<ar.length;i++) {
25259 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25262 // fullly contained node.
25264 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25269 // probably selected..
25270 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25271 other_nodes.push(ar[i]);
25275 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25280 has_other_nodes = true;
25282 if (!nodes.length && other_nodes.length) {
25283 nodes= other_nodes;
25285 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25291 createRange: function(sel)
25293 // this has strange effects when using with
25294 // top toolbar - not sure if it's a great idea.
25295 //this.editor.contentWindow.focus();
25296 if (typeof sel != "undefined") {
25298 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25300 return this.doc.createRange();
25303 return this.doc.createRange();
25306 getParentElement: function()
25309 this.assignDocWin();
25310 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25312 var range = this.createRange(sel);
25315 var p = range.commonAncestorContainer;
25316 while (p.nodeType == 3) { // text node
25327 * Range intersection.. the hard stuff...
25331 * [ -- selected range --- ]
25335 * if end is before start or hits it. fail.
25336 * if start is after end or hits it fail.
25338 * if either hits (but other is outside. - then it's not
25344 // @see http://www.thismuchiknow.co.uk/?p=64.
25345 rangeIntersectsNode : function(range, node)
25347 var nodeRange = node.ownerDocument.createRange();
25349 nodeRange.selectNode(node);
25351 nodeRange.selectNodeContents(node);
25354 var rangeStartRange = range.cloneRange();
25355 rangeStartRange.collapse(true);
25357 var rangeEndRange = range.cloneRange();
25358 rangeEndRange.collapse(false);
25360 var nodeStartRange = nodeRange.cloneRange();
25361 nodeStartRange.collapse(true);
25363 var nodeEndRange = nodeRange.cloneRange();
25364 nodeEndRange.collapse(false);
25366 return rangeStartRange.compareBoundaryPoints(
25367 Range.START_TO_START, nodeEndRange) == -1 &&
25368 rangeEndRange.compareBoundaryPoints(
25369 Range.START_TO_START, nodeStartRange) == 1;
25373 rangeCompareNode : function(range, node)
25375 var nodeRange = node.ownerDocument.createRange();
25377 nodeRange.selectNode(node);
25379 nodeRange.selectNodeContents(node);
25383 range.collapse(true);
25385 nodeRange.collapse(true);
25387 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25388 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25390 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25392 var nodeIsBefore = ss == 1;
25393 var nodeIsAfter = ee == -1;
25395 if (nodeIsBefore && nodeIsAfter) {
25398 if (!nodeIsBefore && nodeIsAfter) {
25399 return 1; //right trailed.
25402 if (nodeIsBefore && !nodeIsAfter) {
25403 return 2; // left trailed.
25409 // private? - in a new class?
25410 cleanUpPaste : function()
25412 // cleans up the whole document..
25413 Roo.log('cleanuppaste');
25415 this.cleanUpChildren(this.doc.body);
25416 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25417 if (clean != this.doc.body.innerHTML) {
25418 this.doc.body.innerHTML = clean;
25423 cleanWordChars : function(input) {// change the chars to hex code
25424 var he = Roo.HtmlEditorCore;
25426 var output = input;
25427 Roo.each(he.swapCodes, function(sw) {
25428 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25430 output = output.replace(swapper, sw[1]);
25437 cleanUpChildren : function (n)
25439 if (!n.childNodes.length) {
25442 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25443 this.cleanUpChild(n.childNodes[i]);
25450 cleanUpChild : function (node)
25453 //console.log(node);
25454 if (node.nodeName == "#text") {
25455 // clean up silly Windows -- stuff?
25458 if (node.nodeName == "#comment") {
25459 node.parentNode.removeChild(node);
25460 // clean up silly Windows -- stuff?
25463 var lcname = node.tagName.toLowerCase();
25464 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25465 // whitelist of tags..
25467 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25469 node.parentNode.removeChild(node);
25474 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25476 // spans with no attributes - just remove them..
25477 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25478 remove_keep_children = true;
25481 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25482 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25484 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25485 // remove_keep_children = true;
25488 if (remove_keep_children) {
25489 this.cleanUpChildren(node);
25490 // inserts everything just before this node...
25491 while (node.childNodes.length) {
25492 var cn = node.childNodes[0];
25493 node.removeChild(cn);
25494 node.parentNode.insertBefore(cn, node);
25496 node.parentNode.removeChild(node);
25500 if (!node.attributes || !node.attributes.length) {
25505 this.cleanUpChildren(node);
25509 function cleanAttr(n,v)
25512 if (v.match(/^\./) || v.match(/^\//)) {
25515 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25518 if (v.match(/^#/)) {
25521 if (v.match(/^\{/)) { // allow template editing.
25524 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25525 node.removeAttribute(n);
25529 var cwhite = this.cwhite;
25530 var cblack = this.cblack;
25532 function cleanStyle(n,v)
25534 if (v.match(/expression/)) { //XSS?? should we even bother..
25535 node.removeAttribute(n);
25539 var parts = v.split(/;/);
25542 Roo.each(parts, function(p) {
25543 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25547 var l = p.split(':').shift().replace(/\s+/g,'');
25548 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25550 if ( cwhite.length && cblack.indexOf(l) > -1) {
25551 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25552 //node.removeAttribute(n);
25556 // only allow 'c whitelisted system attributes'
25557 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25558 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25559 //node.removeAttribute(n);
25569 if (clean.length) {
25570 node.setAttribute(n, clean.join(';'));
25572 node.removeAttribute(n);
25578 for (var i = node.attributes.length-1; i > -1 ; i--) {
25579 var a = node.attributes[i];
25582 if (a.name.toLowerCase().substr(0,2)=='on') {
25583 node.removeAttribute(a.name);
25586 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25587 node.removeAttribute(a.name);
25590 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25591 cleanAttr(a.name,a.value); // fixme..
25594 if (a.name == 'style') {
25595 cleanStyle(a.name,a.value);
25598 /// clean up MS crap..
25599 // tecnically this should be a list of valid class'es..
25602 if (a.name == 'class') {
25603 if (a.value.match(/^Mso/)) {
25604 node.removeAttribute('class');
25607 if (a.value.match(/^body$/)) {
25608 node.removeAttribute('class');
25619 this.cleanUpChildren(node);
25625 * Clean up MS wordisms...
25627 cleanWord : function(node)
25630 this.cleanWord(this.doc.body);
25635 node.nodeName == 'SPAN' &&
25636 !node.hasAttributes() &&
25637 node.childNodes.length == 1 &&
25638 node.firstChild.nodeName == "#text"
25640 var textNode = node.firstChild;
25641 node.removeChild(textNode);
25642 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25643 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25645 node.parentNode.insertBefore(textNode, node);
25646 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25647 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25649 node.parentNode.removeChild(node);
25652 if (node.nodeName == "#text") {
25653 // clean up silly Windows -- stuff?
25656 if (node.nodeName == "#comment") {
25657 node.parentNode.removeChild(node);
25658 // clean up silly Windows -- stuff?
25662 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25663 node.parentNode.removeChild(node);
25666 //Roo.log(node.tagName);
25667 // remove - but keep children..
25668 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25669 //Roo.log('-- removed');
25670 while (node.childNodes.length) {
25671 var cn = node.childNodes[0];
25672 node.removeChild(cn);
25673 node.parentNode.insertBefore(cn, node);
25674 // move node to parent - and clean it..
25675 this.cleanWord(cn);
25677 node.parentNode.removeChild(node);
25678 /// no need to iterate chidlren = it's got none..
25679 //this.iterateChildren(node, this.cleanWord);
25683 if (node.className.length) {
25685 var cn = node.className.split(/\W+/);
25687 Roo.each(cn, function(cls) {
25688 if (cls.match(/Mso[a-zA-Z]+/)) {
25693 node.className = cna.length ? cna.join(' ') : '';
25695 node.removeAttribute("class");
25699 if (node.hasAttribute("lang")) {
25700 node.removeAttribute("lang");
25703 if (node.hasAttribute("style")) {
25705 var styles = node.getAttribute("style").split(";");
25707 Roo.each(styles, function(s) {
25708 if (!s.match(/:/)) {
25711 var kv = s.split(":");
25712 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25715 // what ever is left... we allow.
25718 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25719 if (!nstyle.length) {
25720 node.removeAttribute('style');
25723 this.iterateChildren(node, this.cleanWord);
25729 * iterateChildren of a Node, calling fn each time, using this as the scole..
25730 * @param {DomNode} node node to iterate children of.
25731 * @param {Function} fn method of this class to call on each item.
25733 iterateChildren : function(node, fn)
25735 if (!node.childNodes.length) {
25738 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25739 fn.call(this, node.childNodes[i])
25745 * cleanTableWidths.
25747 * Quite often pasting from word etc.. results in tables with column and widths.
25748 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25751 cleanTableWidths : function(node)
25756 this.cleanTableWidths(this.doc.body);
25761 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25764 Roo.log(node.tagName);
25765 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25766 this.iterateChildren(node, this.cleanTableWidths);
25769 if (node.hasAttribute('width')) {
25770 node.removeAttribute('width');
25774 if (node.hasAttribute("style")) {
25777 var styles = node.getAttribute("style").split(";");
25779 Roo.each(styles, function(s) {
25780 if (!s.match(/:/)) {
25783 var kv = s.split(":");
25784 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25787 // what ever is left... we allow.
25790 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25791 if (!nstyle.length) {
25792 node.removeAttribute('style');
25796 this.iterateChildren(node, this.cleanTableWidths);
25804 domToHTML : function(currentElement, depth, nopadtext) {
25806 depth = depth || 0;
25807 nopadtext = nopadtext || false;
25809 if (!currentElement) {
25810 return this.domToHTML(this.doc.body);
25813 //Roo.log(currentElement);
25815 var allText = false;
25816 var nodeName = currentElement.nodeName;
25817 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25819 if (nodeName == '#text') {
25821 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25826 if (nodeName != 'BODY') {
25829 // Prints the node tagName, such as <A>, <IMG>, etc
25832 for(i = 0; i < currentElement.attributes.length;i++) {
25834 var aname = currentElement.attributes.item(i).name;
25835 if (!currentElement.attributes.item(i).value.length) {
25838 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25841 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25850 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25853 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25858 // Traverse the tree
25860 var currentElementChild = currentElement.childNodes.item(i);
25861 var allText = true;
25862 var innerHTML = '';
25864 while (currentElementChild) {
25865 // Formatting code (indent the tree so it looks nice on the screen)
25866 var nopad = nopadtext;
25867 if (lastnode == 'SPAN') {
25871 if (currentElementChild.nodeName == '#text') {
25872 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25873 toadd = nopadtext ? toadd : toadd.trim();
25874 if (!nopad && toadd.length > 80) {
25875 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25877 innerHTML += toadd;
25880 currentElementChild = currentElement.childNodes.item(i);
25886 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25888 // Recursively traverse the tree structure of the child node
25889 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25890 lastnode = currentElementChild.nodeName;
25892 currentElementChild=currentElement.childNodes.item(i);
25898 // The remaining code is mostly for formatting the tree
25899 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25904 ret+= "</"+tagName+">";
25910 applyBlacklists : function()
25912 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25913 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25917 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25918 if (b.indexOf(tag) > -1) {
25921 this.white.push(tag);
25925 Roo.each(w, function(tag) {
25926 if (b.indexOf(tag) > -1) {
25929 if (this.white.indexOf(tag) > -1) {
25932 this.white.push(tag);
25937 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25938 if (w.indexOf(tag) > -1) {
25941 this.black.push(tag);
25945 Roo.each(b, function(tag) {
25946 if (w.indexOf(tag) > -1) {
25949 if (this.black.indexOf(tag) > -1) {
25952 this.black.push(tag);
25957 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25958 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25962 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25963 if (b.indexOf(tag) > -1) {
25966 this.cwhite.push(tag);
25970 Roo.each(w, function(tag) {
25971 if (b.indexOf(tag) > -1) {
25974 if (this.cwhite.indexOf(tag) > -1) {
25977 this.cwhite.push(tag);
25982 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25983 if (w.indexOf(tag) > -1) {
25986 this.cblack.push(tag);
25990 Roo.each(b, function(tag) {
25991 if (w.indexOf(tag) > -1) {
25994 if (this.cblack.indexOf(tag) > -1) {
25997 this.cblack.push(tag);
26002 setStylesheets : function(stylesheets)
26004 if(typeof(stylesheets) == 'string'){
26005 Roo.get(this.iframe.contentDocument.head).createChild({
26007 rel : 'stylesheet',
26016 Roo.each(stylesheets, function(s) {
26021 Roo.get(_this.iframe.contentDocument.head).createChild({
26023 rel : 'stylesheet',
26032 removeStylesheets : function()
26036 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26041 setStyle : function(style)
26043 Roo.get(this.iframe.contentDocument.head).createChild({
26052 // hide stuff that is not compatible
26066 * @event specialkey
26070 * @cfg {String} fieldClass @hide
26073 * @cfg {String} focusClass @hide
26076 * @cfg {String} autoCreate @hide
26079 * @cfg {String} inputType @hide
26082 * @cfg {String} invalidClass @hide
26085 * @cfg {String} invalidText @hide
26088 * @cfg {String} msgFx @hide
26091 * @cfg {String} validateOnBlur @hide
26095 Roo.HtmlEditorCore.white = [
26096 'area', 'br', 'img', 'input', 'hr', 'wbr',
26098 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26099 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26100 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26101 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26102 'table', 'ul', 'xmp',
26104 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26107 'dir', 'menu', 'ol', 'ul', 'dl',
26113 Roo.HtmlEditorCore.black = [
26114 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26116 'base', 'basefont', 'bgsound', 'blink', 'body',
26117 'frame', 'frameset', 'head', 'html', 'ilayer',
26118 'iframe', 'layer', 'link', 'meta', 'object',
26119 'script', 'style' ,'title', 'xml' // clean later..
26121 Roo.HtmlEditorCore.clean = [
26122 'script', 'style', 'title', 'xml'
26124 Roo.HtmlEditorCore.remove = [
26129 Roo.HtmlEditorCore.ablack = [
26133 Roo.HtmlEditorCore.aclean = [
26134 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26138 Roo.HtmlEditorCore.pwhite= [
26139 'http', 'https', 'mailto'
26142 // white listed style attributes.
26143 Roo.HtmlEditorCore.cwhite= [
26144 // 'text-align', /// default is to allow most things..
26150 // black listed style attributes.
26151 Roo.HtmlEditorCore.cblack= [
26152 // 'font-size' -- this can be set by the project
26156 Roo.HtmlEditorCore.swapCodes =[
26157 [ 8211, "–" ],
26158 [ 8212, "—" ],
26175 * @class Roo.bootstrap.HtmlEditor
26176 * @extends Roo.bootstrap.TextArea
26177 * Bootstrap HtmlEditor class
26180 * Create a new HtmlEditor
26181 * @param {Object} config The config object
26184 Roo.bootstrap.HtmlEditor = function(config){
26185 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26186 if (!this.toolbars) {
26187 this.toolbars = [];
26190 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26193 * @event initialize
26194 * Fires when the editor is fully initialized (including the iframe)
26195 * @param {HtmlEditor} this
26200 * Fires when the editor is first receives the focus. Any insertion must wait
26201 * until after this event.
26202 * @param {HtmlEditor} this
26206 * @event beforesync
26207 * Fires before the textarea is updated with content from the editor iframe. Return false
26208 * to cancel the sync.
26209 * @param {HtmlEditor} this
26210 * @param {String} html
26214 * @event beforepush
26215 * Fires before the iframe editor is updated with content from the textarea. Return false
26216 * to cancel the push.
26217 * @param {HtmlEditor} this
26218 * @param {String} html
26223 * Fires when the textarea is updated with content from the editor iframe.
26224 * @param {HtmlEditor} this
26225 * @param {String} html
26230 * Fires when the iframe editor is updated with content from the textarea.
26231 * @param {HtmlEditor} this
26232 * @param {String} html
26236 * @event editmodechange
26237 * Fires when the editor switches edit modes
26238 * @param {HtmlEditor} this
26239 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26241 editmodechange: true,
26243 * @event editorevent
26244 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26245 * @param {HtmlEditor} this
26249 * @event firstfocus
26250 * Fires when on first focus - needed by toolbars..
26251 * @param {HtmlEditor} this
26256 * Auto save the htmlEditor value as a file into Events
26257 * @param {HtmlEditor} this
26261 * @event savedpreview
26262 * preview the saved version of htmlEditor
26263 * @param {HtmlEditor} this
26270 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26274 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26279 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26284 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26289 * @cfg {Number} height (in pixels)
26293 * @cfg {Number} width (in pixels)
26298 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26301 stylesheets: false,
26306 // private properties
26307 validationEvent : false,
26309 initialized : false,
26312 onFocus : Roo.emptyFn,
26314 hideMode:'offsets',
26316 tbContainer : false,
26320 toolbarContainer :function() {
26321 return this.wrap.select('.x-html-editor-tb',true).first();
26325 * Protected method that will not generally be called directly. It
26326 * is called when the editor creates its toolbar. Override this method if you need to
26327 * add custom toolbar buttons.
26328 * @param {HtmlEditor} editor
26330 createToolbar : function(){
26331 Roo.log('renewing');
26332 Roo.log("create toolbars");
26334 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26335 this.toolbars[0].render(this.toolbarContainer());
26339 // if (!editor.toolbars || !editor.toolbars.length) {
26340 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26343 // for (var i =0 ; i < editor.toolbars.length;i++) {
26344 // editor.toolbars[i] = Roo.factory(
26345 // typeof(editor.toolbars[i]) == 'string' ?
26346 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26347 // Roo.bootstrap.HtmlEditor);
26348 // editor.toolbars[i].init(editor);
26354 onRender : function(ct, position)
26356 // Roo.log("Call onRender: " + this.xtype);
26358 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26360 this.wrap = this.inputEl().wrap({
26361 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26364 this.editorcore.onRender(ct, position);
26366 if (this.resizable) {
26367 this.resizeEl = new Roo.Resizable(this.wrap, {
26371 minHeight : this.height,
26372 height: this.height,
26373 handles : this.resizable,
26376 resize : function(r, w, h) {
26377 _t.onResize(w,h); // -something
26383 this.createToolbar(this);
26386 if(!this.width && this.resizable){
26387 this.setSize(this.wrap.getSize());
26389 if (this.resizeEl) {
26390 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26391 // should trigger onReize..
26397 onResize : function(w, h)
26399 Roo.log('resize: ' +w + ',' + h );
26400 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26404 if(this.inputEl() ){
26405 if(typeof w == 'number'){
26406 var aw = w - this.wrap.getFrameWidth('lr');
26407 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26410 if(typeof h == 'number'){
26411 var tbh = -11; // fixme it needs to tool bar size!
26412 for (var i =0; i < this.toolbars.length;i++) {
26413 // fixme - ask toolbars for heights?
26414 tbh += this.toolbars[i].el.getHeight();
26415 //if (this.toolbars[i].footer) {
26416 // tbh += this.toolbars[i].footer.el.getHeight();
26424 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26425 ah -= 5; // knock a few pixes off for look..
26426 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26430 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26431 this.editorcore.onResize(ew,eh);
26436 * Toggles the editor between standard and source edit mode.
26437 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26439 toggleSourceEdit : function(sourceEditMode)
26441 this.editorcore.toggleSourceEdit(sourceEditMode);
26443 if(this.editorcore.sourceEditMode){
26444 Roo.log('editor - showing textarea');
26447 // Roo.log(this.syncValue());
26449 this.inputEl().removeClass(['hide', 'x-hidden']);
26450 this.inputEl().dom.removeAttribute('tabIndex');
26451 this.inputEl().focus();
26453 Roo.log('editor - hiding textarea');
26455 // Roo.log(this.pushValue());
26458 this.inputEl().addClass(['hide', 'x-hidden']);
26459 this.inputEl().dom.setAttribute('tabIndex', -1);
26460 //this.deferFocus();
26463 if(this.resizable){
26464 this.setSize(this.wrap.getSize());
26467 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26470 // private (for BoxComponent)
26471 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26473 // private (for BoxComponent)
26474 getResizeEl : function(){
26478 // private (for BoxComponent)
26479 getPositionEl : function(){
26484 initEvents : function(){
26485 this.originalValue = this.getValue();
26489 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26492 // markInvalid : Roo.emptyFn,
26494 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26497 // clearInvalid : Roo.emptyFn,
26499 setValue : function(v){
26500 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26501 this.editorcore.pushValue();
26506 deferFocus : function(){
26507 this.focus.defer(10, this);
26511 focus : function(){
26512 this.editorcore.focus();
26518 onDestroy : function(){
26524 for (var i =0; i < this.toolbars.length;i++) {
26525 // fixme - ask toolbars for heights?
26526 this.toolbars[i].onDestroy();
26529 this.wrap.dom.innerHTML = '';
26530 this.wrap.remove();
26535 onFirstFocus : function(){
26536 //Roo.log("onFirstFocus");
26537 this.editorcore.onFirstFocus();
26538 for (var i =0; i < this.toolbars.length;i++) {
26539 this.toolbars[i].onFirstFocus();
26545 syncValue : function()
26547 this.editorcore.syncValue();
26550 pushValue : function()
26552 this.editorcore.pushValue();
26556 // hide stuff that is not compatible
26570 * @event specialkey
26574 * @cfg {String} fieldClass @hide
26577 * @cfg {String} focusClass @hide
26580 * @cfg {String} autoCreate @hide
26583 * @cfg {String} inputType @hide
26587 * @cfg {String} invalidText @hide
26590 * @cfg {String} msgFx @hide
26593 * @cfg {String} validateOnBlur @hide
26602 Roo.namespace('Roo.bootstrap.htmleditor');
26604 * @class Roo.bootstrap.HtmlEditorToolbar1
26610 new Roo.bootstrap.HtmlEditor({
26613 new Roo.bootstrap.HtmlEditorToolbar1({
26614 disable : { fonts: 1 , format: 1, ..., ... , ...],
26620 * @cfg {Object} disable List of elements to disable..
26621 * @cfg {Array} btns List of additional buttons.
26625 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26628 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26631 Roo.apply(this, config);
26633 // default disabled, based on 'good practice'..
26634 this.disable = this.disable || {};
26635 Roo.applyIf(this.disable, {
26638 specialElements : true
26640 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26642 this.editor = config.editor;
26643 this.editorcore = config.editor.editorcore;
26645 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26647 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26648 // dont call parent... till later.
26650 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26655 editorcore : false,
26660 "h1","h2","h3","h4","h5","h6",
26662 "abbr", "acronym", "address", "cite", "samp", "var",
26666 onRender : function(ct, position)
26668 // Roo.log("Call onRender: " + this.xtype);
26670 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26672 this.el.dom.style.marginBottom = '0';
26674 var editorcore = this.editorcore;
26675 var editor= this.editor;
26678 var btn = function(id,cmd , toggle, handler, html){
26680 var event = toggle ? 'toggle' : 'click';
26685 xns: Roo.bootstrap,
26689 enableToggle:toggle !== false,
26691 pressed : toggle ? false : null,
26694 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26695 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26701 // var cb_box = function...
26706 xns: Roo.bootstrap,
26711 xns: Roo.bootstrap,
26715 Roo.each(this.formats, function(f) {
26716 style.menu.items.push({
26718 xns: Roo.bootstrap,
26719 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26724 editorcore.insertTag(this.tagname);
26731 children.push(style);
26733 btn('bold',false,true);
26734 btn('italic',false,true);
26735 btn('align-left', 'justifyleft',true);
26736 btn('align-center', 'justifycenter',true);
26737 btn('align-right' , 'justifyright',true);
26738 btn('link', false, false, function(btn) {
26739 //Roo.log("create link?");
26740 var url = prompt(this.createLinkText, this.defaultLinkValue);
26741 if(url && url != 'http:/'+'/'){
26742 this.editorcore.relayCmd('createlink', url);
26745 btn('list','insertunorderedlist',true);
26746 btn('pencil', false,true, function(btn){
26748 this.toggleSourceEdit(btn.pressed);
26751 if (this.editor.btns.length > 0) {
26752 for (var i = 0; i<this.editor.btns.length; i++) {
26753 children.push(this.editor.btns[i]);
26761 xns: Roo.bootstrap,
26766 xns: Roo.bootstrap,
26771 cog.menu.items.push({
26773 xns: Roo.bootstrap,
26774 html : Clean styles,
26779 editorcore.insertTag(this.tagname);
26788 this.xtype = 'NavSimplebar';
26790 for(var i=0;i< children.length;i++) {
26792 this.buttons.add(this.addxtypeChild(children[i]));
26796 editor.on('editorevent', this.updateToolbar, this);
26798 onBtnClick : function(id)
26800 this.editorcore.relayCmd(id);
26801 this.editorcore.focus();
26805 * Protected method that will not generally be called directly. It triggers
26806 * a toolbar update by reading the markup state of the current selection in the editor.
26808 updateToolbar: function(){
26810 if(!this.editorcore.activated){
26811 this.editor.onFirstFocus(); // is this neeed?
26815 var btns = this.buttons;
26816 var doc = this.editorcore.doc;
26817 btns.get('bold').setActive(doc.queryCommandState('bold'));
26818 btns.get('italic').setActive(doc.queryCommandState('italic'));
26819 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26821 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26822 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26823 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26825 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26826 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26829 var ans = this.editorcore.getAllAncestors();
26830 if (this.formatCombo) {
26833 var store = this.formatCombo.store;
26834 this.formatCombo.setValue("");
26835 for (var i =0; i < ans.length;i++) {
26836 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26838 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26846 // hides menus... - so this cant be on a menu...
26847 Roo.bootstrap.MenuMgr.hideAll();
26849 Roo.bootstrap.MenuMgr.hideAll();
26850 //this.editorsyncValue();
26852 onFirstFocus: function() {
26853 this.buttons.each(function(item){
26857 toggleSourceEdit : function(sourceEditMode){
26860 if(sourceEditMode){
26861 Roo.log("disabling buttons");
26862 this.buttons.each( function(item){
26863 if(item.cmd != 'pencil'){
26869 Roo.log("enabling buttons");
26870 if(this.editorcore.initialized){
26871 this.buttons.each( function(item){
26877 Roo.log("calling toggole on editor");
26878 // tell the editor that it's been pressed..
26879 this.editor.toggleSourceEdit(sourceEditMode);
26893 * @class Roo.bootstrap.Markdown
26894 * @extends Roo.bootstrap.TextArea
26895 * Bootstrap Showdown editable area
26896 * @cfg {string} content
26899 * Create a new Showdown
26902 Roo.bootstrap.Markdown = function(config){
26903 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26907 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26911 initEvents : function()
26914 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26915 this.markdownEl = this.el.createChild({
26916 cls : 'roo-markdown-area'
26918 this.inputEl().addClass('d-none');
26919 if (this.getValue() == '') {
26920 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26923 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26925 this.markdownEl.on('click', this.toggleTextEdit, this);
26926 this.on('blur', this.toggleTextEdit, this);
26927 this.on('specialkey', this.resizeTextArea, this);
26930 toggleTextEdit : function()
26932 var sh = this.markdownEl.getHeight();
26933 this.inputEl().addClass('d-none');
26934 this.markdownEl.addClass('d-none');
26935 if (!this.editing) {
26937 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26938 this.inputEl().removeClass('d-none');
26939 this.inputEl().focus();
26940 this.editing = true;
26943 // show showdown...
26944 this.updateMarkdown();
26945 this.markdownEl.removeClass('d-none');
26946 this.editing = false;
26949 updateMarkdown : function()
26951 if (this.getValue() == '') {
26952 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26956 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26959 resizeTextArea: function () {
26962 Roo.log([sh, this.getValue().split("\n").length * 30]);
26963 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26965 setValue : function(val)
26967 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26968 if (!this.editing) {
26969 this.updateMarkdown();
26975 if (!this.editing) {
26976 this.toggleTextEdit();
26984 * @class Roo.bootstrap.Table.AbstractSelectionModel
26985 * @extends Roo.util.Observable
26986 * Abstract base class for grid SelectionModels. It provides the interface that should be
26987 * implemented by descendant classes. This class should not be directly instantiated.
26990 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26991 this.locked = false;
26992 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26996 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26997 /** @ignore Called by the grid automatically. Do not call directly. */
26998 init : function(grid){
27004 * Locks the selections.
27007 this.locked = true;
27011 * Unlocks the selections.
27013 unlock : function(){
27014 this.locked = false;
27018 * Returns true if the selections are locked.
27019 * @return {Boolean}
27021 isLocked : function(){
27022 return this.locked;
27026 initEvents : function ()
27032 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27033 * @class Roo.bootstrap.Table.RowSelectionModel
27034 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27035 * It supports multiple selections and keyboard selection/navigation.
27037 * @param {Object} config
27040 Roo.bootstrap.Table.RowSelectionModel = function(config){
27041 Roo.apply(this, config);
27042 this.selections = new Roo.util.MixedCollection(false, function(o){
27047 this.lastActive = false;
27051 * @event selectionchange
27052 * Fires when the selection changes
27053 * @param {SelectionModel} this
27055 "selectionchange" : true,
27057 * @event afterselectionchange
27058 * Fires after the selection changes (eg. by key press or clicking)
27059 * @param {SelectionModel} this
27061 "afterselectionchange" : true,
27063 * @event beforerowselect
27064 * Fires when a row is selected being selected, return false to cancel.
27065 * @param {SelectionModel} this
27066 * @param {Number} rowIndex The selected index
27067 * @param {Boolean} keepExisting False if other selections will be cleared
27069 "beforerowselect" : true,
27072 * Fires when a row is selected.
27073 * @param {SelectionModel} this
27074 * @param {Number} rowIndex The selected index
27075 * @param {Roo.data.Record} r The record
27077 "rowselect" : true,
27079 * @event rowdeselect
27080 * Fires when a row is deselected.
27081 * @param {SelectionModel} this
27082 * @param {Number} rowIndex The selected index
27084 "rowdeselect" : true
27086 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27087 this.locked = false;
27090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27092 * @cfg {Boolean} singleSelect
27093 * True to allow selection of only one row at a time (defaults to false)
27095 singleSelect : false,
27098 initEvents : function()
27101 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27102 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27103 //}else{ // allow click to work like normal
27104 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27106 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27107 this.grid.on("rowclick", this.handleMouseDown, this);
27109 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27110 "up" : function(e){
27112 this.selectPrevious(e.shiftKey);
27113 }else if(this.last !== false && this.lastActive !== false){
27114 var last = this.last;
27115 this.selectRange(this.last, this.lastActive-1);
27116 this.grid.getView().focusRow(this.lastActive);
27117 if(last !== false){
27121 this.selectFirstRow();
27123 this.fireEvent("afterselectionchange", this);
27125 "down" : function(e){
27127 this.selectNext(e.shiftKey);
27128 }else if(this.last !== false && this.lastActive !== false){
27129 var last = this.last;
27130 this.selectRange(this.last, this.lastActive+1);
27131 this.grid.getView().focusRow(this.lastActive);
27132 if(last !== false){
27136 this.selectFirstRow();
27138 this.fireEvent("afterselectionchange", this);
27142 this.grid.store.on('load', function(){
27143 this.selections.clear();
27146 var view = this.grid.view;
27147 view.on("refresh", this.onRefresh, this);
27148 view.on("rowupdated", this.onRowUpdated, this);
27149 view.on("rowremoved", this.onRemove, this);
27154 onRefresh : function()
27156 var ds = this.grid.store, i, v = this.grid.view;
27157 var s = this.selections;
27158 s.each(function(r){
27159 if((i = ds.indexOfId(r.id)) != -1){
27168 onRemove : function(v, index, r){
27169 this.selections.remove(r);
27173 onRowUpdated : function(v, index, r){
27174 if(this.isSelected(r)){
27175 v.onRowSelect(index);
27181 * @param {Array} records The records to select
27182 * @param {Boolean} keepExisting (optional) True to keep existing selections
27184 selectRecords : function(records, keepExisting)
27187 this.clearSelections();
27189 var ds = this.grid.store;
27190 for(var i = 0, len = records.length; i < len; i++){
27191 this.selectRow(ds.indexOf(records[i]), true);
27196 * Gets the number of selected rows.
27199 getCount : function(){
27200 return this.selections.length;
27204 * Selects the first row in the grid.
27206 selectFirstRow : function(){
27211 * Select the last row.
27212 * @param {Boolean} keepExisting (optional) True to keep existing selections
27214 selectLastRow : function(keepExisting){
27215 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27216 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27220 * Selects the row immediately following the last selected row.
27221 * @param {Boolean} keepExisting (optional) True to keep existing selections
27223 selectNext : function(keepExisting)
27225 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27226 this.selectRow(this.last+1, keepExisting);
27227 this.grid.getView().focusRow(this.last);
27232 * Selects the row that precedes the last selected row.
27233 * @param {Boolean} keepExisting (optional) True to keep existing selections
27235 selectPrevious : function(keepExisting){
27237 this.selectRow(this.last-1, keepExisting);
27238 this.grid.getView().focusRow(this.last);
27243 * Returns the selected records
27244 * @return {Array} Array of selected records
27246 getSelections : function(){
27247 return [].concat(this.selections.items);
27251 * Returns the first selected record.
27254 getSelected : function(){
27255 return this.selections.itemAt(0);
27260 * Clears all selections.
27262 clearSelections : function(fast)
27268 var ds = this.grid.store;
27269 var s = this.selections;
27270 s.each(function(r){
27271 this.deselectRow(ds.indexOfId(r.id));
27275 this.selections.clear();
27282 * Selects all rows.
27284 selectAll : function(){
27288 this.selections.clear();
27289 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27290 this.selectRow(i, true);
27295 * Returns True if there is a selection.
27296 * @return {Boolean}
27298 hasSelection : function(){
27299 return this.selections.length > 0;
27303 * Returns True if the specified row is selected.
27304 * @param {Number/Record} record The record or index of the record to check
27305 * @return {Boolean}
27307 isSelected : function(index){
27308 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27309 return (r && this.selections.key(r.id) ? true : false);
27313 * Returns True if the specified record id is selected.
27314 * @param {String} id The id of record to check
27315 * @return {Boolean}
27317 isIdSelected : function(id){
27318 return (this.selections.key(id) ? true : false);
27323 handleMouseDBClick : function(e, t){
27327 handleMouseDown : function(e, t)
27329 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27330 if(this.isLocked() || rowIndex < 0 ){
27333 if(e.shiftKey && this.last !== false){
27334 var last = this.last;
27335 this.selectRange(last, rowIndex, e.ctrlKey);
27336 this.last = last; // reset the last
27340 var isSelected = this.isSelected(rowIndex);
27341 //Roo.log("select row:" + rowIndex);
27343 this.deselectRow(rowIndex);
27345 this.selectRow(rowIndex, true);
27349 if(e.button !== 0 && isSelected){
27350 alert('rowIndex 2: ' + rowIndex);
27351 view.focusRow(rowIndex);
27352 }else if(e.ctrlKey && isSelected){
27353 this.deselectRow(rowIndex);
27354 }else if(!isSelected){
27355 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27356 view.focusRow(rowIndex);
27360 this.fireEvent("afterselectionchange", this);
27363 handleDragableRowClick : function(grid, rowIndex, e)
27365 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27366 this.selectRow(rowIndex, false);
27367 grid.view.focusRow(rowIndex);
27368 this.fireEvent("afterselectionchange", this);
27373 * Selects multiple rows.
27374 * @param {Array} rows Array of the indexes of the row to select
27375 * @param {Boolean} keepExisting (optional) True to keep existing selections
27377 selectRows : function(rows, keepExisting){
27379 this.clearSelections();
27381 for(var i = 0, len = rows.length; i < len; i++){
27382 this.selectRow(rows[i], true);
27387 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27388 * @param {Number} startRow The index of the first row in the range
27389 * @param {Number} endRow The index of the last row in the range
27390 * @param {Boolean} keepExisting (optional) True to retain existing selections
27392 selectRange : function(startRow, endRow, keepExisting){
27397 this.clearSelections();
27399 if(startRow <= endRow){
27400 for(var i = startRow; i <= endRow; i++){
27401 this.selectRow(i, true);
27404 for(var i = startRow; i >= endRow; i--){
27405 this.selectRow(i, true);
27411 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27412 * @param {Number} startRow The index of the first row in the range
27413 * @param {Number} endRow The index of the last row in the range
27415 deselectRange : function(startRow, endRow, preventViewNotify){
27419 for(var i = startRow; i <= endRow; i++){
27420 this.deselectRow(i, preventViewNotify);
27426 * @param {Number} row The index of the row to select
27427 * @param {Boolean} keepExisting (optional) True to keep existing selections
27429 selectRow : function(index, keepExisting, preventViewNotify)
27431 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27434 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27435 if(!keepExisting || this.singleSelect){
27436 this.clearSelections();
27439 var r = this.grid.store.getAt(index);
27440 //console.log('selectRow - record id :' + r.id);
27442 this.selections.add(r);
27443 this.last = this.lastActive = index;
27444 if(!preventViewNotify){
27445 var proxy = new Roo.Element(
27446 this.grid.getRowDom(index)
27448 proxy.addClass('bg-info info');
27450 this.fireEvent("rowselect", this, index, r);
27451 this.fireEvent("selectionchange", this);
27457 * @param {Number} row The index of the row to deselect
27459 deselectRow : function(index, preventViewNotify)
27464 if(this.last == index){
27467 if(this.lastActive == index){
27468 this.lastActive = false;
27471 var r = this.grid.store.getAt(index);
27476 this.selections.remove(r);
27477 //.console.log('deselectRow - record id :' + r.id);
27478 if(!preventViewNotify){
27480 var proxy = new Roo.Element(
27481 this.grid.getRowDom(index)
27483 proxy.removeClass('bg-info info');
27485 this.fireEvent("rowdeselect", this, index);
27486 this.fireEvent("selectionchange", this);
27490 restoreLast : function(){
27492 this.last = this._last;
27497 acceptsNav : function(row, col, cm){
27498 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27502 onEditorKey : function(field, e){
27503 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27508 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27510 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27512 }else if(k == e.ENTER && !e.ctrlKey){
27516 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27518 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27520 }else if(k == e.ESC){
27524 g.startEditing(newCell[0], newCell[1]);
27530 * Ext JS Library 1.1.1
27531 * Copyright(c) 2006-2007, Ext JS, LLC.
27533 * Originally Released Under LGPL - original licence link has changed is not relivant.
27536 * <script type="text/javascript">
27540 * @class Roo.bootstrap.PagingToolbar
27541 * @extends Roo.bootstrap.NavSimplebar
27542 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27544 * Create a new PagingToolbar
27545 * @param {Object} config The config object
27546 * @param {Roo.data.Store} store
27548 Roo.bootstrap.PagingToolbar = function(config)
27550 // old args format still supported... - xtype is prefered..
27551 // created from xtype...
27553 this.ds = config.dataSource;
27555 if (config.store && !this.ds) {
27556 this.store= Roo.factory(config.store, Roo.data);
27557 this.ds = this.store;
27558 this.ds.xmodule = this.xmodule || false;
27561 this.toolbarItems = [];
27562 if (config.items) {
27563 this.toolbarItems = config.items;
27566 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27571 this.bind(this.ds);
27574 if (Roo.bootstrap.version == 4) {
27575 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27577 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27582 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27584 * @cfg {Roo.data.Store} dataSource
27585 * The underlying data store providing the paged data
27588 * @cfg {String/HTMLElement/Element} container
27589 * container The id or element that will contain the toolbar
27592 * @cfg {Boolean} displayInfo
27593 * True to display the displayMsg (defaults to false)
27596 * @cfg {Number} pageSize
27597 * The number of records to display per page (defaults to 20)
27601 * @cfg {String} displayMsg
27602 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27604 displayMsg : 'Displaying {0} - {1} of {2}',
27606 * @cfg {String} emptyMsg
27607 * The message to display when no records are found (defaults to "No data to display")
27609 emptyMsg : 'No data to display',
27611 * Customizable piece of the default paging text (defaults to "Page")
27614 beforePageText : "Page",
27616 * Customizable piece of the default paging text (defaults to "of %0")
27619 afterPageText : "of {0}",
27621 * Customizable piece of the default paging text (defaults to "First Page")
27624 firstText : "First Page",
27626 * Customizable piece of the default paging text (defaults to "Previous Page")
27629 prevText : "Previous Page",
27631 * Customizable piece of the default paging text (defaults to "Next Page")
27634 nextText : "Next Page",
27636 * Customizable piece of the default paging text (defaults to "Last Page")
27639 lastText : "Last Page",
27641 * Customizable piece of the default paging text (defaults to "Refresh")
27644 refreshText : "Refresh",
27648 onRender : function(ct, position)
27650 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27651 this.navgroup.parentId = this.id;
27652 this.navgroup.onRender(this.el, null);
27653 // add the buttons to the navgroup
27655 if(this.displayInfo){
27656 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27657 this.displayEl = this.el.select('.x-paging-info', true).first();
27658 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27659 // this.displayEl = navel.el.select('span',true).first();
27665 Roo.each(_this.buttons, function(e){ // this might need to use render????
27666 Roo.factory(e).render(_this.el);
27670 Roo.each(_this.toolbarItems, function(e) {
27671 _this.navgroup.addItem(e);
27675 this.first = this.navgroup.addItem({
27676 tooltip: this.firstText,
27677 cls: "prev btn-outline-secondary",
27678 html : ' <i class="fa fa-step-backward"></i>',
27680 preventDefault: true,
27681 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27684 this.prev = this.navgroup.addItem({
27685 tooltip: this.prevText,
27686 cls: "prev btn-outline-secondary",
27687 html : ' <i class="fa fa-backward"></i>',
27689 preventDefault: true,
27690 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27692 //this.addSeparator();
27695 var field = this.navgroup.addItem( {
27697 cls : 'x-paging-position btn-outline-secondary',
27699 html : this.beforePageText +
27700 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27701 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27704 this.field = field.el.select('input', true).first();
27705 this.field.on("keydown", this.onPagingKeydown, this);
27706 this.field.on("focus", function(){this.dom.select();});
27709 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27710 //this.field.setHeight(18);
27711 //this.addSeparator();
27712 this.next = this.navgroup.addItem({
27713 tooltip: this.nextText,
27714 cls: "next btn-outline-secondary",
27715 html : ' <i class="fa fa-forward"></i>',
27717 preventDefault: true,
27718 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27720 this.last = this.navgroup.addItem({
27721 tooltip: this.lastText,
27722 html : ' <i class="fa fa-step-forward"></i>',
27723 cls: "next btn-outline-secondary",
27725 preventDefault: true,
27726 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27728 //this.addSeparator();
27729 this.loading = this.navgroup.addItem({
27730 tooltip: this.refreshText,
27731 cls: "btn-outline-secondary",
27732 html : ' <i class="fa fa-refresh"></i>',
27733 preventDefault: true,
27734 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27740 updateInfo : function(){
27741 if(this.displayEl){
27742 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27743 var msg = count == 0 ?
27747 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27749 this.displayEl.update(msg);
27754 onLoad : function(ds, r, o)
27756 this.cursor = o.params && o.params.start ? o.params.start : 0;
27758 var d = this.getPageData(),
27763 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27764 this.field.dom.value = ap;
27765 this.first.setDisabled(ap == 1);
27766 this.prev.setDisabled(ap == 1);
27767 this.next.setDisabled(ap == ps);
27768 this.last.setDisabled(ap == ps);
27769 this.loading.enable();
27774 getPageData : function(){
27775 var total = this.ds.getTotalCount();
27778 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27779 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27784 onLoadError : function(){
27785 this.loading.enable();
27789 onPagingKeydown : function(e){
27790 var k = e.getKey();
27791 var d = this.getPageData();
27793 var v = this.field.dom.value, pageNum;
27794 if(!v || isNaN(pageNum = parseInt(v, 10))){
27795 this.field.dom.value = d.activePage;
27798 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27799 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27802 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))
27804 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27805 this.field.dom.value = pageNum;
27806 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27809 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27811 var v = this.field.dom.value, pageNum;
27812 var increment = (e.shiftKey) ? 10 : 1;
27813 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27816 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27817 this.field.dom.value = d.activePage;
27820 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27822 this.field.dom.value = parseInt(v, 10) + increment;
27823 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27824 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27831 beforeLoad : function(){
27833 this.loading.disable();
27838 onClick : function(which){
27847 ds.load({params:{start: 0, limit: this.pageSize}});
27850 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27853 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27856 var total = ds.getTotalCount();
27857 var extra = total % this.pageSize;
27858 var lastStart = extra ? (total - extra) : total-this.pageSize;
27859 ds.load({params:{start: lastStart, limit: this.pageSize}});
27862 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27868 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27869 * @param {Roo.data.Store} store The data store to unbind
27871 unbind : function(ds){
27872 ds.un("beforeload", this.beforeLoad, this);
27873 ds.un("load", this.onLoad, this);
27874 ds.un("loadexception", this.onLoadError, this);
27875 ds.un("remove", this.updateInfo, this);
27876 ds.un("add", this.updateInfo, this);
27877 this.ds = undefined;
27881 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27882 * @param {Roo.data.Store} store The data store to bind
27884 bind : function(ds){
27885 ds.on("beforeload", this.beforeLoad, this);
27886 ds.on("load", this.onLoad, this);
27887 ds.on("loadexception", this.onLoadError, this);
27888 ds.on("remove", this.updateInfo, this);
27889 ds.on("add", this.updateInfo, this);
27900 * @class Roo.bootstrap.MessageBar
27901 * @extends Roo.bootstrap.Component
27902 * Bootstrap MessageBar class
27903 * @cfg {String} html contents of the MessageBar
27904 * @cfg {String} weight (info | success | warning | danger) default info
27905 * @cfg {String} beforeClass insert the bar before the given class
27906 * @cfg {Boolean} closable (true | false) default false
27907 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27910 * Create a new Element
27911 * @param {Object} config The config object
27914 Roo.bootstrap.MessageBar = function(config){
27915 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27918 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27924 beforeClass: 'bootstrap-sticky-wrap',
27926 getAutoCreate : function(){
27930 cls: 'alert alert-dismissable alert-' + this.weight,
27935 html: this.html || ''
27941 cfg.cls += ' alert-messages-fixed';
27955 onRender : function(ct, position)
27957 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27960 var cfg = Roo.apply({}, this.getAutoCreate());
27964 cfg.cls += ' ' + this.cls;
27967 cfg.style = this.style;
27969 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27971 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27974 this.el.select('>button.close').on('click', this.hide, this);
27980 if (!this.rendered) {
27986 this.fireEvent('show', this);
27992 if (!this.rendered) {
27998 this.fireEvent('hide', this);
28001 update : function()
28003 // var e = this.el.dom.firstChild;
28005 // if(this.closable){
28006 // e = e.nextSibling;
28009 // e.data = this.html || '';
28011 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28027 * @class Roo.bootstrap.Graph
28028 * @extends Roo.bootstrap.Component
28029 * Bootstrap Graph class
28033 @cfg {String} graphtype bar | vbar | pie
28034 @cfg {number} g_x coodinator | centre x (pie)
28035 @cfg {number} g_y coodinator | centre y (pie)
28036 @cfg {number} g_r radius (pie)
28037 @cfg {number} g_height height of the chart (respected by all elements in the set)
28038 @cfg {number} g_width width of the chart (respected by all elements in the set)
28039 @cfg {Object} title The title of the chart
28042 -opts (object) options for the chart
28044 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28045 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28047 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.
28048 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28050 o stretch (boolean)
28052 -opts (object) options for the pie
28055 o startAngle (number)
28056 o endAngle (number)
28060 * Create a new Input
28061 * @param {Object} config The config object
28064 Roo.bootstrap.Graph = function(config){
28065 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28071 * The img click event for the img.
28072 * @param {Roo.EventObject} e
28078 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28089 //g_colors: this.colors,
28096 getAutoCreate : function(){
28107 onRender : function(ct,position){
28110 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28112 if (typeof(Raphael) == 'undefined') {
28113 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28117 this.raphael = Raphael(this.el.dom);
28119 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28120 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28121 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28122 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28124 r.text(160, 10, "Single Series Chart").attr(txtattr);
28125 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28126 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28127 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28129 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28130 r.barchart(330, 10, 300, 220, data1);
28131 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28132 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28135 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28136 // r.barchart(30, 30, 560, 250, xdata, {
28137 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28138 // axis : "0 0 1 1",
28139 // axisxlabels : xdata
28140 // //yvalues : cols,
28143 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28145 // this.load(null,xdata,{
28146 // axis : "0 0 1 1",
28147 // axisxlabels : xdata
28152 load : function(graphtype,xdata,opts)
28154 this.raphael.clear();
28156 graphtype = this.graphtype;
28161 var r = this.raphael,
28162 fin = function () {
28163 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28165 fout = function () {
28166 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28168 pfin = function() {
28169 this.sector.stop();
28170 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28173 this.label[0].stop();
28174 this.label[0].attr({ r: 7.5 });
28175 this.label[1].attr({ "font-weight": 800 });
28178 pfout = function() {
28179 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28182 this.label[0].animate({ r: 5 }, 500, "bounce");
28183 this.label[1].attr({ "font-weight": 400 });
28189 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28192 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28195 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28196 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28198 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28205 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28210 setTitle: function(o)
28215 initEvents: function() {
28218 this.el.on('click', this.onClick, this);
28222 onClick : function(e)
28224 Roo.log('img onclick');
28225 this.fireEvent('click', this, e);
28237 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28240 * @class Roo.bootstrap.dash.NumberBox
28241 * @extends Roo.bootstrap.Component
28242 * Bootstrap NumberBox class
28243 * @cfg {String} headline Box headline
28244 * @cfg {String} content Box content
28245 * @cfg {String} icon Box icon
28246 * @cfg {String} footer Footer text
28247 * @cfg {String} fhref Footer href
28250 * Create a new NumberBox
28251 * @param {Object} config The config object
28255 Roo.bootstrap.dash.NumberBox = function(config){
28256 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28260 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28269 getAutoCreate : function(){
28273 cls : 'small-box ',
28281 cls : 'roo-headline',
28282 html : this.headline
28286 cls : 'roo-content',
28287 html : this.content
28301 cls : 'ion ' + this.icon
28310 cls : 'small-box-footer',
28311 href : this.fhref || '#',
28315 cfg.cn.push(footer);
28322 onRender : function(ct,position){
28323 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28330 setHeadline: function (value)
28332 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28335 setFooter: function (value, href)
28337 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28340 this.el.select('a.small-box-footer',true).first().attr('href', href);
28345 setContent: function (value)
28347 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28350 initEvents: function()
28364 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28367 * @class Roo.bootstrap.dash.TabBox
28368 * @extends Roo.bootstrap.Component
28369 * Bootstrap TabBox class
28370 * @cfg {String} title Title of the TabBox
28371 * @cfg {String} icon Icon of the TabBox
28372 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28373 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28376 * Create a new TabBox
28377 * @param {Object} config The config object
28381 Roo.bootstrap.dash.TabBox = function(config){
28382 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28387 * When a pane is added
28388 * @param {Roo.bootstrap.dash.TabPane} pane
28392 * @event activatepane
28393 * When a pane is activated
28394 * @param {Roo.bootstrap.dash.TabPane} pane
28396 "activatepane" : true
28404 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28409 tabScrollable : false,
28411 getChildContainer : function()
28413 return this.el.select('.tab-content', true).first();
28416 getAutoCreate : function(){
28420 cls: 'pull-left header',
28428 cls: 'fa ' + this.icon
28434 cls: 'nav nav-tabs pull-right',
28440 if(this.tabScrollable){
28447 cls: 'nav nav-tabs pull-right',
28458 cls: 'nav-tabs-custom',
28463 cls: 'tab-content no-padding',
28471 initEvents : function()
28473 //Roo.log('add add pane handler');
28474 this.on('addpane', this.onAddPane, this);
28477 * Updates the box title
28478 * @param {String} html to set the title to.
28480 setTitle : function(value)
28482 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28484 onAddPane : function(pane)
28486 this.panes.push(pane);
28487 //Roo.log('addpane');
28489 // tabs are rendere left to right..
28490 if(!this.showtabs){
28494 var ctr = this.el.select('.nav-tabs', true).first();
28497 var existing = ctr.select('.nav-tab',true);
28498 var qty = existing.getCount();;
28501 var tab = ctr.createChild({
28503 cls : 'nav-tab' + (qty ? '' : ' active'),
28511 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28514 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28516 pane.el.addClass('active');
28521 onTabClick : function(ev,un,ob,pane)
28523 //Roo.log('tab - prev default');
28524 ev.preventDefault();
28527 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28528 pane.tab.addClass('active');
28529 //Roo.log(pane.title);
28530 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28531 // technically we should have a deactivate event.. but maybe add later.
28532 // and it should not de-activate the selected tab...
28533 this.fireEvent('activatepane', pane);
28534 pane.el.addClass('active');
28535 pane.fireEvent('activate');
28540 getActivePane : function()
28543 Roo.each(this.panes, function(p) {
28544 if(p.el.hasClass('active')){
28565 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28567 * @class Roo.bootstrap.TabPane
28568 * @extends Roo.bootstrap.Component
28569 * Bootstrap TabPane class
28570 * @cfg {Boolean} active (false | true) Default false
28571 * @cfg {String} title title of panel
28575 * Create a new TabPane
28576 * @param {Object} config The config object
28579 Roo.bootstrap.dash.TabPane = function(config){
28580 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28586 * When a pane is activated
28587 * @param {Roo.bootstrap.dash.TabPane} pane
28594 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28599 // the tabBox that this is attached to.
28602 getAutoCreate : function()
28610 cfg.cls += ' active';
28615 initEvents : function()
28617 //Roo.log('trigger add pane handler');
28618 this.parent().fireEvent('addpane', this)
28622 * Updates the tab title
28623 * @param {String} html to set the title to.
28625 setTitle: function(str)
28631 this.tab.select('a', true).first().dom.innerHTML = str;
28648 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28651 * @class Roo.bootstrap.menu.Menu
28652 * @extends Roo.bootstrap.Component
28653 * Bootstrap Menu class - container for Menu
28654 * @cfg {String} html Text of the menu
28655 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28656 * @cfg {String} icon Font awesome icon
28657 * @cfg {String} pos Menu align to (top | bottom) default bottom
28661 * Create a new Menu
28662 * @param {Object} config The config object
28666 Roo.bootstrap.menu.Menu = function(config){
28667 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28671 * @event beforeshow
28672 * Fires before this menu is displayed
28673 * @param {Roo.bootstrap.menu.Menu} this
28677 * @event beforehide
28678 * Fires before this menu is hidden
28679 * @param {Roo.bootstrap.menu.Menu} this
28684 * Fires after this menu is displayed
28685 * @param {Roo.bootstrap.menu.Menu} this
28690 * Fires after this menu is hidden
28691 * @param {Roo.bootstrap.menu.Menu} this
28696 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28697 * @param {Roo.bootstrap.menu.Menu} this
28698 * @param {Roo.EventObject} e
28705 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28709 weight : 'default',
28714 getChildContainer : function() {
28715 if(this.isSubMenu){
28719 return this.el.select('ul.dropdown-menu', true).first();
28722 getAutoCreate : function()
28727 cls : 'roo-menu-text',
28735 cls : 'fa ' + this.icon
28746 cls : 'dropdown-button btn btn-' + this.weight,
28751 cls : 'dropdown-toggle btn btn-' + this.weight,
28761 cls : 'dropdown-menu'
28767 if(this.pos == 'top'){
28768 cfg.cls += ' dropup';
28771 if(this.isSubMenu){
28774 cls : 'dropdown-menu'
28781 onRender : function(ct, position)
28783 this.isSubMenu = ct.hasClass('dropdown-submenu');
28785 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28788 initEvents : function()
28790 if(this.isSubMenu){
28794 this.hidden = true;
28796 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28797 this.triggerEl.on('click', this.onTriggerPress, this);
28799 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28800 this.buttonEl.on('click', this.onClick, this);
28806 if(this.isSubMenu){
28810 return this.el.select('ul.dropdown-menu', true).first();
28813 onClick : function(e)
28815 this.fireEvent("click", this, e);
28818 onTriggerPress : function(e)
28820 if (this.isVisible()) {
28827 isVisible : function(){
28828 return !this.hidden;
28833 this.fireEvent("beforeshow", this);
28835 this.hidden = false;
28836 this.el.addClass('open');
28838 Roo.get(document).on("mouseup", this.onMouseUp, this);
28840 this.fireEvent("show", this);
28847 this.fireEvent("beforehide", this);
28849 this.hidden = true;
28850 this.el.removeClass('open');
28852 Roo.get(document).un("mouseup", this.onMouseUp);
28854 this.fireEvent("hide", this);
28857 onMouseUp : function()
28871 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28874 * @class Roo.bootstrap.menu.Item
28875 * @extends Roo.bootstrap.Component
28876 * Bootstrap MenuItem class
28877 * @cfg {Boolean} submenu (true | false) default false
28878 * @cfg {String} html text of the item
28879 * @cfg {String} href the link
28880 * @cfg {Boolean} disable (true | false) default false
28881 * @cfg {Boolean} preventDefault (true | false) default true
28882 * @cfg {String} icon Font awesome icon
28883 * @cfg {String} pos Submenu align to (left | right) default right
28887 * Create a new Item
28888 * @param {Object} config The config object
28892 Roo.bootstrap.menu.Item = function(config){
28893 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28897 * Fires when the mouse is hovering over this menu
28898 * @param {Roo.bootstrap.menu.Item} this
28899 * @param {Roo.EventObject} e
28904 * Fires when the mouse exits this menu
28905 * @param {Roo.bootstrap.menu.Item} this
28906 * @param {Roo.EventObject} e
28912 * The raw click event for the entire grid.
28913 * @param {Roo.EventObject} e
28919 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28924 preventDefault: true,
28929 getAutoCreate : function()
28934 cls : 'roo-menu-item-text',
28942 cls : 'fa ' + this.icon
28951 href : this.href || '#',
28958 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28962 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28964 if(this.pos == 'left'){
28965 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28972 initEvents : function()
28974 this.el.on('mouseover', this.onMouseOver, this);
28975 this.el.on('mouseout', this.onMouseOut, this);
28977 this.el.select('a', true).first().on('click', this.onClick, this);
28981 onClick : function(e)
28983 if(this.preventDefault){
28984 e.preventDefault();
28987 this.fireEvent("click", this, e);
28990 onMouseOver : function(e)
28992 if(this.submenu && this.pos == 'left'){
28993 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28996 this.fireEvent("mouseover", this, e);
28999 onMouseOut : function(e)
29001 this.fireEvent("mouseout", this, e);
29013 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29016 * @class Roo.bootstrap.menu.Separator
29017 * @extends Roo.bootstrap.Component
29018 * Bootstrap Separator class
29021 * Create a new Separator
29022 * @param {Object} config The config object
29026 Roo.bootstrap.menu.Separator = function(config){
29027 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29030 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29032 getAutoCreate : function(){
29035 cls: 'dropdown-divider divider'
29053 * @class Roo.bootstrap.Tooltip
29054 * Bootstrap Tooltip class
29055 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29056 * to determine which dom element triggers the tooltip.
29058 * It needs to add support for additional attributes like tooltip-position
29061 * Create a new Toolti
29062 * @param {Object} config The config object
29065 Roo.bootstrap.Tooltip = function(config){
29066 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29068 this.alignment = Roo.bootstrap.Tooltip.alignment;
29070 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29071 this.alignment = config.alignment;
29076 Roo.apply(Roo.bootstrap.Tooltip, {
29078 * @function init initialize tooltip monitoring.
29082 currentTip : false,
29083 currentRegion : false,
29089 Roo.get(document).on('mouseover', this.enter ,this);
29090 Roo.get(document).on('mouseout', this.leave, this);
29093 this.currentTip = new Roo.bootstrap.Tooltip();
29096 enter : function(ev)
29098 var dom = ev.getTarget();
29100 //Roo.log(['enter',dom]);
29101 var el = Roo.fly(dom);
29102 if (this.currentEl) {
29104 //Roo.log(this.currentEl);
29105 //Roo.log(this.currentEl.contains(dom));
29106 if (this.currentEl == el) {
29109 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29115 if (this.currentTip.el) {
29116 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29120 if(!el || el.dom == document){
29126 if (!el.attr('tooltip')) {
29127 pel = el.findParent("[tooltip]");
29129 bindEl = Roo.get(pel);
29135 // you can not look for children, as if el is the body.. then everythign is the child..
29136 if (!pel && !el.attr('tooltip')) { //
29137 if (!el.select("[tooltip]").elements.length) {
29140 // is the mouse over this child...?
29141 bindEl = el.select("[tooltip]").first();
29142 var xy = ev.getXY();
29143 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29144 //Roo.log("not in region.");
29147 //Roo.log("child element over..");
29150 this.currentEl = el;
29151 this.currentTip.bind(bindEl);
29152 this.currentRegion = Roo.lib.Region.getRegion(dom);
29153 this.currentTip.enter();
29156 leave : function(ev)
29158 var dom = ev.getTarget();
29159 //Roo.log(['leave',dom]);
29160 if (!this.currentEl) {
29165 if (dom != this.currentEl.dom) {
29168 var xy = ev.getXY();
29169 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29172 // only activate leave if mouse cursor is outside... bounding box..
29177 if (this.currentTip) {
29178 this.currentTip.leave();
29180 //Roo.log('clear currentEl');
29181 this.currentEl = false;
29186 'left' : ['r-l', [-2,0], 'right'],
29187 'right' : ['l-r', [2,0], 'left'],
29188 'bottom' : ['t-b', [0,2], 'top'],
29189 'top' : [ 'b-t', [0,-2], 'bottom']
29195 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29200 delay : null, // can be { show : 300 , hide: 500}
29204 hoverState : null, //???
29206 placement : 'bottom',
29210 getAutoCreate : function(){
29217 cls : 'tooltip-arrow arrow'
29220 cls : 'tooltip-inner'
29227 bind : function(el)
29232 initEvents : function()
29234 this.arrowEl = this.el.select('.arrow', true).first();
29235 this.innerEl = this.el.select('.tooltip-inner', true).first();
29238 enter : function () {
29240 if (this.timeout != null) {
29241 clearTimeout(this.timeout);
29244 this.hoverState = 'in';
29245 //Roo.log("enter - show");
29246 if (!this.delay || !this.delay.show) {
29251 this.timeout = setTimeout(function () {
29252 if (_t.hoverState == 'in') {
29255 }, this.delay.show);
29259 clearTimeout(this.timeout);
29261 this.hoverState = 'out';
29262 if (!this.delay || !this.delay.hide) {
29268 this.timeout = setTimeout(function () {
29269 //Roo.log("leave - timeout");
29271 if (_t.hoverState == 'out') {
29273 Roo.bootstrap.Tooltip.currentEl = false;
29278 show : function (msg)
29281 this.render(document.body);
29284 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29286 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29288 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29290 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29291 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29293 var placement = typeof this.placement == 'function' ?
29294 this.placement.call(this, this.el, on_el) :
29297 var autoToken = /\s?auto?\s?/i;
29298 var autoPlace = autoToken.test(placement);
29300 placement = placement.replace(autoToken, '') || 'top';
29304 //this.el.setXY([0,0]);
29306 //this.el.dom.style.display='block';
29308 //this.el.appendTo(on_el);
29310 var p = this.getPosition();
29311 var box = this.el.getBox();
29317 var align = this.alignment[placement];
29319 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29321 if(placement == 'top' || placement == 'bottom'){
29323 placement = 'right';
29326 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29327 placement = 'left';
29330 var scroll = Roo.select('body', true).first().getScroll();
29332 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29336 align = this.alignment[placement];
29338 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29342 var elems = document.getElementsByTagName('div');
29343 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29344 for (var i = 0; i < elems.length; i++) {
29345 var zindex = Number.parseInt(
29346 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29349 if (zindex > highest) {
29356 this.el.dom.style.zIndex = highest;
29358 this.el.alignTo(this.bindEl, align[0],align[1]);
29359 //var arrow = this.el.select('.arrow',true).first();
29360 //arrow.set(align[2],
29362 this.el.addClass(placement);
29363 this.el.addClass("bs-tooltip-"+ placement);
29365 this.el.addClass('in fade show');
29367 this.hoverState = null;
29369 if (this.el.hasClass('fade')) {
29384 //this.el.setXY([0,0]);
29385 this.el.removeClass(['show', 'in']);
29401 * @class Roo.bootstrap.LocationPicker
29402 * @extends Roo.bootstrap.Component
29403 * Bootstrap LocationPicker class
29404 * @cfg {Number} latitude Position when init default 0
29405 * @cfg {Number} longitude Position when init default 0
29406 * @cfg {Number} zoom default 15
29407 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29408 * @cfg {Boolean} mapTypeControl default false
29409 * @cfg {Boolean} disableDoubleClickZoom default false
29410 * @cfg {Boolean} scrollwheel default true
29411 * @cfg {Boolean} streetViewControl default false
29412 * @cfg {Number} radius default 0
29413 * @cfg {String} locationName
29414 * @cfg {Boolean} draggable default true
29415 * @cfg {Boolean} enableAutocomplete default false
29416 * @cfg {Boolean} enableReverseGeocode default true
29417 * @cfg {String} markerTitle
29420 * Create a new LocationPicker
29421 * @param {Object} config The config object
29425 Roo.bootstrap.LocationPicker = function(config){
29427 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29432 * Fires when the picker initialized.
29433 * @param {Roo.bootstrap.LocationPicker} this
29434 * @param {Google Location} location
29438 * @event positionchanged
29439 * Fires when the picker position changed.
29440 * @param {Roo.bootstrap.LocationPicker} this
29441 * @param {Google Location} location
29443 positionchanged : true,
29446 * Fires when the map resize.
29447 * @param {Roo.bootstrap.LocationPicker} this
29452 * Fires when the map show.
29453 * @param {Roo.bootstrap.LocationPicker} this
29458 * Fires when the map hide.
29459 * @param {Roo.bootstrap.LocationPicker} this
29464 * Fires when click the map.
29465 * @param {Roo.bootstrap.LocationPicker} this
29466 * @param {Map event} e
29470 * @event mapRightClick
29471 * Fires when right click the map.
29472 * @param {Roo.bootstrap.LocationPicker} this
29473 * @param {Map event} e
29475 mapRightClick : true,
29477 * @event markerClick
29478 * Fires when click the marker.
29479 * @param {Roo.bootstrap.LocationPicker} this
29480 * @param {Map event} e
29482 markerClick : true,
29484 * @event markerRightClick
29485 * Fires when right click the marker.
29486 * @param {Roo.bootstrap.LocationPicker} this
29487 * @param {Map event} e
29489 markerRightClick : true,
29491 * @event OverlayViewDraw
29492 * Fires when OverlayView Draw
29493 * @param {Roo.bootstrap.LocationPicker} this
29495 OverlayViewDraw : true,
29497 * @event OverlayViewOnAdd
29498 * Fires when OverlayView Draw
29499 * @param {Roo.bootstrap.LocationPicker} this
29501 OverlayViewOnAdd : true,
29503 * @event OverlayViewOnRemove
29504 * Fires when OverlayView Draw
29505 * @param {Roo.bootstrap.LocationPicker} this
29507 OverlayViewOnRemove : true,
29509 * @event OverlayViewShow
29510 * Fires when OverlayView Draw
29511 * @param {Roo.bootstrap.LocationPicker} this
29512 * @param {Pixel} cpx
29514 OverlayViewShow : true,
29516 * @event OverlayViewHide
29517 * Fires when OverlayView Draw
29518 * @param {Roo.bootstrap.LocationPicker} this
29520 OverlayViewHide : true,
29522 * @event loadexception
29523 * Fires when load google lib failed.
29524 * @param {Roo.bootstrap.LocationPicker} this
29526 loadexception : true
29531 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29533 gMapContext: false,
29539 mapTypeControl: false,
29540 disableDoubleClickZoom: false,
29542 streetViewControl: false,
29546 enableAutocomplete: false,
29547 enableReverseGeocode: true,
29550 getAutoCreate: function()
29555 cls: 'roo-location-picker'
29561 initEvents: function(ct, position)
29563 if(!this.el.getWidth() || this.isApplied()){
29567 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29572 initial: function()
29574 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29575 this.fireEvent('loadexception', this);
29579 if(!this.mapTypeId){
29580 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29583 this.gMapContext = this.GMapContext();
29585 this.initOverlayView();
29587 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29591 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29592 _this.setPosition(_this.gMapContext.marker.position);
29595 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29596 _this.fireEvent('mapClick', this, event);
29600 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29601 _this.fireEvent('mapRightClick', this, event);
29605 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29606 _this.fireEvent('markerClick', this, event);
29610 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29611 _this.fireEvent('markerRightClick', this, event);
29615 this.setPosition(this.gMapContext.location);
29617 this.fireEvent('initial', this, this.gMapContext.location);
29620 initOverlayView: function()
29624 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29628 _this.fireEvent('OverlayViewDraw', _this);
29633 _this.fireEvent('OverlayViewOnAdd', _this);
29636 onRemove: function()
29638 _this.fireEvent('OverlayViewOnRemove', _this);
29641 show: function(cpx)
29643 _this.fireEvent('OverlayViewShow', _this, cpx);
29648 _this.fireEvent('OverlayViewHide', _this);
29654 fromLatLngToContainerPixel: function(event)
29656 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29659 isApplied: function()
29661 return this.getGmapContext() == false ? false : true;
29664 getGmapContext: function()
29666 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29669 GMapContext: function()
29671 var position = new google.maps.LatLng(this.latitude, this.longitude);
29673 var _map = new google.maps.Map(this.el.dom, {
29676 mapTypeId: this.mapTypeId,
29677 mapTypeControl: this.mapTypeControl,
29678 disableDoubleClickZoom: this.disableDoubleClickZoom,
29679 scrollwheel: this.scrollwheel,
29680 streetViewControl: this.streetViewControl,
29681 locationName: this.locationName,
29682 draggable: this.draggable,
29683 enableAutocomplete: this.enableAutocomplete,
29684 enableReverseGeocode: this.enableReverseGeocode
29687 var _marker = new google.maps.Marker({
29688 position: position,
29690 title: this.markerTitle,
29691 draggable: this.draggable
29698 location: position,
29699 radius: this.radius,
29700 locationName: this.locationName,
29701 addressComponents: {
29702 formatted_address: null,
29703 addressLine1: null,
29704 addressLine2: null,
29706 streetNumber: null,
29710 stateOrProvince: null
29713 domContainer: this.el.dom,
29714 geodecoder: new google.maps.Geocoder()
29718 drawCircle: function(center, radius, options)
29720 if (this.gMapContext.circle != null) {
29721 this.gMapContext.circle.setMap(null);
29725 options = Roo.apply({}, options, {
29726 strokeColor: "#0000FF",
29727 strokeOpacity: .35,
29729 fillColor: "#0000FF",
29733 options.map = this.gMapContext.map;
29734 options.radius = radius;
29735 options.center = center;
29736 this.gMapContext.circle = new google.maps.Circle(options);
29737 return this.gMapContext.circle;
29743 setPosition: function(location)
29745 this.gMapContext.location = location;
29746 this.gMapContext.marker.setPosition(location);
29747 this.gMapContext.map.panTo(location);
29748 this.drawCircle(location, this.gMapContext.radius, {});
29752 if (this.gMapContext.settings.enableReverseGeocode) {
29753 this.gMapContext.geodecoder.geocode({
29754 latLng: this.gMapContext.location
29755 }, function(results, status) {
29757 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29758 _this.gMapContext.locationName = results[0].formatted_address;
29759 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29761 _this.fireEvent('positionchanged', this, location);
29768 this.fireEvent('positionchanged', this, location);
29773 google.maps.event.trigger(this.gMapContext.map, "resize");
29775 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29777 this.fireEvent('resize', this);
29780 setPositionByLatLng: function(latitude, longitude)
29782 this.setPosition(new google.maps.LatLng(latitude, longitude));
29785 getCurrentPosition: function()
29788 latitude: this.gMapContext.location.lat(),
29789 longitude: this.gMapContext.location.lng()
29793 getAddressName: function()
29795 return this.gMapContext.locationName;
29798 getAddressComponents: function()
29800 return this.gMapContext.addressComponents;
29803 address_component_from_google_geocode: function(address_components)
29807 for (var i = 0; i < address_components.length; i++) {
29808 var component = address_components[i];
29809 if (component.types.indexOf("postal_code") >= 0) {
29810 result.postalCode = component.short_name;
29811 } else if (component.types.indexOf("street_number") >= 0) {
29812 result.streetNumber = component.short_name;
29813 } else if (component.types.indexOf("route") >= 0) {
29814 result.streetName = component.short_name;
29815 } else if (component.types.indexOf("neighborhood") >= 0) {
29816 result.city = component.short_name;
29817 } else if (component.types.indexOf("locality") >= 0) {
29818 result.city = component.short_name;
29819 } else if (component.types.indexOf("sublocality") >= 0) {
29820 result.district = component.short_name;
29821 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29822 result.stateOrProvince = component.short_name;
29823 } else if (component.types.indexOf("country") >= 0) {
29824 result.country = component.short_name;
29828 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29829 result.addressLine2 = "";
29833 setZoomLevel: function(zoom)
29835 this.gMapContext.map.setZoom(zoom);
29848 this.fireEvent('show', this);
29859 this.fireEvent('hide', this);
29864 Roo.apply(Roo.bootstrap.LocationPicker, {
29866 OverlayView : function(map, options)
29868 options = options || {};
29875 * @class Roo.bootstrap.Alert
29876 * @extends Roo.bootstrap.Component
29877 * Bootstrap Alert class - shows an alert area box
29879 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29880 Enter a valid email address
29883 * @cfg {String} title The title of alert
29884 * @cfg {String} html The content of alert
29885 * @cfg {String} weight ( success | info | warning | danger )
29886 * @cfg {String} fa font-awesomeicon
29887 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29888 * @cfg {Boolean} close true to show a x closer
29892 * Create a new alert
29893 * @param {Object} config The config object
29897 Roo.bootstrap.Alert = function(config){
29898 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29902 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29908 faicon: false, // BC
29912 getAutoCreate : function()
29924 style : this.close ? '' : 'display:none'
29928 cls : 'roo-alert-icon'
29933 cls : 'roo-alert-title',
29938 cls : 'roo-alert-text',
29945 cfg.cn[0].cls += ' fa ' + this.faicon;
29948 cfg.cn[0].cls += ' fa ' + this.fa;
29952 cfg.cls += ' alert-' + this.weight;
29958 initEvents: function()
29960 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29961 this.titleEl = this.el.select('.roo-alert-title',true).first();
29962 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29963 if (this.seconds > 0) {
29964 this.hide.defer(this.seconds, this);
29968 setTitle : function(str)
29970 this.titleEl.dom.innerHTML = str;
29973 setText : function(str)
29975 this.titleEl.dom.innerHTML = str;
29978 setWeight : function(weight)
29981 this.el.removeClass('alert-' + this.weight);
29984 this.weight = weight;
29986 this.el.addClass('alert-' + this.weight);
29989 setIcon : function(icon)
29992 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29995 this.faicon = icon;
29997 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30018 * @class Roo.bootstrap.UploadCropbox
30019 * @extends Roo.bootstrap.Component
30020 * Bootstrap UploadCropbox class
30021 * @cfg {String} emptyText show when image has been loaded
30022 * @cfg {String} rotateNotify show when image too small to rotate
30023 * @cfg {Number} errorTimeout default 3000
30024 * @cfg {Number} minWidth default 300
30025 * @cfg {Number} minHeight default 300
30026 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30027 * @cfg {Boolean} isDocument (true|false) default false
30028 * @cfg {String} url action url
30029 * @cfg {String} paramName default 'imageUpload'
30030 * @cfg {String} method default POST
30031 * @cfg {Boolean} loadMask (true|false) default true
30032 * @cfg {Boolean} loadingText default 'Loading...'
30035 * Create a new UploadCropbox
30036 * @param {Object} config The config object
30039 Roo.bootstrap.UploadCropbox = function(config){
30040 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30044 * @event beforeselectfile
30045 * Fire before select file
30046 * @param {Roo.bootstrap.UploadCropbox} this
30048 "beforeselectfile" : true,
30051 * Fire after initEvent
30052 * @param {Roo.bootstrap.UploadCropbox} this
30057 * Fire after initEvent
30058 * @param {Roo.bootstrap.UploadCropbox} this
30059 * @param {String} data
30064 * Fire when preparing the file data
30065 * @param {Roo.bootstrap.UploadCropbox} this
30066 * @param {Object} file
30071 * Fire when get exception
30072 * @param {Roo.bootstrap.UploadCropbox} this
30073 * @param {XMLHttpRequest} xhr
30075 "exception" : true,
30077 * @event beforeloadcanvas
30078 * Fire before load the canvas
30079 * @param {Roo.bootstrap.UploadCropbox} this
30080 * @param {String} src
30082 "beforeloadcanvas" : true,
30085 * Fire when trash image
30086 * @param {Roo.bootstrap.UploadCropbox} this
30091 * Fire when download the image
30092 * @param {Roo.bootstrap.UploadCropbox} this
30096 * @event footerbuttonclick
30097 * Fire when footerbuttonclick
30098 * @param {Roo.bootstrap.UploadCropbox} this
30099 * @param {String} type
30101 "footerbuttonclick" : true,
30105 * @param {Roo.bootstrap.UploadCropbox} this
30110 * Fire when rotate the image
30111 * @param {Roo.bootstrap.UploadCropbox} this
30112 * @param {String} pos
30117 * Fire when inspect the file
30118 * @param {Roo.bootstrap.UploadCropbox} this
30119 * @param {Object} file
30124 * Fire when xhr upload the file
30125 * @param {Roo.bootstrap.UploadCropbox} this
30126 * @param {Object} data
30131 * Fire when arrange the file data
30132 * @param {Roo.bootstrap.UploadCropbox} this
30133 * @param {Object} formData
30138 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30141 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30143 emptyText : 'Click to upload image',
30144 rotateNotify : 'Image is too small to rotate',
30145 errorTimeout : 3000,
30159 cropType : 'image/jpeg',
30161 canvasLoaded : false,
30162 isDocument : false,
30164 paramName : 'imageUpload',
30166 loadingText : 'Loading...',
30169 getAutoCreate : function()
30173 cls : 'roo-upload-cropbox',
30177 cls : 'roo-upload-cropbox-selector',
30182 cls : 'roo-upload-cropbox-body',
30183 style : 'cursor:pointer',
30187 cls : 'roo-upload-cropbox-preview'
30191 cls : 'roo-upload-cropbox-thumb'
30195 cls : 'roo-upload-cropbox-empty-notify',
30196 html : this.emptyText
30200 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30201 html : this.rotateNotify
30207 cls : 'roo-upload-cropbox-footer',
30210 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30220 onRender : function(ct, position)
30222 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30224 if (this.buttons.length) {
30226 Roo.each(this.buttons, function(bb) {
30228 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30230 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30236 this.maskEl = this.el;
30240 initEvents : function()
30242 this.urlAPI = (window.createObjectURL && window) ||
30243 (window.URL && URL.revokeObjectURL && URL) ||
30244 (window.webkitURL && webkitURL);
30246 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30247 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30249 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30250 this.selectorEl.hide();
30252 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30253 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30255 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30256 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30257 this.thumbEl.hide();
30259 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30260 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30263 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30264 this.errorEl.hide();
30266 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30267 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30268 this.footerEl.hide();
30270 this.setThumbBoxSize();
30276 this.fireEvent('initial', this);
30283 window.addEventListener("resize", function() { _this.resize(); } );
30285 this.bodyEl.on('click', this.beforeSelectFile, this);
30288 this.bodyEl.on('touchstart', this.onTouchStart, this);
30289 this.bodyEl.on('touchmove', this.onTouchMove, this);
30290 this.bodyEl.on('touchend', this.onTouchEnd, this);
30294 this.bodyEl.on('mousedown', this.onMouseDown, this);
30295 this.bodyEl.on('mousemove', this.onMouseMove, this);
30296 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30297 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30298 Roo.get(document).on('mouseup', this.onMouseUp, this);
30301 this.selectorEl.on('change', this.onFileSelected, this);
30307 this.baseScale = 1;
30309 this.baseRotate = 1;
30310 this.dragable = false;
30311 this.pinching = false;
30314 this.cropData = false;
30315 this.notifyEl.dom.innerHTML = this.emptyText;
30317 this.selectorEl.dom.value = '';
30321 resize : function()
30323 if(this.fireEvent('resize', this) != false){
30324 this.setThumbBoxPosition();
30325 this.setCanvasPosition();
30329 onFooterButtonClick : function(e, el, o, type)
30332 case 'rotate-left' :
30333 this.onRotateLeft(e);
30335 case 'rotate-right' :
30336 this.onRotateRight(e);
30339 this.beforeSelectFile(e);
30354 this.fireEvent('footerbuttonclick', this, type);
30357 beforeSelectFile : function(e)
30359 e.preventDefault();
30361 if(this.fireEvent('beforeselectfile', this) != false){
30362 this.selectorEl.dom.click();
30366 onFileSelected : function(e)
30368 e.preventDefault();
30370 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30374 var file = this.selectorEl.dom.files[0];
30376 if(this.fireEvent('inspect', this, file) != false){
30377 this.prepare(file);
30382 trash : function(e)
30384 this.fireEvent('trash', this);
30387 download : function(e)
30389 this.fireEvent('download', this);
30392 loadCanvas : function(src)
30394 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30398 this.imageEl = document.createElement('img');
30402 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30404 this.imageEl.src = src;
30408 onLoadCanvas : function()
30410 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30411 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30413 this.bodyEl.un('click', this.beforeSelectFile, this);
30415 this.notifyEl.hide();
30416 this.thumbEl.show();
30417 this.footerEl.show();
30419 this.baseRotateLevel();
30421 if(this.isDocument){
30422 this.setThumbBoxSize();
30425 this.setThumbBoxPosition();
30427 this.baseScaleLevel();
30433 this.canvasLoaded = true;
30436 this.maskEl.unmask();
30441 setCanvasPosition : function()
30443 if(!this.canvasEl){
30447 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30448 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30450 this.previewEl.setLeft(pw);
30451 this.previewEl.setTop(ph);
30455 onMouseDown : function(e)
30459 this.dragable = true;
30460 this.pinching = false;
30462 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30463 this.dragable = false;
30467 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30468 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30472 onMouseMove : function(e)
30476 if(!this.canvasLoaded){
30480 if (!this.dragable){
30484 var minX = Math.ceil(this.thumbEl.getLeft(true));
30485 var minY = Math.ceil(this.thumbEl.getTop(true));
30487 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30488 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30490 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30491 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30493 x = x - this.mouseX;
30494 y = y - this.mouseY;
30496 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30497 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30499 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30500 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30502 this.previewEl.setLeft(bgX);
30503 this.previewEl.setTop(bgY);
30505 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30506 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30509 onMouseUp : function(e)
30513 this.dragable = false;
30516 onMouseWheel : function(e)
30520 this.startScale = this.scale;
30522 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30524 if(!this.zoomable()){
30525 this.scale = this.startScale;
30534 zoomable : function()
30536 var minScale = this.thumbEl.getWidth() / this.minWidth;
30538 if(this.minWidth < this.minHeight){
30539 minScale = this.thumbEl.getHeight() / this.minHeight;
30542 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30543 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30547 (this.rotate == 0 || this.rotate == 180) &&
30549 width > this.imageEl.OriginWidth ||
30550 height > this.imageEl.OriginHeight ||
30551 (width < this.minWidth && height < this.minHeight)
30559 (this.rotate == 90 || this.rotate == 270) &&
30561 width > this.imageEl.OriginWidth ||
30562 height > this.imageEl.OriginHeight ||
30563 (width < this.minHeight && height < this.minWidth)
30570 !this.isDocument &&
30571 (this.rotate == 0 || this.rotate == 180) &&
30573 width < this.minWidth ||
30574 width > this.imageEl.OriginWidth ||
30575 height < this.minHeight ||
30576 height > this.imageEl.OriginHeight
30583 !this.isDocument &&
30584 (this.rotate == 90 || this.rotate == 270) &&
30586 width < this.minHeight ||
30587 width > this.imageEl.OriginWidth ||
30588 height < this.minWidth ||
30589 height > this.imageEl.OriginHeight
30599 onRotateLeft : function(e)
30601 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30603 var minScale = this.thumbEl.getWidth() / this.minWidth;
30605 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30606 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30608 this.startScale = this.scale;
30610 while (this.getScaleLevel() < minScale){
30612 this.scale = this.scale + 1;
30614 if(!this.zoomable()){
30619 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30620 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30625 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30632 this.scale = this.startScale;
30634 this.onRotateFail();
30639 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30641 if(this.isDocument){
30642 this.setThumbBoxSize();
30643 this.setThumbBoxPosition();
30644 this.setCanvasPosition();
30649 this.fireEvent('rotate', this, 'left');
30653 onRotateRight : function(e)
30655 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30657 var minScale = this.thumbEl.getWidth() / this.minWidth;
30659 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30660 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30662 this.startScale = this.scale;
30664 while (this.getScaleLevel() < minScale){
30666 this.scale = this.scale + 1;
30668 if(!this.zoomable()){
30673 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30674 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30679 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30686 this.scale = this.startScale;
30688 this.onRotateFail();
30693 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30695 if(this.isDocument){
30696 this.setThumbBoxSize();
30697 this.setThumbBoxPosition();
30698 this.setCanvasPosition();
30703 this.fireEvent('rotate', this, 'right');
30706 onRotateFail : function()
30708 this.errorEl.show(true);
30712 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30717 this.previewEl.dom.innerHTML = '';
30719 var canvasEl = document.createElement("canvas");
30721 var contextEl = canvasEl.getContext("2d");
30723 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30724 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30725 var center = this.imageEl.OriginWidth / 2;
30727 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30728 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30729 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30730 center = this.imageEl.OriginHeight / 2;
30733 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30735 contextEl.translate(center, center);
30736 contextEl.rotate(this.rotate * Math.PI / 180);
30738 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30740 this.canvasEl = document.createElement("canvas");
30742 this.contextEl = this.canvasEl.getContext("2d");
30744 switch (this.rotate) {
30747 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30748 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30750 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30755 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30756 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30758 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30759 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);
30763 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30768 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30769 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30771 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30772 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);
30776 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);
30781 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30782 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30784 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30785 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30789 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);
30796 this.previewEl.appendChild(this.canvasEl);
30798 this.setCanvasPosition();
30803 if(!this.canvasLoaded){
30807 var imageCanvas = document.createElement("canvas");
30809 var imageContext = imageCanvas.getContext("2d");
30811 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30812 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30814 var center = imageCanvas.width / 2;
30816 imageContext.translate(center, center);
30818 imageContext.rotate(this.rotate * Math.PI / 180);
30820 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30822 var canvas = document.createElement("canvas");
30824 var context = canvas.getContext("2d");
30826 canvas.width = this.minWidth;
30827 canvas.height = this.minHeight;
30829 switch (this.rotate) {
30832 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30833 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30835 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30836 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30838 var targetWidth = this.minWidth - 2 * x;
30839 var targetHeight = this.minHeight - 2 * y;
30843 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30844 scale = targetWidth / width;
30847 if(x > 0 && y == 0){
30848 scale = targetHeight / height;
30851 if(x > 0 && y > 0){
30852 scale = targetWidth / width;
30854 if(width < height){
30855 scale = targetHeight / height;
30859 context.scale(scale, scale);
30861 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30862 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30864 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30865 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30867 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30872 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30873 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30875 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30876 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30878 var targetWidth = this.minWidth - 2 * x;
30879 var targetHeight = this.minHeight - 2 * y;
30883 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30884 scale = targetWidth / width;
30887 if(x > 0 && y == 0){
30888 scale = targetHeight / height;
30891 if(x > 0 && y > 0){
30892 scale = targetWidth / width;
30894 if(width < height){
30895 scale = targetHeight / height;
30899 context.scale(scale, scale);
30901 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30902 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30904 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30905 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30907 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30909 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30914 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30915 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30917 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30918 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30920 var targetWidth = this.minWidth - 2 * x;
30921 var targetHeight = this.minHeight - 2 * y;
30925 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30926 scale = targetWidth / width;
30929 if(x > 0 && y == 0){
30930 scale = targetHeight / height;
30933 if(x > 0 && y > 0){
30934 scale = targetWidth / width;
30936 if(width < height){
30937 scale = targetHeight / height;
30941 context.scale(scale, scale);
30943 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30944 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30946 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30947 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30949 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30950 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30952 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30957 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30958 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30960 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30961 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30963 var targetWidth = this.minWidth - 2 * x;
30964 var targetHeight = this.minHeight - 2 * y;
30968 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30969 scale = targetWidth / width;
30972 if(x > 0 && y == 0){
30973 scale = targetHeight / height;
30976 if(x > 0 && y > 0){
30977 scale = targetWidth / width;
30979 if(width < height){
30980 scale = targetHeight / height;
30984 context.scale(scale, scale);
30986 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30987 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30989 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30990 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30992 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30994 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31001 this.cropData = canvas.toDataURL(this.cropType);
31003 if(this.fireEvent('crop', this, this.cropData) !== false){
31004 this.process(this.file, this.cropData);
31011 setThumbBoxSize : function()
31015 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31016 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31017 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31019 this.minWidth = width;
31020 this.minHeight = height;
31022 if(this.rotate == 90 || this.rotate == 270){
31023 this.minWidth = height;
31024 this.minHeight = width;
31029 width = Math.ceil(this.minWidth * height / this.minHeight);
31031 if(this.minWidth > this.minHeight){
31033 height = Math.ceil(this.minHeight * width / this.minWidth);
31036 this.thumbEl.setStyle({
31037 width : width + 'px',
31038 height : height + 'px'
31045 setThumbBoxPosition : function()
31047 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31048 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31050 this.thumbEl.setLeft(x);
31051 this.thumbEl.setTop(y);
31055 baseRotateLevel : function()
31057 this.baseRotate = 1;
31060 typeof(this.exif) != 'undefined' &&
31061 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31062 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31064 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31067 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31071 baseScaleLevel : function()
31075 if(this.isDocument){
31077 if(this.baseRotate == 6 || this.baseRotate == 8){
31079 height = this.thumbEl.getHeight();
31080 this.baseScale = height / this.imageEl.OriginWidth;
31082 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31083 width = this.thumbEl.getWidth();
31084 this.baseScale = width / this.imageEl.OriginHeight;
31090 height = this.thumbEl.getHeight();
31091 this.baseScale = height / this.imageEl.OriginHeight;
31093 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31094 width = this.thumbEl.getWidth();
31095 this.baseScale = width / this.imageEl.OriginWidth;
31101 if(this.baseRotate == 6 || this.baseRotate == 8){
31103 width = this.thumbEl.getHeight();
31104 this.baseScale = width / this.imageEl.OriginHeight;
31106 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31107 height = this.thumbEl.getWidth();
31108 this.baseScale = height / this.imageEl.OriginHeight;
31111 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31112 height = this.thumbEl.getWidth();
31113 this.baseScale = height / this.imageEl.OriginHeight;
31115 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31116 width = this.thumbEl.getHeight();
31117 this.baseScale = width / this.imageEl.OriginWidth;
31124 width = this.thumbEl.getWidth();
31125 this.baseScale = width / this.imageEl.OriginWidth;
31127 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31128 height = this.thumbEl.getHeight();
31129 this.baseScale = height / this.imageEl.OriginHeight;
31132 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31134 height = this.thumbEl.getHeight();
31135 this.baseScale = height / this.imageEl.OriginHeight;
31137 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31138 width = this.thumbEl.getWidth();
31139 this.baseScale = width / this.imageEl.OriginWidth;
31147 getScaleLevel : function()
31149 return this.baseScale * Math.pow(1.1, this.scale);
31152 onTouchStart : function(e)
31154 if(!this.canvasLoaded){
31155 this.beforeSelectFile(e);
31159 var touches = e.browserEvent.touches;
31165 if(touches.length == 1){
31166 this.onMouseDown(e);
31170 if(touches.length != 2){
31176 for(var i = 0, finger; finger = touches[i]; i++){
31177 coords.push(finger.pageX, finger.pageY);
31180 var x = Math.pow(coords[0] - coords[2], 2);
31181 var y = Math.pow(coords[1] - coords[3], 2);
31183 this.startDistance = Math.sqrt(x + y);
31185 this.startScale = this.scale;
31187 this.pinching = true;
31188 this.dragable = false;
31192 onTouchMove : function(e)
31194 if(!this.pinching && !this.dragable){
31198 var touches = e.browserEvent.touches;
31205 this.onMouseMove(e);
31211 for(var i = 0, finger; finger = touches[i]; i++){
31212 coords.push(finger.pageX, finger.pageY);
31215 var x = Math.pow(coords[0] - coords[2], 2);
31216 var y = Math.pow(coords[1] - coords[3], 2);
31218 this.endDistance = Math.sqrt(x + y);
31220 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31222 if(!this.zoomable()){
31223 this.scale = this.startScale;
31231 onTouchEnd : function(e)
31233 this.pinching = false;
31234 this.dragable = false;
31238 process : function(file, crop)
31241 this.maskEl.mask(this.loadingText);
31244 this.xhr = new XMLHttpRequest();
31246 file.xhr = this.xhr;
31248 this.xhr.open(this.method, this.url, true);
31251 "Accept": "application/json",
31252 "Cache-Control": "no-cache",
31253 "X-Requested-With": "XMLHttpRequest"
31256 for (var headerName in headers) {
31257 var headerValue = headers[headerName];
31259 this.xhr.setRequestHeader(headerName, headerValue);
31265 this.xhr.onload = function()
31267 _this.xhrOnLoad(_this.xhr);
31270 this.xhr.onerror = function()
31272 _this.xhrOnError(_this.xhr);
31275 var formData = new FormData();
31277 formData.append('returnHTML', 'NO');
31280 formData.append('crop', crop);
31283 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31284 formData.append(this.paramName, file, file.name);
31287 if(typeof(file.filename) != 'undefined'){
31288 formData.append('filename', file.filename);
31291 if(typeof(file.mimetype) != 'undefined'){
31292 formData.append('mimetype', file.mimetype);
31295 if(this.fireEvent('arrange', this, formData) != false){
31296 this.xhr.send(formData);
31300 xhrOnLoad : function(xhr)
31303 this.maskEl.unmask();
31306 if (xhr.readyState !== 4) {
31307 this.fireEvent('exception', this, xhr);
31311 var response = Roo.decode(xhr.responseText);
31313 if(!response.success){
31314 this.fireEvent('exception', this, xhr);
31318 var response = Roo.decode(xhr.responseText);
31320 this.fireEvent('upload', this, response);
31324 xhrOnError : function()
31327 this.maskEl.unmask();
31330 Roo.log('xhr on error');
31332 var response = Roo.decode(xhr.responseText);
31338 prepare : function(file)
31341 this.maskEl.mask(this.loadingText);
31347 if(typeof(file) === 'string'){
31348 this.loadCanvas(file);
31352 if(!file || !this.urlAPI){
31357 this.cropType = file.type;
31361 if(this.fireEvent('prepare', this, this.file) != false){
31363 var reader = new FileReader();
31365 reader.onload = function (e) {
31366 if (e.target.error) {
31367 Roo.log(e.target.error);
31371 var buffer = e.target.result,
31372 dataView = new DataView(buffer),
31374 maxOffset = dataView.byteLength - 4,
31378 if (dataView.getUint16(0) === 0xffd8) {
31379 while (offset < maxOffset) {
31380 markerBytes = dataView.getUint16(offset);
31382 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31383 markerLength = dataView.getUint16(offset + 2) + 2;
31384 if (offset + markerLength > dataView.byteLength) {
31385 Roo.log('Invalid meta data: Invalid segment size.');
31389 if(markerBytes == 0xffe1){
31390 _this.parseExifData(
31397 offset += markerLength;
31407 var url = _this.urlAPI.createObjectURL(_this.file);
31409 _this.loadCanvas(url);
31414 reader.readAsArrayBuffer(this.file);
31420 parseExifData : function(dataView, offset, length)
31422 var tiffOffset = offset + 10,
31426 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31427 // No Exif data, might be XMP data instead
31431 // Check for the ASCII code for "Exif" (0x45786966):
31432 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31433 // No Exif data, might be XMP data instead
31436 if (tiffOffset + 8 > dataView.byteLength) {
31437 Roo.log('Invalid Exif data: Invalid segment size.');
31440 // Check for the two null bytes:
31441 if (dataView.getUint16(offset + 8) !== 0x0000) {
31442 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31445 // Check the byte alignment:
31446 switch (dataView.getUint16(tiffOffset)) {
31448 littleEndian = true;
31451 littleEndian = false;
31454 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31457 // Check for the TIFF tag marker (0x002A):
31458 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31459 Roo.log('Invalid Exif data: Missing TIFF marker.');
31462 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31463 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31465 this.parseExifTags(
31468 tiffOffset + dirOffset,
31473 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31478 if (dirOffset + 6 > dataView.byteLength) {
31479 Roo.log('Invalid Exif data: Invalid directory offset.');
31482 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31483 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31484 if (dirEndOffset + 4 > dataView.byteLength) {
31485 Roo.log('Invalid Exif data: Invalid directory size.');
31488 for (i = 0; i < tagsNumber; i += 1) {
31492 dirOffset + 2 + 12 * i, // tag offset
31496 // Return the offset to the next directory:
31497 return dataView.getUint32(dirEndOffset, littleEndian);
31500 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31502 var tag = dataView.getUint16(offset, littleEndian);
31504 this.exif[tag] = this.getExifValue(
31508 dataView.getUint16(offset + 2, littleEndian), // tag type
31509 dataView.getUint32(offset + 4, littleEndian), // tag length
31514 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31516 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31525 Roo.log('Invalid Exif data: Invalid tag type.');
31529 tagSize = tagType.size * length;
31530 // Determine if the value is contained in the dataOffset bytes,
31531 // or if the value at the dataOffset is a pointer to the actual data:
31532 dataOffset = tagSize > 4 ?
31533 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31534 if (dataOffset + tagSize > dataView.byteLength) {
31535 Roo.log('Invalid Exif data: Invalid data offset.');
31538 if (length === 1) {
31539 return tagType.getValue(dataView, dataOffset, littleEndian);
31542 for (i = 0; i < length; i += 1) {
31543 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31546 if (tagType.ascii) {
31548 // Concatenate the chars:
31549 for (i = 0; i < values.length; i += 1) {
31551 // Ignore the terminating NULL byte(s):
31552 if (c === '\u0000') {
31564 Roo.apply(Roo.bootstrap.UploadCropbox, {
31566 'Orientation': 0x0112
31570 1: 0, //'top-left',
31572 3: 180, //'bottom-right',
31573 // 4: 'bottom-left',
31575 6: 90, //'right-top',
31576 // 7: 'right-bottom',
31577 8: 270 //'left-bottom'
31581 // byte, 8-bit unsigned int:
31583 getValue: function (dataView, dataOffset) {
31584 return dataView.getUint8(dataOffset);
31588 // ascii, 8-bit byte:
31590 getValue: function (dataView, dataOffset) {
31591 return String.fromCharCode(dataView.getUint8(dataOffset));
31596 // short, 16 bit int:
31598 getValue: function (dataView, dataOffset, littleEndian) {
31599 return dataView.getUint16(dataOffset, littleEndian);
31603 // long, 32 bit int:
31605 getValue: function (dataView, dataOffset, littleEndian) {
31606 return dataView.getUint32(dataOffset, littleEndian);
31610 // rational = two long values, first is numerator, second is denominator:
31612 getValue: function (dataView, dataOffset, littleEndian) {
31613 return dataView.getUint32(dataOffset, littleEndian) /
31614 dataView.getUint32(dataOffset + 4, littleEndian);
31618 // slong, 32 bit signed int:
31620 getValue: function (dataView, dataOffset, littleEndian) {
31621 return dataView.getInt32(dataOffset, littleEndian);
31625 // srational, two slongs, first is numerator, second is denominator:
31627 getValue: function (dataView, dataOffset, littleEndian) {
31628 return dataView.getInt32(dataOffset, littleEndian) /
31629 dataView.getInt32(dataOffset + 4, littleEndian);
31639 cls : 'btn-group roo-upload-cropbox-rotate-left',
31640 action : 'rotate-left',
31644 cls : 'btn btn-default',
31645 html : '<i class="fa fa-undo"></i>'
31651 cls : 'btn-group roo-upload-cropbox-picture',
31652 action : 'picture',
31656 cls : 'btn btn-default',
31657 html : '<i class="fa fa-picture-o"></i>'
31663 cls : 'btn-group roo-upload-cropbox-rotate-right',
31664 action : 'rotate-right',
31668 cls : 'btn btn-default',
31669 html : '<i class="fa fa-repeat"></i>'
31677 cls : 'btn-group roo-upload-cropbox-rotate-left',
31678 action : 'rotate-left',
31682 cls : 'btn btn-default',
31683 html : '<i class="fa fa-undo"></i>'
31689 cls : 'btn-group roo-upload-cropbox-download',
31690 action : 'download',
31694 cls : 'btn btn-default',
31695 html : '<i class="fa fa-download"></i>'
31701 cls : 'btn-group roo-upload-cropbox-crop',
31706 cls : 'btn btn-default',
31707 html : '<i class="fa fa-crop"></i>'
31713 cls : 'btn-group roo-upload-cropbox-trash',
31718 cls : 'btn btn-default',
31719 html : '<i class="fa fa-trash"></i>'
31725 cls : 'btn-group roo-upload-cropbox-rotate-right',
31726 action : 'rotate-right',
31730 cls : 'btn btn-default',
31731 html : '<i class="fa fa-repeat"></i>'
31739 cls : 'btn-group roo-upload-cropbox-rotate-left',
31740 action : 'rotate-left',
31744 cls : 'btn btn-default',
31745 html : '<i class="fa fa-undo"></i>'
31751 cls : 'btn-group roo-upload-cropbox-rotate-right',
31752 action : 'rotate-right',
31756 cls : 'btn btn-default',
31757 html : '<i class="fa fa-repeat"></i>'
31770 * @class Roo.bootstrap.DocumentManager
31771 * @extends Roo.bootstrap.Component
31772 * Bootstrap DocumentManager class
31773 * @cfg {String} paramName default 'imageUpload'
31774 * @cfg {String} toolTipName default 'filename'
31775 * @cfg {String} method default POST
31776 * @cfg {String} url action url
31777 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31778 * @cfg {Boolean} multiple multiple upload default true
31779 * @cfg {Number} thumbSize default 300
31780 * @cfg {String} fieldLabel
31781 * @cfg {Number} labelWidth default 4
31782 * @cfg {String} labelAlign (left|top) default left
31783 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31784 * @cfg {Number} labellg set the width of label (1-12)
31785 * @cfg {Number} labelmd set the width of label (1-12)
31786 * @cfg {Number} labelsm set the width of label (1-12)
31787 * @cfg {Number} labelxs set the width of label (1-12)
31790 * Create a new DocumentManager
31791 * @param {Object} config The config object
31794 Roo.bootstrap.DocumentManager = function(config){
31795 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31798 this.delegates = [];
31803 * Fire when initial the DocumentManager
31804 * @param {Roo.bootstrap.DocumentManager} this
31809 * inspect selected file
31810 * @param {Roo.bootstrap.DocumentManager} this
31811 * @param {File} file
31816 * Fire when xhr load exception
31817 * @param {Roo.bootstrap.DocumentManager} this
31818 * @param {XMLHttpRequest} xhr
31820 "exception" : true,
31822 * @event afterupload
31823 * Fire when xhr load exception
31824 * @param {Roo.bootstrap.DocumentManager} this
31825 * @param {XMLHttpRequest} xhr
31827 "afterupload" : true,
31830 * prepare the form data
31831 * @param {Roo.bootstrap.DocumentManager} this
31832 * @param {Object} formData
31837 * Fire when remove the file
31838 * @param {Roo.bootstrap.DocumentManager} this
31839 * @param {Object} file
31844 * Fire after refresh the file
31845 * @param {Roo.bootstrap.DocumentManager} this
31850 * Fire after click the image
31851 * @param {Roo.bootstrap.DocumentManager} this
31852 * @param {Object} file
31857 * Fire when upload a image and editable set to true
31858 * @param {Roo.bootstrap.DocumentManager} this
31859 * @param {Object} file
31863 * @event beforeselectfile
31864 * Fire before select file
31865 * @param {Roo.bootstrap.DocumentManager} this
31867 "beforeselectfile" : true,
31870 * Fire before process file
31871 * @param {Roo.bootstrap.DocumentManager} this
31872 * @param {Object} file
31876 * @event previewrendered
31877 * Fire when preview rendered
31878 * @param {Roo.bootstrap.DocumentManager} this
31879 * @param {Object} file
31881 "previewrendered" : true,
31884 "previewResize" : true
31889 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31898 paramName : 'imageUpload',
31899 toolTipName : 'filename',
31902 labelAlign : 'left',
31912 getAutoCreate : function()
31914 var managerWidget = {
31916 cls : 'roo-document-manager',
31920 cls : 'roo-document-manager-selector',
31925 cls : 'roo-document-manager-uploader',
31929 cls : 'roo-document-manager-upload-btn',
31930 html : '<i class="fa fa-plus"></i>'
31941 cls : 'column col-md-12',
31946 if(this.fieldLabel.length){
31951 cls : 'column col-md-12',
31952 html : this.fieldLabel
31956 cls : 'column col-md-12',
31961 if(this.labelAlign == 'left'){
31966 html : this.fieldLabel
31975 if(this.labelWidth > 12){
31976 content[0].style = "width: " + this.labelWidth + 'px';
31979 if(this.labelWidth < 13 && this.labelmd == 0){
31980 this.labelmd = this.labelWidth;
31983 if(this.labellg > 0){
31984 content[0].cls += ' col-lg-' + this.labellg;
31985 content[1].cls += ' col-lg-' + (12 - this.labellg);
31988 if(this.labelmd > 0){
31989 content[0].cls += ' col-md-' + this.labelmd;
31990 content[1].cls += ' col-md-' + (12 - this.labelmd);
31993 if(this.labelsm > 0){
31994 content[0].cls += ' col-sm-' + this.labelsm;
31995 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31998 if(this.labelxs > 0){
31999 content[0].cls += ' col-xs-' + this.labelxs;
32000 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32008 cls : 'row clearfix',
32016 initEvents : function()
32018 this.managerEl = this.el.select('.roo-document-manager', true).first();
32019 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32021 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32022 this.selectorEl.hide();
32025 this.selectorEl.attr('multiple', 'multiple');
32028 this.selectorEl.on('change', this.onFileSelected, this);
32030 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32031 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32033 this.uploader.on('click', this.onUploaderClick, this);
32035 this.renderProgressDialog();
32039 window.addEventListener("resize", function() { _this.refresh(); } );
32041 this.fireEvent('initial', this);
32044 renderProgressDialog : function()
32048 this.progressDialog = new Roo.bootstrap.Modal({
32049 cls : 'roo-document-manager-progress-dialog',
32050 allow_close : false,
32061 btnclick : function() {
32062 _this.uploadCancel();
32068 this.progressDialog.render(Roo.get(document.body));
32070 this.progress = new Roo.bootstrap.Progress({
32071 cls : 'roo-document-manager-progress',
32076 this.progress.render(this.progressDialog.getChildContainer());
32078 this.progressBar = new Roo.bootstrap.ProgressBar({
32079 cls : 'roo-document-manager-progress-bar',
32082 aria_valuemax : 12,
32086 this.progressBar.render(this.progress.getChildContainer());
32089 onUploaderClick : function(e)
32091 e.preventDefault();
32093 if(this.fireEvent('beforeselectfile', this) != false){
32094 this.selectorEl.dom.click();
32099 onFileSelected : function(e)
32101 e.preventDefault();
32103 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32107 Roo.each(this.selectorEl.dom.files, function(file){
32108 if(this.fireEvent('inspect', this, file) != false){
32109 this.files.push(file);
32119 this.selectorEl.dom.value = '';
32121 if(!this.files || !this.files.length){
32125 if(this.boxes > 0 && this.files.length > this.boxes){
32126 this.files = this.files.slice(0, this.boxes);
32129 this.uploader.show();
32131 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32132 this.uploader.hide();
32141 Roo.each(this.files, function(file){
32143 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32144 var f = this.renderPreview(file);
32149 if(file.type.indexOf('image') != -1){
32150 this.delegates.push(
32152 _this.process(file);
32153 }).createDelegate(this)
32161 _this.process(file);
32162 }).createDelegate(this)
32167 this.files = files;
32169 this.delegates = this.delegates.concat(docs);
32171 if(!this.delegates.length){
32176 this.progressBar.aria_valuemax = this.delegates.length;
32183 arrange : function()
32185 if(!this.delegates.length){
32186 this.progressDialog.hide();
32191 var delegate = this.delegates.shift();
32193 this.progressDialog.show();
32195 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32197 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32202 refresh : function()
32204 this.uploader.show();
32206 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32207 this.uploader.hide();
32210 Roo.isTouch ? this.closable(false) : this.closable(true);
32212 this.fireEvent('refresh', this);
32215 onRemove : function(e, el, o)
32217 e.preventDefault();
32219 this.fireEvent('remove', this, o);
32223 remove : function(o)
32227 Roo.each(this.files, function(file){
32228 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32237 this.files = files;
32244 Roo.each(this.files, function(file){
32249 file.target.remove();
32258 onClick : function(e, el, o)
32260 e.preventDefault();
32262 this.fireEvent('click', this, o);
32266 closable : function(closable)
32268 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32270 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32282 xhrOnLoad : function(xhr)
32284 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32288 if (xhr.readyState !== 4) {
32290 this.fireEvent('exception', this, xhr);
32294 var response = Roo.decode(xhr.responseText);
32296 if(!response.success){
32298 this.fireEvent('exception', this, xhr);
32302 var file = this.renderPreview(response.data);
32304 this.files.push(file);
32308 this.fireEvent('afterupload', this, xhr);
32312 xhrOnError : function(xhr)
32314 Roo.log('xhr on error');
32316 var response = Roo.decode(xhr.responseText);
32323 process : function(file)
32325 if(this.fireEvent('process', this, file) !== false){
32326 if(this.editable && file.type.indexOf('image') != -1){
32327 this.fireEvent('edit', this, file);
32331 this.uploadStart(file, false);
32338 uploadStart : function(file, crop)
32340 this.xhr = new XMLHttpRequest();
32342 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32347 file.xhr = this.xhr;
32349 this.managerEl.createChild({
32351 cls : 'roo-document-manager-loading',
32355 tooltip : file.name,
32356 cls : 'roo-document-manager-thumb',
32357 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32363 this.xhr.open(this.method, this.url, true);
32366 "Accept": "application/json",
32367 "Cache-Control": "no-cache",
32368 "X-Requested-With": "XMLHttpRequest"
32371 for (var headerName in headers) {
32372 var headerValue = headers[headerName];
32374 this.xhr.setRequestHeader(headerName, headerValue);
32380 this.xhr.onload = function()
32382 _this.xhrOnLoad(_this.xhr);
32385 this.xhr.onerror = function()
32387 _this.xhrOnError(_this.xhr);
32390 var formData = new FormData();
32392 formData.append('returnHTML', 'NO');
32395 formData.append('crop', crop);
32398 formData.append(this.paramName, file, file.name);
32405 if(this.fireEvent('prepare', this, formData, options) != false){
32407 if(options.manually){
32411 this.xhr.send(formData);
32415 this.uploadCancel();
32418 uploadCancel : function()
32424 this.delegates = [];
32426 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32433 renderPreview : function(file)
32435 if(typeof(file.target) != 'undefined' && file.target){
32439 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32441 var previewEl = this.managerEl.createChild({
32443 cls : 'roo-document-manager-preview',
32447 tooltip : file[this.toolTipName],
32448 cls : 'roo-document-manager-thumb',
32449 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32454 html : '<i class="fa fa-times-circle"></i>'
32459 var close = previewEl.select('button.close', true).first();
32461 close.on('click', this.onRemove, this, file);
32463 file.target = previewEl;
32465 var image = previewEl.select('img', true).first();
32469 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32471 image.on('click', this.onClick, this, file);
32473 this.fireEvent('previewrendered', this, file);
32479 onPreviewLoad : function(file, image)
32481 if(typeof(file.target) == 'undefined' || !file.target){
32485 var width = image.dom.naturalWidth || image.dom.width;
32486 var height = image.dom.naturalHeight || image.dom.height;
32488 if(!this.previewResize) {
32492 if(width > height){
32493 file.target.addClass('wide');
32497 file.target.addClass('tall');
32502 uploadFromSource : function(file, crop)
32504 this.xhr = new XMLHttpRequest();
32506 this.managerEl.createChild({
32508 cls : 'roo-document-manager-loading',
32512 tooltip : file.name,
32513 cls : 'roo-document-manager-thumb',
32514 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32520 this.xhr.open(this.method, this.url, true);
32523 "Accept": "application/json",
32524 "Cache-Control": "no-cache",
32525 "X-Requested-With": "XMLHttpRequest"
32528 for (var headerName in headers) {
32529 var headerValue = headers[headerName];
32531 this.xhr.setRequestHeader(headerName, headerValue);
32537 this.xhr.onload = function()
32539 _this.xhrOnLoad(_this.xhr);
32542 this.xhr.onerror = function()
32544 _this.xhrOnError(_this.xhr);
32547 var formData = new FormData();
32549 formData.append('returnHTML', 'NO');
32551 formData.append('crop', crop);
32553 if(typeof(file.filename) != 'undefined'){
32554 formData.append('filename', file.filename);
32557 if(typeof(file.mimetype) != 'undefined'){
32558 formData.append('mimetype', file.mimetype);
32563 if(this.fireEvent('prepare', this, formData) != false){
32564 this.xhr.send(formData);
32574 * @class Roo.bootstrap.DocumentViewer
32575 * @extends Roo.bootstrap.Component
32576 * Bootstrap DocumentViewer class
32577 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32578 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32581 * Create a new DocumentViewer
32582 * @param {Object} config The config object
32585 Roo.bootstrap.DocumentViewer = function(config){
32586 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32591 * Fire after initEvent
32592 * @param {Roo.bootstrap.DocumentViewer} this
32598 * @param {Roo.bootstrap.DocumentViewer} this
32603 * Fire after download button
32604 * @param {Roo.bootstrap.DocumentViewer} this
32609 * Fire after trash button
32610 * @param {Roo.bootstrap.DocumentViewer} this
32617 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32619 showDownload : true,
32623 getAutoCreate : function()
32627 cls : 'roo-document-viewer',
32631 cls : 'roo-document-viewer-body',
32635 cls : 'roo-document-viewer-thumb',
32639 cls : 'roo-document-viewer-image'
32647 cls : 'roo-document-viewer-footer',
32650 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32654 cls : 'btn-group roo-document-viewer-download',
32658 cls : 'btn btn-default',
32659 html : '<i class="fa fa-download"></i>'
32665 cls : 'btn-group roo-document-viewer-trash',
32669 cls : 'btn btn-default',
32670 html : '<i class="fa fa-trash"></i>'
32683 initEvents : function()
32685 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32686 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32688 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32689 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32691 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32692 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32694 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32695 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32697 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32698 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32700 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32701 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32703 this.bodyEl.on('click', this.onClick, this);
32704 this.downloadBtn.on('click', this.onDownload, this);
32705 this.trashBtn.on('click', this.onTrash, this);
32707 this.downloadBtn.hide();
32708 this.trashBtn.hide();
32710 if(this.showDownload){
32711 this.downloadBtn.show();
32714 if(this.showTrash){
32715 this.trashBtn.show();
32718 if(!this.showDownload && !this.showTrash) {
32719 this.footerEl.hide();
32724 initial : function()
32726 this.fireEvent('initial', this);
32730 onClick : function(e)
32732 e.preventDefault();
32734 this.fireEvent('click', this);
32737 onDownload : function(e)
32739 e.preventDefault();
32741 this.fireEvent('download', this);
32744 onTrash : function(e)
32746 e.preventDefault();
32748 this.fireEvent('trash', this);
32760 * @class Roo.bootstrap.NavProgressBar
32761 * @extends Roo.bootstrap.Component
32762 * Bootstrap NavProgressBar class
32765 * Create a new nav progress bar
32766 * @param {Object} config The config object
32769 Roo.bootstrap.NavProgressBar = function(config){
32770 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32772 this.bullets = this.bullets || [];
32774 // Roo.bootstrap.NavProgressBar.register(this);
32778 * Fires when the active item changes
32779 * @param {Roo.bootstrap.NavProgressBar} this
32780 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32781 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32788 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32793 getAutoCreate : function()
32795 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32799 cls : 'roo-navigation-bar-group',
32803 cls : 'roo-navigation-top-bar'
32807 cls : 'roo-navigation-bullets-bar',
32811 cls : 'roo-navigation-bar'
32818 cls : 'roo-navigation-bottom-bar'
32828 initEvents: function()
32833 onRender : function(ct, position)
32835 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32837 if(this.bullets.length){
32838 Roo.each(this.bullets, function(b){
32847 addItem : function(cfg)
32849 var item = new Roo.bootstrap.NavProgressItem(cfg);
32851 item.parentId = this.id;
32852 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32855 var top = new Roo.bootstrap.Element({
32857 cls : 'roo-navigation-bar-text'
32860 var bottom = new Roo.bootstrap.Element({
32862 cls : 'roo-navigation-bar-text'
32865 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32866 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32868 var topText = new Roo.bootstrap.Element({
32870 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32873 var bottomText = new Roo.bootstrap.Element({
32875 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32878 topText.onRender(top.el, null);
32879 bottomText.onRender(bottom.el, null);
32882 item.bottomEl = bottom;
32885 this.barItems.push(item);
32890 getActive : function()
32892 var active = false;
32894 Roo.each(this.barItems, function(v){
32896 if (!v.isActive()) {
32908 setActiveItem : function(item)
32912 Roo.each(this.barItems, function(v){
32913 if (v.rid == item.rid) {
32917 if (v.isActive()) {
32918 v.setActive(false);
32923 item.setActive(true);
32925 this.fireEvent('changed', this, item, prev);
32928 getBarItem: function(rid)
32932 Roo.each(this.barItems, function(e) {
32933 if (e.rid != rid) {
32944 indexOfItem : function(item)
32948 Roo.each(this.barItems, function(v, i){
32950 if (v.rid != item.rid) {
32961 setActiveNext : function()
32963 var i = this.indexOfItem(this.getActive());
32965 if (i > this.barItems.length) {
32969 this.setActiveItem(this.barItems[i+1]);
32972 setActivePrev : function()
32974 var i = this.indexOfItem(this.getActive());
32980 this.setActiveItem(this.barItems[i-1]);
32983 format : function()
32985 if(!this.barItems.length){
32989 var width = 100 / this.barItems.length;
32991 Roo.each(this.barItems, function(i){
32992 i.el.setStyle('width', width + '%');
32993 i.topEl.el.setStyle('width', width + '%');
32994 i.bottomEl.el.setStyle('width', width + '%');
33003 * Nav Progress Item
33008 * @class Roo.bootstrap.NavProgressItem
33009 * @extends Roo.bootstrap.Component
33010 * Bootstrap NavProgressItem class
33011 * @cfg {String} rid the reference id
33012 * @cfg {Boolean} active (true|false) Is item active default false
33013 * @cfg {Boolean} disabled (true|false) Is item active default false
33014 * @cfg {String} html
33015 * @cfg {String} position (top|bottom) text position default bottom
33016 * @cfg {String} icon show icon instead of number
33019 * Create a new NavProgressItem
33020 * @param {Object} config The config object
33022 Roo.bootstrap.NavProgressItem = function(config){
33023 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33028 * The raw click event for the entire grid.
33029 * @param {Roo.bootstrap.NavProgressItem} this
33030 * @param {Roo.EventObject} e
33037 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33043 position : 'bottom',
33046 getAutoCreate : function()
33048 var iconCls = 'roo-navigation-bar-item-icon';
33050 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33054 cls: 'roo-navigation-bar-item',
33064 cfg.cls += ' active';
33067 cfg.cls += ' disabled';
33073 disable : function()
33075 this.setDisabled(true);
33078 enable : function()
33080 this.setDisabled(false);
33083 initEvents: function()
33085 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33087 this.iconEl.on('click', this.onClick, this);
33090 onClick : function(e)
33092 e.preventDefault();
33098 if(this.fireEvent('click', this, e) === false){
33102 this.parent().setActiveItem(this);
33105 isActive: function ()
33107 return this.active;
33110 setActive : function(state)
33112 if(this.active == state){
33116 this.active = state;
33119 this.el.addClass('active');
33123 this.el.removeClass('active');
33128 setDisabled : function(state)
33130 if(this.disabled == state){
33134 this.disabled = state;
33137 this.el.addClass('disabled');
33141 this.el.removeClass('disabled');
33144 tooltipEl : function()
33146 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33159 * @class Roo.bootstrap.FieldLabel
33160 * @extends Roo.bootstrap.Component
33161 * Bootstrap FieldLabel class
33162 * @cfg {String} html contents of the element
33163 * @cfg {String} tag tag of the element default label
33164 * @cfg {String} cls class of the element
33165 * @cfg {String} target label target
33166 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33167 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33168 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33169 * @cfg {String} iconTooltip default "This field is required"
33170 * @cfg {String} indicatorpos (left|right) default left
33173 * Create a new FieldLabel
33174 * @param {Object} config The config object
33177 Roo.bootstrap.FieldLabel = function(config){
33178 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33183 * Fires after the field has been marked as invalid.
33184 * @param {Roo.form.FieldLabel} this
33185 * @param {String} msg The validation message
33190 * Fires after the field has been validated with no errors.
33191 * @param {Roo.form.FieldLabel} this
33197 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33204 invalidClass : 'has-warning',
33205 validClass : 'has-success',
33206 iconTooltip : 'This field is required',
33207 indicatorpos : 'left',
33209 getAutoCreate : function(){
33212 if (!this.allowBlank) {
33218 cls : 'roo-bootstrap-field-label ' + this.cls,
33223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33224 tooltip : this.iconTooltip
33233 if(this.indicatorpos == 'right'){
33236 cls : 'roo-bootstrap-field-label ' + this.cls,
33245 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33246 tooltip : this.iconTooltip
33255 initEvents: function()
33257 Roo.bootstrap.Element.superclass.initEvents.call(this);
33259 this.indicator = this.indicatorEl();
33261 if(this.indicator){
33262 this.indicator.removeClass('visible');
33263 this.indicator.addClass('invisible');
33266 Roo.bootstrap.FieldLabel.register(this);
33269 indicatorEl : function()
33271 var indicator = this.el.select('i.roo-required-indicator',true).first();
33282 * Mark this field as valid
33284 markValid : function()
33286 if(this.indicator){
33287 this.indicator.removeClass('visible');
33288 this.indicator.addClass('invisible');
33290 if (Roo.bootstrap.version == 3) {
33291 this.el.removeClass(this.invalidClass);
33292 this.el.addClass(this.validClass);
33294 this.el.removeClass('is-invalid');
33295 this.el.addClass('is-valid');
33299 this.fireEvent('valid', this);
33303 * Mark this field as invalid
33304 * @param {String} msg The validation message
33306 markInvalid : function(msg)
33308 if(this.indicator){
33309 this.indicator.removeClass('invisible');
33310 this.indicator.addClass('visible');
33312 if (Roo.bootstrap.version == 3) {
33313 this.el.removeClass(this.validClass);
33314 this.el.addClass(this.invalidClass);
33316 this.el.removeClass('is-valid');
33317 this.el.addClass('is-invalid');
33321 this.fireEvent('invalid', this, msg);
33327 Roo.apply(Roo.bootstrap.FieldLabel, {
33332 * register a FieldLabel Group
33333 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33335 register : function(label)
33337 if(this.groups.hasOwnProperty(label.target)){
33341 this.groups[label.target] = label;
33345 * fetch a FieldLabel Group based on the target
33346 * @param {string} target
33347 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33349 get: function(target) {
33350 if (typeof(this.groups[target]) == 'undefined') {
33354 return this.groups[target] ;
33363 * page DateSplitField.
33369 * @class Roo.bootstrap.DateSplitField
33370 * @extends Roo.bootstrap.Component
33371 * Bootstrap DateSplitField class
33372 * @cfg {string} fieldLabel - the label associated
33373 * @cfg {Number} labelWidth set the width of label (0-12)
33374 * @cfg {String} labelAlign (top|left)
33375 * @cfg {Boolean} dayAllowBlank (true|false) default false
33376 * @cfg {Boolean} monthAllowBlank (true|false) default false
33377 * @cfg {Boolean} yearAllowBlank (true|false) default false
33378 * @cfg {string} dayPlaceholder
33379 * @cfg {string} monthPlaceholder
33380 * @cfg {string} yearPlaceholder
33381 * @cfg {string} dayFormat default 'd'
33382 * @cfg {string} monthFormat default 'm'
33383 * @cfg {string} yearFormat default 'Y'
33384 * @cfg {Number} labellg set the width of label (1-12)
33385 * @cfg {Number} labelmd set the width of label (1-12)
33386 * @cfg {Number} labelsm set the width of label (1-12)
33387 * @cfg {Number} labelxs set the width of label (1-12)
33391 * Create a new DateSplitField
33392 * @param {Object} config The config object
33395 Roo.bootstrap.DateSplitField = function(config){
33396 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33402 * getting the data of years
33403 * @param {Roo.bootstrap.DateSplitField} this
33404 * @param {Object} years
33409 * getting the data of days
33410 * @param {Roo.bootstrap.DateSplitField} this
33411 * @param {Object} days
33416 * Fires after the field has been marked as invalid.
33417 * @param {Roo.form.Field} this
33418 * @param {String} msg The validation message
33423 * Fires after the field has been validated with no errors.
33424 * @param {Roo.form.Field} this
33430 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33433 labelAlign : 'top',
33435 dayAllowBlank : false,
33436 monthAllowBlank : false,
33437 yearAllowBlank : false,
33438 dayPlaceholder : '',
33439 monthPlaceholder : '',
33440 yearPlaceholder : '',
33444 isFormField : true,
33450 getAutoCreate : function()
33454 cls : 'row roo-date-split-field-group',
33459 cls : 'form-hidden-field roo-date-split-field-group-value',
33465 var labelCls = 'col-md-12';
33466 var contentCls = 'col-md-4';
33468 if(this.fieldLabel){
33472 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33476 html : this.fieldLabel
33481 if(this.labelAlign == 'left'){
33483 if(this.labelWidth > 12){
33484 label.style = "width: " + this.labelWidth + 'px';
33487 if(this.labelWidth < 13 && this.labelmd == 0){
33488 this.labelmd = this.labelWidth;
33491 if(this.labellg > 0){
33492 labelCls = ' col-lg-' + this.labellg;
33493 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33496 if(this.labelmd > 0){
33497 labelCls = ' col-md-' + this.labelmd;
33498 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33501 if(this.labelsm > 0){
33502 labelCls = ' col-sm-' + this.labelsm;
33503 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33506 if(this.labelxs > 0){
33507 labelCls = ' col-xs-' + this.labelxs;
33508 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33512 label.cls += ' ' + labelCls;
33514 cfg.cn.push(label);
33517 Roo.each(['day', 'month', 'year'], function(t){
33520 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33527 inputEl: function ()
33529 return this.el.select('.roo-date-split-field-group-value', true).first();
33532 onRender : function(ct, position)
33536 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33538 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33540 this.dayField = new Roo.bootstrap.ComboBox({
33541 allowBlank : this.dayAllowBlank,
33542 alwaysQuery : true,
33543 displayField : 'value',
33546 forceSelection : true,
33548 placeholder : this.dayPlaceholder,
33549 selectOnFocus : true,
33550 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33551 triggerAction : 'all',
33553 valueField : 'value',
33554 store : new Roo.data.SimpleStore({
33555 data : (function() {
33557 _this.fireEvent('days', _this, days);
33560 fields : [ 'value' ]
33563 select : function (_self, record, index)
33565 _this.setValue(_this.getValue());
33570 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33572 this.monthField = new Roo.bootstrap.MonthField({
33573 after : '<i class=\"fa fa-calendar\"></i>',
33574 allowBlank : this.monthAllowBlank,
33575 placeholder : this.monthPlaceholder,
33578 render : function (_self)
33580 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33581 e.preventDefault();
33585 select : function (_self, oldvalue, newvalue)
33587 _this.setValue(_this.getValue());
33592 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33594 this.yearField = new Roo.bootstrap.ComboBox({
33595 allowBlank : this.yearAllowBlank,
33596 alwaysQuery : true,
33597 displayField : 'value',
33600 forceSelection : true,
33602 placeholder : this.yearPlaceholder,
33603 selectOnFocus : true,
33604 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33605 triggerAction : 'all',
33607 valueField : 'value',
33608 store : new Roo.data.SimpleStore({
33609 data : (function() {
33611 _this.fireEvent('years', _this, years);
33614 fields : [ 'value' ]
33617 select : function (_self, record, index)
33619 _this.setValue(_this.getValue());
33624 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33627 setValue : function(v, format)
33629 this.inputEl.dom.value = v;
33631 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33633 var d = Date.parseDate(v, f);
33640 this.setDay(d.format(this.dayFormat));
33641 this.setMonth(d.format(this.monthFormat));
33642 this.setYear(d.format(this.yearFormat));
33649 setDay : function(v)
33651 this.dayField.setValue(v);
33652 this.inputEl.dom.value = this.getValue();
33657 setMonth : function(v)
33659 this.monthField.setValue(v, true);
33660 this.inputEl.dom.value = this.getValue();
33665 setYear : function(v)
33667 this.yearField.setValue(v);
33668 this.inputEl.dom.value = this.getValue();
33673 getDay : function()
33675 return this.dayField.getValue();
33678 getMonth : function()
33680 return this.monthField.getValue();
33683 getYear : function()
33685 return this.yearField.getValue();
33688 getValue : function()
33690 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33692 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33702 this.inputEl.dom.value = '';
33707 validate : function()
33709 var d = this.dayField.validate();
33710 var m = this.monthField.validate();
33711 var y = this.yearField.validate();
33716 (!this.dayAllowBlank && !d) ||
33717 (!this.monthAllowBlank && !m) ||
33718 (!this.yearAllowBlank && !y)
33723 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33732 this.markInvalid();
33737 markValid : function()
33740 var label = this.el.select('label', true).first();
33741 var icon = this.el.select('i.fa-star', true).first();
33747 this.fireEvent('valid', this);
33751 * Mark this field as invalid
33752 * @param {String} msg The validation message
33754 markInvalid : function(msg)
33757 var label = this.el.select('label', true).first();
33758 var icon = this.el.select('i.fa-star', true).first();
33760 if(label && !icon){
33761 this.el.select('.roo-date-split-field-label', true).createChild({
33763 cls : 'text-danger fa fa-lg fa-star',
33764 tooltip : 'This field is required',
33765 style : 'margin-right:5px;'
33769 this.fireEvent('invalid', this, msg);
33772 clearInvalid : function()
33774 var label = this.el.select('label', true).first();
33775 var icon = this.el.select('i.fa-star', true).first();
33781 this.fireEvent('valid', this);
33784 getName: function()
33794 * http://masonry.desandro.com
33796 * The idea is to render all the bricks based on vertical width...
33798 * The original code extends 'outlayer' - we might need to use that....
33804 * @class Roo.bootstrap.LayoutMasonry
33805 * @extends Roo.bootstrap.Component
33806 * Bootstrap Layout Masonry class
33809 * Create a new Element
33810 * @param {Object} config The config object
33813 Roo.bootstrap.LayoutMasonry = function(config){
33815 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33819 Roo.bootstrap.LayoutMasonry.register(this);
33825 * Fire after layout the items
33826 * @param {Roo.bootstrap.LayoutMasonry} this
33827 * @param {Roo.EventObject} e
33834 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33837 * @cfg {Boolean} isLayoutInstant = no animation?
33839 isLayoutInstant : false, // needed?
33842 * @cfg {Number} boxWidth width of the columns
33847 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33852 * @cfg {Number} padWidth padding below box..
33857 * @cfg {Number} gutter gutter width..
33862 * @cfg {Number} maxCols maximum number of columns
33868 * @cfg {Boolean} isAutoInitial defalut true
33870 isAutoInitial : true,
33875 * @cfg {Boolean} isHorizontal defalut false
33877 isHorizontal : false,
33879 currentSize : null,
33885 bricks: null, //CompositeElement
33889 _isLayoutInited : false,
33891 // isAlternative : false, // only use for vertical layout...
33894 * @cfg {Number} alternativePadWidth padding below box..
33896 alternativePadWidth : 50,
33898 selectedBrick : [],
33900 getAutoCreate : function(){
33902 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33906 cls: 'blog-masonary-wrapper ' + this.cls,
33908 cls : 'mas-boxes masonary'
33915 getChildContainer: function( )
33917 if (this.boxesEl) {
33918 return this.boxesEl;
33921 this.boxesEl = this.el.select('.mas-boxes').first();
33923 return this.boxesEl;
33927 initEvents : function()
33931 if(this.isAutoInitial){
33932 Roo.log('hook children rendered');
33933 this.on('childrenrendered', function() {
33934 Roo.log('children rendered');
33940 initial : function()
33942 this.selectedBrick = [];
33944 this.currentSize = this.el.getBox(true);
33946 Roo.EventManager.onWindowResize(this.resize, this);
33948 if(!this.isAutoInitial){
33956 //this.layout.defer(500,this);
33960 resize : function()
33962 var cs = this.el.getBox(true);
33965 this.currentSize.width == cs.width &&
33966 this.currentSize.x == cs.x &&
33967 this.currentSize.height == cs.height &&
33968 this.currentSize.y == cs.y
33970 Roo.log("no change in with or X or Y");
33974 this.currentSize = cs;
33980 layout : function()
33982 this._resetLayout();
33984 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33986 this.layoutItems( isInstant );
33988 this._isLayoutInited = true;
33990 this.fireEvent('layout', this);
33994 _resetLayout : function()
33996 if(this.isHorizontal){
33997 this.horizontalMeasureColumns();
34001 this.verticalMeasureColumns();
34005 verticalMeasureColumns : function()
34007 this.getContainerWidth();
34009 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34010 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34014 var boxWidth = this.boxWidth + this.padWidth;
34016 if(this.containerWidth < this.boxWidth){
34017 boxWidth = this.containerWidth
34020 var containerWidth = this.containerWidth;
34022 var cols = Math.floor(containerWidth / boxWidth);
34024 this.cols = Math.max( cols, 1 );
34026 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34028 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34030 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34032 this.colWidth = boxWidth + avail - this.padWidth;
34034 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34035 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34038 horizontalMeasureColumns : function()
34040 this.getContainerWidth();
34042 var boxWidth = this.boxWidth;
34044 if(this.containerWidth < boxWidth){
34045 boxWidth = this.containerWidth;
34048 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34050 this.el.setHeight(boxWidth);
34054 getContainerWidth : function()
34056 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34059 layoutItems : function( isInstant )
34061 Roo.log(this.bricks);
34063 var items = Roo.apply([], this.bricks);
34065 if(this.isHorizontal){
34066 this._horizontalLayoutItems( items , isInstant );
34070 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34071 // this._verticalAlternativeLayoutItems( items , isInstant );
34075 this._verticalLayoutItems( items , isInstant );
34079 _verticalLayoutItems : function ( items , isInstant)
34081 if ( !items || !items.length ) {
34086 ['xs', 'xs', 'xs', 'tall'],
34087 ['xs', 'xs', 'tall'],
34088 ['xs', 'xs', 'sm'],
34089 ['xs', 'xs', 'xs'],
34095 ['sm', 'xs', 'xs'],
34099 ['tall', 'xs', 'xs', 'xs'],
34100 ['tall', 'xs', 'xs'],
34112 Roo.each(items, function(item, k){
34114 switch (item.size) {
34115 // these layouts take up a full box,
34126 boxes.push([item]);
34149 var filterPattern = function(box, length)
34157 var pattern = box.slice(0, length);
34161 Roo.each(pattern, function(i){
34162 format.push(i.size);
34165 Roo.each(standard, function(s){
34167 if(String(s) != String(format)){
34176 if(!match && length == 1){
34181 filterPattern(box, length - 1);
34185 queue.push(pattern);
34187 box = box.slice(length, box.length);
34189 filterPattern(box, 4);
34195 Roo.each(boxes, function(box, k){
34201 if(box.length == 1){
34206 filterPattern(box, 4);
34210 this._processVerticalLayoutQueue( queue, isInstant );
34214 // _verticalAlternativeLayoutItems : function( items , isInstant )
34216 // if ( !items || !items.length ) {
34220 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34224 _horizontalLayoutItems : function ( items , isInstant)
34226 if ( !items || !items.length || items.length < 3) {
34232 var eItems = items.slice(0, 3);
34234 items = items.slice(3, items.length);
34237 ['xs', 'xs', 'xs', 'wide'],
34238 ['xs', 'xs', 'wide'],
34239 ['xs', 'xs', 'sm'],
34240 ['xs', 'xs', 'xs'],
34246 ['sm', 'xs', 'xs'],
34250 ['wide', 'xs', 'xs', 'xs'],
34251 ['wide', 'xs', 'xs'],
34264 Roo.each(items, function(item, k){
34266 switch (item.size) {
34277 boxes.push([item]);
34301 var filterPattern = function(box, length)
34309 var pattern = box.slice(0, length);
34313 Roo.each(pattern, function(i){
34314 format.push(i.size);
34317 Roo.each(standard, function(s){
34319 if(String(s) != String(format)){
34328 if(!match && length == 1){
34333 filterPattern(box, length - 1);
34337 queue.push(pattern);
34339 box = box.slice(length, box.length);
34341 filterPattern(box, 4);
34347 Roo.each(boxes, function(box, k){
34353 if(box.length == 1){
34358 filterPattern(box, 4);
34365 var pos = this.el.getBox(true);
34369 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34371 var hit_end = false;
34373 Roo.each(queue, function(box){
34377 Roo.each(box, function(b){
34379 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34389 Roo.each(box, function(b){
34391 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34394 mx = Math.max(mx, b.x);
34398 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34402 Roo.each(box, function(b){
34404 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34418 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34421 /** Sets position of item in DOM
34422 * @param {Element} item
34423 * @param {Number} x - horizontal position
34424 * @param {Number} y - vertical position
34425 * @param {Boolean} isInstant - disables transitions
34427 _processVerticalLayoutQueue : function( queue, isInstant )
34429 var pos = this.el.getBox(true);
34434 for (var i = 0; i < this.cols; i++){
34438 Roo.each(queue, function(box, k){
34440 var col = k % this.cols;
34442 Roo.each(box, function(b,kk){
34444 b.el.position('absolute');
34446 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34447 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34449 if(b.size == 'md-left' || b.size == 'md-right'){
34450 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34451 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34454 b.el.setWidth(width);
34455 b.el.setHeight(height);
34457 b.el.select('iframe',true).setSize(width,height);
34461 for (var i = 0; i < this.cols; i++){
34463 if(maxY[i] < maxY[col]){
34468 col = Math.min(col, i);
34472 x = pos.x + col * (this.colWidth + this.padWidth);
34476 var positions = [];
34478 switch (box.length){
34480 positions = this.getVerticalOneBoxColPositions(x, y, box);
34483 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34486 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34489 positions = this.getVerticalFourBoxColPositions(x, y, box);
34495 Roo.each(box, function(b,kk){
34497 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34499 var sz = b.el.getSize();
34501 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34509 for (var i = 0; i < this.cols; i++){
34510 mY = Math.max(mY, maxY[i]);
34513 this.el.setHeight(mY - pos.y);
34517 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34519 // var pos = this.el.getBox(true);
34522 // var maxX = pos.right;
34524 // var maxHeight = 0;
34526 // Roo.each(items, function(item, k){
34530 // item.el.position('absolute');
34532 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34534 // item.el.setWidth(width);
34536 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34538 // item.el.setHeight(height);
34541 // item.el.setXY([x, y], isInstant ? false : true);
34543 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34546 // y = y + height + this.alternativePadWidth;
34548 // maxHeight = maxHeight + height + this.alternativePadWidth;
34552 // this.el.setHeight(maxHeight);
34556 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34558 var pos = this.el.getBox(true);
34563 var maxX = pos.right;
34565 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34567 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34569 Roo.each(queue, function(box, k){
34571 Roo.each(box, function(b, kk){
34573 b.el.position('absolute');
34575 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34576 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34578 if(b.size == 'md-left' || b.size == 'md-right'){
34579 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34580 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34583 b.el.setWidth(width);
34584 b.el.setHeight(height);
34592 var positions = [];
34594 switch (box.length){
34596 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34599 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34602 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34605 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34611 Roo.each(box, function(b,kk){
34613 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34615 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34623 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34625 Roo.each(eItems, function(b,k){
34627 b.size = (k == 0) ? 'sm' : 'xs';
34628 b.x = (k == 0) ? 2 : 1;
34629 b.y = (k == 0) ? 2 : 1;
34631 b.el.position('absolute');
34633 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34635 b.el.setWidth(width);
34637 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34639 b.el.setHeight(height);
34643 var positions = [];
34646 x : maxX - this.unitWidth * 2 - this.gutter,
34651 x : maxX - this.unitWidth,
34652 y : minY + (this.unitWidth + this.gutter) * 2
34656 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34660 Roo.each(eItems, function(b,k){
34662 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34668 getVerticalOneBoxColPositions : function(x, y, box)
34672 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34674 if(box[0].size == 'md-left'){
34678 if(box[0].size == 'md-right'){
34683 x : x + (this.unitWidth + this.gutter) * rand,
34690 getVerticalTwoBoxColPositions : function(x, y, box)
34694 if(box[0].size == 'xs'){
34698 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34702 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34716 x : x + (this.unitWidth + this.gutter) * 2,
34717 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34724 getVerticalThreeBoxColPositions : function(x, y, box)
34728 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34736 x : x + (this.unitWidth + this.gutter) * 1,
34741 x : x + (this.unitWidth + this.gutter) * 2,
34749 if(box[0].size == 'xs' && box[1].size == 'xs'){
34758 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34762 x : x + (this.unitWidth + this.gutter) * 1,
34776 x : x + (this.unitWidth + this.gutter) * 2,
34781 x : x + (this.unitWidth + this.gutter) * 2,
34782 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34789 getVerticalFourBoxColPositions : function(x, y, box)
34793 if(box[0].size == 'xs'){
34802 y : y + (this.unitHeight + this.gutter) * 1
34807 y : y + (this.unitHeight + this.gutter) * 2
34811 x : x + (this.unitWidth + this.gutter) * 1,
34825 x : x + (this.unitWidth + this.gutter) * 2,
34830 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34831 y : y + (this.unitHeight + this.gutter) * 1
34835 x : x + (this.unitWidth + this.gutter) * 2,
34836 y : y + (this.unitWidth + this.gutter) * 2
34843 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34847 if(box[0].size == 'md-left'){
34849 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34856 if(box[0].size == 'md-right'){
34858 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34859 y : minY + (this.unitWidth + this.gutter) * 1
34865 var rand = Math.floor(Math.random() * (4 - box[0].y));
34868 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34869 y : minY + (this.unitWidth + this.gutter) * rand
34876 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34880 if(box[0].size == 'xs'){
34883 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34888 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34902 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34903 y : minY + (this.unitWidth + this.gutter) * 2
34910 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34914 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34917 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34922 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34923 y : minY + (this.unitWidth + this.gutter) * 1
34927 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34928 y : minY + (this.unitWidth + this.gutter) * 2
34935 if(box[0].size == 'xs' && box[1].size == 'xs'){
34938 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34943 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34948 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34949 y : minY + (this.unitWidth + this.gutter) * 1
34957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963 y : minY + (this.unitWidth + this.gutter) * 2
34967 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34968 y : minY + (this.unitWidth + this.gutter) * 2
34975 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34979 if(box[0].size == 'xs'){
34982 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34987 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34992 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),
34997 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34998 y : minY + (this.unitWidth + this.gutter) * 1
35006 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35011 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35012 y : minY + (this.unitWidth + this.gutter) * 2
35016 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35017 y : minY + (this.unitWidth + this.gutter) * 2
35021 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),
35022 y : minY + (this.unitWidth + this.gutter) * 2
35030 * remove a Masonry Brick
35031 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35033 removeBrick : function(brick_id)
35039 for (var i = 0; i<this.bricks.length; i++) {
35040 if (this.bricks[i].id == brick_id) {
35041 this.bricks.splice(i,1);
35042 this.el.dom.removeChild(Roo.get(brick_id).dom);
35049 * adds a Masonry Brick
35050 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35052 addBrick : function(cfg)
35054 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35055 //this.register(cn);
35056 cn.parentId = this.id;
35057 cn.render(this.el);
35062 * register a Masonry Brick
35063 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35066 register : function(brick)
35068 this.bricks.push(brick);
35069 brick.masonryId = this.id;
35073 * clear all the Masonry Brick
35075 clearAll : function()
35078 //this.getChildContainer().dom.innerHTML = "";
35079 this.el.dom.innerHTML = '';
35082 getSelected : function()
35084 if (!this.selectedBrick) {
35088 return this.selectedBrick;
35092 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35096 * register a Masonry Layout
35097 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35100 register : function(layout)
35102 this.groups[layout.id] = layout;
35105 * fetch a Masonry Layout based on the masonry layout ID
35106 * @param {string} the masonry layout to add
35107 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35110 get: function(layout_id) {
35111 if (typeof(this.groups[layout_id]) == 'undefined') {
35114 return this.groups[layout_id] ;
35126 * http://masonry.desandro.com
35128 * The idea is to render all the bricks based on vertical width...
35130 * The original code extends 'outlayer' - we might need to use that....
35136 * @class Roo.bootstrap.LayoutMasonryAuto
35137 * @extends Roo.bootstrap.Component
35138 * Bootstrap Layout Masonry class
35141 * Create a new Element
35142 * @param {Object} config The config object
35145 Roo.bootstrap.LayoutMasonryAuto = function(config){
35146 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35149 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35152 * @cfg {Boolean} isFitWidth - resize the width..
35154 isFitWidth : false, // options..
35156 * @cfg {Boolean} isOriginLeft = left align?
35158 isOriginLeft : true,
35160 * @cfg {Boolean} isOriginTop = top align?
35162 isOriginTop : false,
35164 * @cfg {Boolean} isLayoutInstant = no animation?
35166 isLayoutInstant : false, // needed?
35168 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35170 isResizingContainer : true,
35172 * @cfg {Number} columnWidth width of the columns
35178 * @cfg {Number} maxCols maximum number of columns
35183 * @cfg {Number} padHeight padding below box..
35189 * @cfg {Boolean} isAutoInitial defalut true
35192 isAutoInitial : true,
35198 initialColumnWidth : 0,
35199 currentSize : null,
35201 colYs : null, // array.
35208 bricks: null, //CompositeElement
35209 cols : 0, // array?
35210 // element : null, // wrapped now this.el
35211 _isLayoutInited : null,
35214 getAutoCreate : function(){
35218 cls: 'blog-masonary-wrapper ' + this.cls,
35220 cls : 'mas-boxes masonary'
35227 getChildContainer: function( )
35229 if (this.boxesEl) {
35230 return this.boxesEl;
35233 this.boxesEl = this.el.select('.mas-boxes').first();
35235 return this.boxesEl;
35239 initEvents : function()
35243 if(this.isAutoInitial){
35244 Roo.log('hook children rendered');
35245 this.on('childrenrendered', function() {
35246 Roo.log('children rendered');
35253 initial : function()
35255 this.reloadItems();
35257 this.currentSize = this.el.getBox(true);
35259 /// was window resize... - let's see if this works..
35260 Roo.EventManager.onWindowResize(this.resize, this);
35262 if(!this.isAutoInitial){
35267 this.layout.defer(500,this);
35270 reloadItems: function()
35272 this.bricks = this.el.select('.masonry-brick', true);
35274 this.bricks.each(function(b) {
35275 //Roo.log(b.getSize());
35276 if (!b.attr('originalwidth')) {
35277 b.attr('originalwidth', b.getSize().width);
35282 Roo.log(this.bricks.elements.length);
35285 resize : function()
35288 var cs = this.el.getBox(true);
35290 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35291 Roo.log("no change in with or X");
35294 this.currentSize = cs;
35298 layout : function()
35301 this._resetLayout();
35302 //this._manageStamps();
35304 // don't animate first layout
35305 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35306 this.layoutItems( isInstant );
35308 // flag for initalized
35309 this._isLayoutInited = true;
35312 layoutItems : function( isInstant )
35314 //var items = this._getItemsForLayout( this.items );
35315 // original code supports filtering layout items.. we just ignore it..
35317 this._layoutItems( this.bricks , isInstant );
35319 this._postLayout();
35321 _layoutItems : function ( items , isInstant)
35323 //this.fireEvent( 'layout', this, items );
35326 if ( !items || !items.elements.length ) {
35327 // no items, emit event with empty array
35332 items.each(function(item) {
35333 Roo.log("layout item");
35335 // get x/y object from method
35336 var position = this._getItemLayoutPosition( item );
35338 position.item = item;
35339 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35340 queue.push( position );
35343 this._processLayoutQueue( queue );
35345 /** Sets position of item in DOM
35346 * @param {Element} item
35347 * @param {Number} x - horizontal position
35348 * @param {Number} y - vertical position
35349 * @param {Boolean} isInstant - disables transitions
35351 _processLayoutQueue : function( queue )
35353 for ( var i=0, len = queue.length; i < len; i++ ) {
35354 var obj = queue[i];
35355 obj.item.position('absolute');
35356 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35362 * Any logic you want to do after each layout,
35363 * i.e. size the container
35365 _postLayout : function()
35367 this.resizeContainer();
35370 resizeContainer : function()
35372 if ( !this.isResizingContainer ) {
35375 var size = this._getContainerSize();
35377 this.el.setSize(size.width,size.height);
35378 this.boxesEl.setSize(size.width,size.height);
35384 _resetLayout : function()
35386 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35387 this.colWidth = this.el.getWidth();
35388 //this.gutter = this.el.getWidth();
35390 this.measureColumns();
35396 this.colYs.push( 0 );
35402 measureColumns : function()
35404 this.getContainerWidth();
35405 // if columnWidth is 0, default to outerWidth of first item
35406 if ( !this.columnWidth ) {
35407 var firstItem = this.bricks.first();
35408 Roo.log(firstItem);
35409 this.columnWidth = this.containerWidth;
35410 if (firstItem && firstItem.attr('originalwidth') ) {
35411 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35413 // columnWidth fall back to item of first element
35414 Roo.log("set column width?");
35415 this.initialColumnWidth = this.columnWidth ;
35417 // if first elem has no width, default to size of container
35422 if (this.initialColumnWidth) {
35423 this.columnWidth = this.initialColumnWidth;
35428 // column width is fixed at the top - however if container width get's smaller we should
35431 // this bit calcs how man columns..
35433 var columnWidth = this.columnWidth += this.gutter;
35435 // calculate columns
35436 var containerWidth = this.containerWidth + this.gutter;
35438 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35439 // fix rounding errors, typically with gutters
35440 var excess = columnWidth - containerWidth % columnWidth;
35443 // if overshoot is less than a pixel, round up, otherwise floor it
35444 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35445 cols = Math[ mathMethod ]( cols );
35446 this.cols = Math.max( cols, 1 );
35447 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35449 // padding positioning..
35450 var totalColWidth = this.cols * this.columnWidth;
35451 var padavail = this.containerWidth - totalColWidth;
35452 // so for 2 columns - we need 3 'pads'
35454 var padNeeded = (1+this.cols) * this.padWidth;
35456 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35458 this.columnWidth += padExtra
35459 //this.padWidth = Math.floor(padavail / ( this.cols));
35461 // adjust colum width so that padding is fixed??
35463 // we have 3 columns ... total = width * 3
35464 // we have X left over... that should be used by
35466 //if (this.expandC) {
35474 getContainerWidth : function()
35476 /* // container is parent if fit width
35477 var container = this.isFitWidth ? this.element.parentNode : this.element;
35478 // check that this.size and size are there
35479 // IE8 triggers resize on body size change, so they might not be
35481 var size = getSize( container ); //FIXME
35482 this.containerWidth = size && size.innerWidth; //FIXME
35485 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35489 _getItemLayoutPosition : function( item ) // what is item?
35491 // we resize the item to our columnWidth..
35493 item.setWidth(this.columnWidth);
35494 item.autoBoxAdjust = false;
35496 var sz = item.getSize();
35498 // how many columns does this brick span
35499 var remainder = this.containerWidth % this.columnWidth;
35501 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35502 // round if off by 1 pixel, otherwise use ceil
35503 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35504 colSpan = Math.min( colSpan, this.cols );
35506 // normally this should be '1' as we dont' currently allow multi width columns..
35508 var colGroup = this._getColGroup( colSpan );
35509 // get the minimum Y value from the columns
35510 var minimumY = Math.min.apply( Math, colGroup );
35511 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35513 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35515 // position the brick
35517 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35518 y: this.currentSize.y + minimumY + this.padHeight
35522 // apply setHeight to necessary columns
35523 var setHeight = minimumY + sz.height + this.padHeight;
35524 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35526 var setSpan = this.cols + 1 - colGroup.length;
35527 for ( var i = 0; i < setSpan; i++ ) {
35528 this.colYs[ shortColIndex + i ] = setHeight ;
35535 * @param {Number} colSpan - number of columns the element spans
35536 * @returns {Array} colGroup
35538 _getColGroup : function( colSpan )
35540 if ( colSpan < 2 ) {
35541 // if brick spans only one column, use all the column Ys
35546 // how many different places could this brick fit horizontally
35547 var groupCount = this.cols + 1 - colSpan;
35548 // for each group potential horizontal position
35549 for ( var i = 0; i < groupCount; i++ ) {
35550 // make an array of colY values for that one group
35551 var groupColYs = this.colYs.slice( i, i + colSpan );
35552 // and get the max value of the array
35553 colGroup[i] = Math.max.apply( Math, groupColYs );
35558 _manageStamp : function( stamp )
35560 var stampSize = stamp.getSize();
35561 var offset = stamp.getBox();
35562 // get the columns that this stamp affects
35563 var firstX = this.isOriginLeft ? offset.x : offset.right;
35564 var lastX = firstX + stampSize.width;
35565 var firstCol = Math.floor( firstX / this.columnWidth );
35566 firstCol = Math.max( 0, firstCol );
35568 var lastCol = Math.floor( lastX / this.columnWidth );
35569 // lastCol should not go over if multiple of columnWidth #425
35570 lastCol -= lastX % this.columnWidth ? 0 : 1;
35571 lastCol = Math.min( this.cols - 1, lastCol );
35573 // set colYs to bottom of the stamp
35574 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35577 for ( var i = firstCol; i <= lastCol; i++ ) {
35578 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35583 _getContainerSize : function()
35585 this.maxY = Math.max.apply( Math, this.colYs );
35590 if ( this.isFitWidth ) {
35591 size.width = this._getContainerFitWidth();
35597 _getContainerFitWidth : function()
35599 var unusedCols = 0;
35600 // count unused columns
35603 if ( this.colYs[i] !== 0 ) {
35608 // fit container to columns that have been used
35609 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35612 needsResizeLayout : function()
35614 var previousWidth = this.containerWidth;
35615 this.getContainerWidth();
35616 return previousWidth !== this.containerWidth;
35631 * @class Roo.bootstrap.MasonryBrick
35632 * @extends Roo.bootstrap.Component
35633 * Bootstrap MasonryBrick class
35636 * Create a new MasonryBrick
35637 * @param {Object} config The config object
35640 Roo.bootstrap.MasonryBrick = function(config){
35642 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35644 Roo.bootstrap.MasonryBrick.register(this);
35650 * When a MasonryBrick is clcik
35651 * @param {Roo.bootstrap.MasonryBrick} this
35652 * @param {Roo.EventObject} e
35658 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35661 * @cfg {String} title
35665 * @cfg {String} html
35669 * @cfg {String} bgimage
35673 * @cfg {String} videourl
35677 * @cfg {String} cls
35681 * @cfg {String} href
35685 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35690 * @cfg {String} placetitle (center|bottom)
35695 * @cfg {Boolean} isFitContainer defalut true
35697 isFitContainer : true,
35700 * @cfg {Boolean} preventDefault defalut false
35702 preventDefault : false,
35705 * @cfg {Boolean} inverse defalut false
35707 maskInverse : false,
35709 getAutoCreate : function()
35711 if(!this.isFitContainer){
35712 return this.getSplitAutoCreate();
35715 var cls = 'masonry-brick masonry-brick-full';
35717 if(this.href.length){
35718 cls += ' masonry-brick-link';
35721 if(this.bgimage.length){
35722 cls += ' masonry-brick-image';
35725 if(this.maskInverse){
35726 cls += ' mask-inverse';
35729 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35730 cls += ' enable-mask';
35734 cls += ' masonry-' + this.size + '-brick';
35737 if(this.placetitle.length){
35739 switch (this.placetitle) {
35741 cls += ' masonry-center-title';
35744 cls += ' masonry-bottom-title';
35751 if(!this.html.length && !this.bgimage.length){
35752 cls += ' masonry-center-title';
35755 if(!this.html.length && this.bgimage.length){
35756 cls += ' masonry-bottom-title';
35761 cls += ' ' + this.cls;
35765 tag: (this.href.length) ? 'a' : 'div',
35770 cls: 'masonry-brick-mask'
35774 cls: 'masonry-brick-paragraph',
35780 if(this.href.length){
35781 cfg.href = this.href;
35784 var cn = cfg.cn[1].cn;
35786 if(this.title.length){
35789 cls: 'masonry-brick-title',
35794 if(this.html.length){
35797 cls: 'masonry-brick-text',
35802 if (!this.title.length && !this.html.length) {
35803 cfg.cn[1].cls += ' hide';
35806 if(this.bgimage.length){
35809 cls: 'masonry-brick-image-view',
35814 if(this.videourl.length){
35815 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35816 // youtube support only?
35819 cls: 'masonry-brick-image-view',
35822 allowfullscreen : true
35830 getSplitAutoCreate : function()
35832 var cls = 'masonry-brick masonry-brick-split';
35834 if(this.href.length){
35835 cls += ' masonry-brick-link';
35838 if(this.bgimage.length){
35839 cls += ' masonry-brick-image';
35843 cls += ' masonry-' + this.size + '-brick';
35846 switch (this.placetitle) {
35848 cls += ' masonry-center-title';
35851 cls += ' masonry-bottom-title';
35854 if(!this.bgimage.length){
35855 cls += ' masonry-center-title';
35858 if(this.bgimage.length){
35859 cls += ' masonry-bottom-title';
35865 cls += ' ' + this.cls;
35869 tag: (this.href.length) ? 'a' : 'div',
35874 cls: 'masonry-brick-split-head',
35878 cls: 'masonry-brick-paragraph',
35885 cls: 'masonry-brick-split-body',
35891 if(this.href.length){
35892 cfg.href = this.href;
35895 if(this.title.length){
35896 cfg.cn[0].cn[0].cn.push({
35898 cls: 'masonry-brick-title',
35903 if(this.html.length){
35904 cfg.cn[1].cn.push({
35906 cls: 'masonry-brick-text',
35911 if(this.bgimage.length){
35912 cfg.cn[0].cn.push({
35914 cls: 'masonry-brick-image-view',
35919 if(this.videourl.length){
35920 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35921 // youtube support only?
35922 cfg.cn[0].cn.cn.push({
35924 cls: 'masonry-brick-image-view',
35927 allowfullscreen : true
35934 initEvents: function()
35936 switch (this.size) {
35969 this.el.on('touchstart', this.onTouchStart, this);
35970 this.el.on('touchmove', this.onTouchMove, this);
35971 this.el.on('touchend', this.onTouchEnd, this);
35972 this.el.on('contextmenu', this.onContextMenu, this);
35974 this.el.on('mouseenter' ,this.enter, this);
35975 this.el.on('mouseleave', this.leave, this);
35976 this.el.on('click', this.onClick, this);
35979 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35980 this.parent().bricks.push(this);
35985 onClick: function(e, el)
35987 var time = this.endTimer - this.startTimer;
35988 // Roo.log(e.preventDefault());
35991 e.preventDefault();
35996 if(!this.preventDefault){
36000 e.preventDefault();
36002 if (this.activeClass != '') {
36003 this.selectBrick();
36006 this.fireEvent('click', this, e);
36009 enter: function(e, el)
36011 e.preventDefault();
36013 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36017 if(this.bgimage.length && this.html.length){
36018 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36022 leave: function(e, el)
36024 e.preventDefault();
36026 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36030 if(this.bgimage.length && this.html.length){
36031 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36035 onTouchStart: function(e, el)
36037 // e.preventDefault();
36039 this.touchmoved = false;
36041 if(!this.isFitContainer){
36045 if(!this.bgimage.length || !this.html.length){
36049 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36051 this.timer = new Date().getTime();
36055 onTouchMove: function(e, el)
36057 this.touchmoved = true;
36060 onContextMenu : function(e,el)
36062 e.preventDefault();
36063 e.stopPropagation();
36067 onTouchEnd: function(e, el)
36069 // e.preventDefault();
36071 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36078 if(!this.bgimage.length || !this.html.length){
36080 if(this.href.length){
36081 window.location.href = this.href;
36087 if(!this.isFitContainer){
36091 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36093 window.location.href = this.href;
36096 //selection on single brick only
36097 selectBrick : function() {
36099 if (!this.parentId) {
36103 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36104 var index = m.selectedBrick.indexOf(this.id);
36107 m.selectedBrick.splice(index,1);
36108 this.el.removeClass(this.activeClass);
36112 for(var i = 0; i < m.selectedBrick.length; i++) {
36113 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36114 b.el.removeClass(b.activeClass);
36117 m.selectedBrick = [];
36119 m.selectedBrick.push(this.id);
36120 this.el.addClass(this.activeClass);
36124 isSelected : function(){
36125 return this.el.hasClass(this.activeClass);
36130 Roo.apply(Roo.bootstrap.MasonryBrick, {
36133 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36135 * register a Masonry Brick
36136 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36139 register : function(brick)
36141 //this.groups[brick.id] = brick;
36142 this.groups.add(brick.id, brick);
36145 * fetch a masonry brick based on the masonry brick ID
36146 * @param {string} the masonry brick to add
36147 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36150 get: function(brick_id)
36152 // if (typeof(this.groups[brick_id]) == 'undefined') {
36155 // return this.groups[brick_id] ;
36157 if(this.groups.key(brick_id)) {
36158 return this.groups.key(brick_id);
36176 * @class Roo.bootstrap.Brick
36177 * @extends Roo.bootstrap.Component
36178 * Bootstrap Brick class
36181 * Create a new Brick
36182 * @param {Object} config The config object
36185 Roo.bootstrap.Brick = function(config){
36186 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36192 * When a Brick is click
36193 * @param {Roo.bootstrap.Brick} this
36194 * @param {Roo.EventObject} e
36200 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36203 * @cfg {String} title
36207 * @cfg {String} html
36211 * @cfg {String} bgimage
36215 * @cfg {String} cls
36219 * @cfg {String} href
36223 * @cfg {String} video
36227 * @cfg {Boolean} square
36231 getAutoCreate : function()
36233 var cls = 'roo-brick';
36235 if(this.href.length){
36236 cls += ' roo-brick-link';
36239 if(this.bgimage.length){
36240 cls += ' roo-brick-image';
36243 if(!this.html.length && !this.bgimage.length){
36244 cls += ' roo-brick-center-title';
36247 if(!this.html.length && this.bgimage.length){
36248 cls += ' roo-brick-bottom-title';
36252 cls += ' ' + this.cls;
36256 tag: (this.href.length) ? 'a' : 'div',
36261 cls: 'roo-brick-paragraph',
36267 if(this.href.length){
36268 cfg.href = this.href;
36271 var cn = cfg.cn[0].cn;
36273 if(this.title.length){
36276 cls: 'roo-brick-title',
36281 if(this.html.length){
36284 cls: 'roo-brick-text',
36291 if(this.bgimage.length){
36294 cls: 'roo-brick-image-view',
36302 initEvents: function()
36304 if(this.title.length || this.html.length){
36305 this.el.on('mouseenter' ,this.enter, this);
36306 this.el.on('mouseleave', this.leave, this);
36309 Roo.EventManager.onWindowResize(this.resize, this);
36311 if(this.bgimage.length){
36312 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36313 this.imageEl.on('load', this.onImageLoad, this);
36320 onImageLoad : function()
36325 resize : function()
36327 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36329 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36331 if(this.bgimage.length){
36332 var image = this.el.select('.roo-brick-image-view', true).first();
36334 image.setWidth(paragraph.getWidth());
36337 image.setHeight(paragraph.getWidth());
36340 this.el.setHeight(image.getHeight());
36341 paragraph.setHeight(image.getHeight());
36347 enter: function(e, el)
36349 e.preventDefault();
36351 if(this.bgimage.length){
36352 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36353 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36357 leave: function(e, el)
36359 e.preventDefault();
36361 if(this.bgimage.length){
36362 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36363 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36378 * @class Roo.bootstrap.NumberField
36379 * @extends Roo.bootstrap.Input
36380 * Bootstrap NumberField class
36386 * Create a new NumberField
36387 * @param {Object} config The config object
36390 Roo.bootstrap.NumberField = function(config){
36391 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36394 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36397 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36399 allowDecimals : true,
36401 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36403 decimalSeparator : ".",
36405 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36407 decimalPrecision : 2,
36409 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36411 allowNegative : true,
36414 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36418 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36420 minValue : Number.NEGATIVE_INFINITY,
36422 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36424 maxValue : Number.MAX_VALUE,
36426 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36428 minText : "The minimum value for this field is {0}",
36430 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36432 maxText : "The maximum value for this field is {0}",
36434 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36435 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36437 nanText : "{0} is not a valid number",
36439 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36441 thousandsDelimiter : false,
36443 * @cfg {String} valueAlign alignment of value
36445 valueAlign : "left",
36447 getAutoCreate : function()
36449 var hiddenInput = {
36453 cls: 'hidden-number-input'
36457 hiddenInput.name = this.name;
36462 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36464 this.name = hiddenInput.name;
36466 if(cfg.cn.length > 0) {
36467 cfg.cn.push(hiddenInput);
36474 initEvents : function()
36476 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36478 var allowed = "0123456789";
36480 if(this.allowDecimals){
36481 allowed += this.decimalSeparator;
36484 if(this.allowNegative){
36488 if(this.thousandsDelimiter) {
36492 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36494 var keyPress = function(e){
36496 var k = e.getKey();
36498 var c = e.getCharCode();
36501 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36502 allowed.indexOf(String.fromCharCode(c)) === -1
36508 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36512 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36517 this.el.on("keypress", keyPress, this);
36520 validateValue : function(value)
36523 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36527 var num = this.parseValue(value);
36530 this.markInvalid(String.format(this.nanText, value));
36534 if(num < this.minValue){
36535 this.markInvalid(String.format(this.minText, this.minValue));
36539 if(num > this.maxValue){
36540 this.markInvalid(String.format(this.maxText, this.maxValue));
36547 getValue : function()
36549 var v = this.hiddenEl().getValue();
36551 return this.fixPrecision(this.parseValue(v));
36554 parseValue : function(value)
36556 if(this.thousandsDelimiter) {
36558 r = new RegExp(",", "g");
36559 value = value.replace(r, "");
36562 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36563 return isNaN(value) ? '' : value;
36566 fixPrecision : function(value)
36568 if(this.thousandsDelimiter) {
36570 r = new RegExp(",", "g");
36571 value = value.replace(r, "");
36574 var nan = isNaN(value);
36576 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36577 return nan ? '' : value;
36579 return parseFloat(value).toFixed(this.decimalPrecision);
36582 setValue : function(v)
36584 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36590 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36592 this.inputEl().dom.value = (v == '') ? '' :
36593 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36595 if(!this.allowZero && v === '0') {
36596 this.hiddenEl().dom.value = '';
36597 this.inputEl().dom.value = '';
36604 decimalPrecisionFcn : function(v)
36606 return Math.floor(v);
36609 beforeBlur : function()
36611 var v = this.parseValue(this.getRawValue());
36613 if(v || v === 0 || v === ''){
36618 hiddenEl : function()
36620 return this.el.select('input.hidden-number-input',true).first();
36632 * @class Roo.bootstrap.DocumentSlider
36633 * @extends Roo.bootstrap.Component
36634 * Bootstrap DocumentSlider class
36637 * Create a new DocumentViewer
36638 * @param {Object} config The config object
36641 Roo.bootstrap.DocumentSlider = function(config){
36642 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36649 * Fire after initEvent
36650 * @param {Roo.bootstrap.DocumentSlider} this
36655 * Fire after update
36656 * @param {Roo.bootstrap.DocumentSlider} this
36662 * @param {Roo.bootstrap.DocumentSlider} this
36668 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36674 getAutoCreate : function()
36678 cls : 'roo-document-slider',
36682 cls : 'roo-document-slider-header',
36686 cls : 'roo-document-slider-header-title'
36692 cls : 'roo-document-slider-body',
36696 cls : 'roo-document-slider-prev',
36700 cls : 'fa fa-chevron-left'
36706 cls : 'roo-document-slider-thumb',
36710 cls : 'roo-document-slider-image'
36716 cls : 'roo-document-slider-next',
36720 cls : 'fa fa-chevron-right'
36732 initEvents : function()
36734 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36735 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36737 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36738 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36740 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36741 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36743 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36744 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36746 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36747 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36749 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36750 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36752 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36753 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36755 this.thumbEl.on('click', this.onClick, this);
36757 this.prevIndicator.on('click', this.prev, this);
36759 this.nextIndicator.on('click', this.next, this);
36763 initial : function()
36765 if(this.files.length){
36766 this.indicator = 1;
36770 this.fireEvent('initial', this);
36773 update : function()
36775 this.imageEl.attr('src', this.files[this.indicator - 1]);
36777 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36779 this.prevIndicator.show();
36781 if(this.indicator == 1){
36782 this.prevIndicator.hide();
36785 this.nextIndicator.show();
36787 if(this.indicator == this.files.length){
36788 this.nextIndicator.hide();
36791 this.thumbEl.scrollTo('top');
36793 this.fireEvent('update', this);
36796 onClick : function(e)
36798 e.preventDefault();
36800 this.fireEvent('click', this);
36805 e.preventDefault();
36807 this.indicator = Math.max(1, this.indicator - 1);
36814 e.preventDefault();
36816 this.indicator = Math.min(this.files.length, this.indicator + 1);
36830 * @class Roo.bootstrap.RadioSet
36831 * @extends Roo.bootstrap.Input
36832 * Bootstrap RadioSet class
36833 * @cfg {String} indicatorpos (left|right) default left
36834 * @cfg {Boolean} inline (true|false) inline the element (default true)
36835 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36837 * Create a new RadioSet
36838 * @param {Object} config The config object
36841 Roo.bootstrap.RadioSet = function(config){
36843 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36847 Roo.bootstrap.RadioSet.register(this);
36852 * Fires when the element is checked or unchecked.
36853 * @param {Roo.bootstrap.RadioSet} this This radio
36854 * @param {Roo.bootstrap.Radio} item The checked item
36859 * Fires when the element is click.
36860 * @param {Roo.bootstrap.RadioSet} this This radio set
36861 * @param {Roo.bootstrap.Radio} item The checked item
36862 * @param {Roo.EventObject} e The event object
36869 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36877 indicatorpos : 'left',
36879 getAutoCreate : function()
36883 cls : 'roo-radio-set-label',
36887 html : this.fieldLabel
36891 if (Roo.bootstrap.version == 3) {
36894 if(this.indicatorpos == 'left'){
36897 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36898 tooltip : 'This field is required'
36903 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36904 tooltip : 'This field is required'
36910 cls : 'roo-radio-set-items'
36913 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36915 if (align === 'left' && this.fieldLabel.length) {
36918 cls : "roo-radio-set-right",
36924 if(this.labelWidth > 12){
36925 label.style = "width: " + this.labelWidth + 'px';
36928 if(this.labelWidth < 13 && this.labelmd == 0){
36929 this.labelmd = this.labelWidth;
36932 if(this.labellg > 0){
36933 label.cls += ' col-lg-' + this.labellg;
36934 items.cls += ' col-lg-' + (12 - this.labellg);
36937 if(this.labelmd > 0){
36938 label.cls += ' col-md-' + this.labelmd;
36939 items.cls += ' col-md-' + (12 - this.labelmd);
36942 if(this.labelsm > 0){
36943 label.cls += ' col-sm-' + this.labelsm;
36944 items.cls += ' col-sm-' + (12 - this.labelsm);
36947 if(this.labelxs > 0){
36948 label.cls += ' col-xs-' + this.labelxs;
36949 items.cls += ' col-xs-' + (12 - this.labelxs);
36955 cls : 'roo-radio-set',
36959 cls : 'roo-radio-set-input',
36962 value : this.value ? this.value : ''
36969 if(this.weight.length){
36970 cfg.cls += ' roo-radio-' + this.weight;
36974 cfg.cls += ' roo-radio-set-inline';
36978 ['xs','sm','md','lg'].map(function(size){
36979 if (settings[size]) {
36980 cfg.cls += ' col-' + size + '-' + settings[size];
36988 initEvents : function()
36990 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36991 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36993 if(!this.fieldLabel.length){
36994 this.labelEl.hide();
36997 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36998 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37000 this.indicator = this.indicatorEl();
37002 if(this.indicator){
37003 this.indicator.addClass('invisible');
37006 this.originalValue = this.getValue();
37010 inputEl: function ()
37012 return this.el.select('.roo-radio-set-input', true).first();
37015 getChildContainer : function()
37017 return this.itemsEl;
37020 register : function(item)
37022 this.radioes.push(item);
37026 validate : function()
37028 if(this.getVisibilityEl().hasClass('hidden')){
37034 Roo.each(this.radioes, function(i){
37043 if(this.allowBlank) {
37047 if(this.disabled || valid){
37052 this.markInvalid();
37057 markValid : function()
37059 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37060 this.indicatorEl().removeClass('visible');
37061 this.indicatorEl().addClass('invisible');
37065 if (Roo.bootstrap.version == 3) {
37066 this.el.removeClass([this.invalidClass, this.validClass]);
37067 this.el.addClass(this.validClass);
37069 this.el.removeClass(['is-invalid','is-valid']);
37070 this.el.addClass(['is-valid']);
37072 this.fireEvent('valid', this);
37075 markInvalid : function(msg)
37077 if(this.allowBlank || this.disabled){
37081 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37082 this.indicatorEl().removeClass('invisible');
37083 this.indicatorEl().addClass('visible');
37085 if (Roo.bootstrap.version == 3) {
37086 this.el.removeClass([this.invalidClass, this.validClass]);
37087 this.el.addClass(this.invalidClass);
37089 this.el.removeClass(['is-invalid','is-valid']);
37090 this.el.addClass(['is-invalid']);
37093 this.fireEvent('invalid', this, msg);
37097 setValue : function(v, suppressEvent)
37099 if(this.value === v){
37106 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37109 Roo.each(this.radioes, function(i){
37111 i.el.removeClass('checked');
37114 Roo.each(this.radioes, function(i){
37116 if(i.value === v || i.value.toString() === v.toString()){
37118 i.el.addClass('checked');
37120 if(suppressEvent !== true){
37121 this.fireEvent('check', this, i);
37132 clearInvalid : function(){
37134 if(!this.el || this.preventMark){
37138 this.el.removeClass([this.invalidClass]);
37140 this.fireEvent('valid', this);
37145 Roo.apply(Roo.bootstrap.RadioSet, {
37149 register : function(set)
37151 this.groups[set.name] = set;
37154 get: function(name)
37156 if (typeof(this.groups[name]) == 'undefined') {
37160 return this.groups[name] ;
37166 * Ext JS Library 1.1.1
37167 * Copyright(c) 2006-2007, Ext JS, LLC.
37169 * Originally Released Under LGPL - original licence link has changed is not relivant.
37172 * <script type="text/javascript">
37177 * @class Roo.bootstrap.SplitBar
37178 * @extends Roo.util.Observable
37179 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37183 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37184 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37185 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37186 split.minSize = 100;
37187 split.maxSize = 600;
37188 split.animate = true;
37189 split.on('moved', splitterMoved);
37192 * Create a new SplitBar
37193 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37194 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37195 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37196 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37197 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37198 position of the SplitBar).
37200 Roo.bootstrap.SplitBar = function(cfg){
37205 // dragElement : elm
37206 // resizingElement: el,
37208 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37209 // placement : Roo.bootstrap.SplitBar.LEFT ,
37210 // existingProxy ???
37213 this.el = Roo.get(cfg.dragElement, true);
37214 this.el.dom.unselectable = "on";
37216 this.resizingEl = Roo.get(cfg.resizingElement, true);
37220 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37221 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37224 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37227 * The minimum size of the resizing element. (Defaults to 0)
37233 * The maximum size of the resizing element. (Defaults to 2000)
37236 this.maxSize = 2000;
37239 * Whether to animate the transition to the new size
37242 this.animate = false;
37245 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37248 this.useShim = false;
37253 if(!cfg.existingProxy){
37255 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37257 this.proxy = Roo.get(cfg.existingProxy).dom;
37260 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37263 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37266 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37269 this.dragSpecs = {};
37272 * @private The adapter to use to positon and resize elements
37274 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37275 this.adapter.init(this);
37277 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37279 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37280 this.el.addClass("roo-splitbar-h");
37283 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37284 this.el.addClass("roo-splitbar-v");
37290 * Fires when the splitter is moved (alias for {@link #event-moved})
37291 * @param {Roo.bootstrap.SplitBar} this
37292 * @param {Number} newSize the new width or height
37297 * Fires when the splitter is moved
37298 * @param {Roo.bootstrap.SplitBar} this
37299 * @param {Number} newSize the new width or height
37303 * @event beforeresize
37304 * Fires before the splitter is dragged
37305 * @param {Roo.bootstrap.SplitBar} this
37307 "beforeresize" : true,
37309 "beforeapply" : true
37312 Roo.util.Observable.call(this);
37315 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37316 onStartProxyDrag : function(x, y){
37317 this.fireEvent("beforeresize", this);
37319 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37321 o.enableDisplayMode("block");
37322 // all splitbars share the same overlay
37323 Roo.bootstrap.SplitBar.prototype.overlay = o;
37325 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37326 this.overlay.show();
37327 Roo.get(this.proxy).setDisplayed("block");
37328 var size = this.adapter.getElementSize(this);
37329 this.activeMinSize = this.getMinimumSize();;
37330 this.activeMaxSize = this.getMaximumSize();;
37331 var c1 = size - this.activeMinSize;
37332 var c2 = Math.max(this.activeMaxSize - size, 0);
37333 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37334 this.dd.resetConstraints();
37335 this.dd.setXConstraint(
37336 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37337 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37339 this.dd.setYConstraint(0, 0);
37341 this.dd.resetConstraints();
37342 this.dd.setXConstraint(0, 0);
37343 this.dd.setYConstraint(
37344 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37345 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37348 this.dragSpecs.startSize = size;
37349 this.dragSpecs.startPoint = [x, y];
37350 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37354 * @private Called after the drag operation by the DDProxy
37356 onEndProxyDrag : function(e){
37357 Roo.get(this.proxy).setDisplayed(false);
37358 var endPoint = Roo.lib.Event.getXY(e);
37360 this.overlay.hide();
37363 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37364 newSize = this.dragSpecs.startSize +
37365 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37366 endPoint[0] - this.dragSpecs.startPoint[0] :
37367 this.dragSpecs.startPoint[0] - endPoint[0]
37370 newSize = this.dragSpecs.startSize +
37371 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37372 endPoint[1] - this.dragSpecs.startPoint[1] :
37373 this.dragSpecs.startPoint[1] - endPoint[1]
37376 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37377 if(newSize != this.dragSpecs.startSize){
37378 if(this.fireEvent('beforeapply', this, newSize) !== false){
37379 this.adapter.setElementSize(this, newSize);
37380 this.fireEvent("moved", this, newSize);
37381 this.fireEvent("resize", this, newSize);
37387 * Get the adapter this SplitBar uses
37388 * @return The adapter object
37390 getAdapter : function(){
37391 return this.adapter;
37395 * Set the adapter this SplitBar uses
37396 * @param {Object} adapter A SplitBar adapter object
37398 setAdapter : function(adapter){
37399 this.adapter = adapter;
37400 this.adapter.init(this);
37404 * Gets the minimum size for the resizing element
37405 * @return {Number} The minimum size
37407 getMinimumSize : function(){
37408 return this.minSize;
37412 * Sets the minimum size for the resizing element
37413 * @param {Number} minSize The minimum size
37415 setMinimumSize : function(minSize){
37416 this.minSize = minSize;
37420 * Gets the maximum size for the resizing element
37421 * @return {Number} The maximum size
37423 getMaximumSize : function(){
37424 return this.maxSize;
37428 * Sets the maximum size for the resizing element
37429 * @param {Number} maxSize The maximum size
37431 setMaximumSize : function(maxSize){
37432 this.maxSize = maxSize;
37436 * Sets the initialize size for the resizing element
37437 * @param {Number} size The initial size
37439 setCurrentSize : function(size){
37440 var oldAnimate = this.animate;
37441 this.animate = false;
37442 this.adapter.setElementSize(this, size);
37443 this.animate = oldAnimate;
37447 * Destroy this splitbar.
37448 * @param {Boolean} removeEl True to remove the element
37450 destroy : function(removeEl){
37452 this.shim.remove();
37455 this.proxy.parentNode.removeChild(this.proxy);
37463 * @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.
37465 Roo.bootstrap.SplitBar.createProxy = function(dir){
37466 var proxy = new Roo.Element(document.createElement("div"));
37467 proxy.unselectable();
37468 var cls = 'roo-splitbar-proxy';
37469 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37470 document.body.appendChild(proxy.dom);
37475 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37476 * Default Adapter. It assumes the splitter and resizing element are not positioned
37477 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37479 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37482 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37483 // do nothing for now
37484 init : function(s){
37488 * Called before drag operations to get the current size of the resizing element.
37489 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37491 getElementSize : function(s){
37492 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37493 return s.resizingEl.getWidth();
37495 return s.resizingEl.getHeight();
37500 * Called after drag operations to set the size of the resizing element.
37501 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37502 * @param {Number} newSize The new size to set
37503 * @param {Function} onComplete A function to be invoked when resizing is complete
37505 setElementSize : function(s, newSize, onComplete){
37506 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37508 s.resizingEl.setWidth(newSize);
37510 onComplete(s, newSize);
37513 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37518 s.resizingEl.setHeight(newSize);
37520 onComplete(s, newSize);
37523 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37530 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37531 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37532 * Adapter that moves the splitter element to align with the resized sizing element.
37533 * Used with an absolute positioned SplitBar.
37534 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37535 * document.body, make sure you assign an id to the body element.
37537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37538 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37539 this.container = Roo.get(container);
37542 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37543 init : function(s){
37544 this.basic.init(s);
37547 getElementSize : function(s){
37548 return this.basic.getElementSize(s);
37551 setElementSize : function(s, newSize, onComplete){
37552 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37555 moveSplitter : function(s){
37556 var yes = Roo.bootstrap.SplitBar;
37557 switch(s.placement){
37559 s.el.setX(s.resizingEl.getRight());
37562 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37565 s.el.setY(s.resizingEl.getBottom());
37568 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37575 * Orientation constant - Create a vertical SplitBar
37579 Roo.bootstrap.SplitBar.VERTICAL = 1;
37582 * Orientation constant - Create a horizontal SplitBar
37586 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37589 * Placement constant - The resizing element is to the left of the splitter element
37593 Roo.bootstrap.SplitBar.LEFT = 1;
37596 * Placement constant - The resizing element is to the right of the splitter element
37600 Roo.bootstrap.SplitBar.RIGHT = 2;
37603 * Placement constant - The resizing element is positioned above the splitter element
37607 Roo.bootstrap.SplitBar.TOP = 3;
37610 * Placement constant - The resizing element is positioned under splitter element
37614 Roo.bootstrap.SplitBar.BOTTOM = 4;
37615 Roo.namespace("Roo.bootstrap.layout");/*
37617 * Ext JS Library 1.1.1
37618 * Copyright(c) 2006-2007, Ext JS, LLC.
37620 * Originally Released Under LGPL - original licence link has changed is not relivant.
37623 * <script type="text/javascript">
37627 * @class Roo.bootstrap.layout.Manager
37628 * @extends Roo.bootstrap.Component
37629 * Base class for layout managers.
37631 Roo.bootstrap.layout.Manager = function(config)
37633 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37639 /** false to disable window resize monitoring @type Boolean */
37640 this.monitorWindowResize = true;
37645 * Fires when a layout is performed.
37646 * @param {Roo.LayoutManager} this
37650 * @event regionresized
37651 * Fires when the user resizes a region.
37652 * @param {Roo.LayoutRegion} region The resized region
37653 * @param {Number} newSize The new size (width for east/west, height for north/south)
37655 "regionresized" : true,
37657 * @event regioncollapsed
37658 * Fires when a region is collapsed.
37659 * @param {Roo.LayoutRegion} region The collapsed region
37661 "regioncollapsed" : true,
37663 * @event regionexpanded
37664 * Fires when a region is expanded.
37665 * @param {Roo.LayoutRegion} region The expanded region
37667 "regionexpanded" : true
37669 this.updating = false;
37672 this.el = Roo.get(config.el);
37678 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37683 monitorWindowResize : true,
37689 onRender : function(ct, position)
37692 this.el = Roo.get(ct);
37695 //this.fireEvent('render',this);
37699 initEvents: function()
37703 // ie scrollbar fix
37704 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37705 document.body.scroll = "no";
37706 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37707 this.el.position('relative');
37709 this.id = this.el.id;
37710 this.el.addClass("roo-layout-container");
37711 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37712 if(this.el.dom != document.body ) {
37713 this.el.on('resize', this.layout,this);
37714 this.el.on('show', this.layout,this);
37720 * Returns true if this layout is currently being updated
37721 * @return {Boolean}
37723 isUpdating : function(){
37724 return this.updating;
37728 * Suspend the LayoutManager from doing auto-layouts while
37729 * making multiple add or remove calls
37731 beginUpdate : function(){
37732 this.updating = true;
37736 * Restore auto-layouts and optionally disable the manager from performing a layout
37737 * @param {Boolean} noLayout true to disable a layout update
37739 endUpdate : function(noLayout){
37740 this.updating = false;
37746 layout: function(){
37750 onRegionResized : function(region, newSize){
37751 this.fireEvent("regionresized", region, newSize);
37755 onRegionCollapsed : function(region){
37756 this.fireEvent("regioncollapsed", region);
37759 onRegionExpanded : function(region){
37760 this.fireEvent("regionexpanded", region);
37764 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37765 * performs box-model adjustments.
37766 * @return {Object} The size as an object {width: (the width), height: (the height)}
37768 getViewSize : function()
37771 if(this.el.dom != document.body){
37772 size = this.el.getSize();
37774 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37776 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37777 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37782 * Returns the Element this layout is bound to.
37783 * @return {Roo.Element}
37785 getEl : function(){
37790 * Returns the specified region.
37791 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37792 * @return {Roo.LayoutRegion}
37794 getRegion : function(target){
37795 return this.regions[target.toLowerCase()];
37798 onWindowResize : function(){
37799 if(this.monitorWindowResize){
37806 * Ext JS Library 1.1.1
37807 * Copyright(c) 2006-2007, Ext JS, LLC.
37809 * Originally Released Under LGPL - original licence link has changed is not relivant.
37812 * <script type="text/javascript">
37815 * @class Roo.bootstrap.layout.Border
37816 * @extends Roo.bootstrap.layout.Manager
37817 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37818 * please see: examples/bootstrap/nested.html<br><br>
37820 <b>The container the layout is rendered into can be either the body element or any other element.
37821 If it is not the body element, the container needs to either be an absolute positioned element,
37822 or you will need to add "position:relative" to the css of the container. You will also need to specify
37823 the container size if it is not the body element.</b>
37826 * Create a new Border
37827 * @param {Object} config Configuration options
37829 Roo.bootstrap.layout.Border = function(config){
37830 config = config || {};
37831 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37835 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37836 if(config[region]){
37837 config[region].region = region;
37838 this.addRegion(config[region]);
37844 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37846 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37848 parent : false, // this might point to a 'nest' or a ???
37851 * Creates and adds a new region if it doesn't already exist.
37852 * @param {String} target The target region key (north, south, east, west or center).
37853 * @param {Object} config The regions config object
37854 * @return {BorderLayoutRegion} The new region
37856 addRegion : function(config)
37858 if(!this.regions[config.region]){
37859 var r = this.factory(config);
37860 this.bindRegion(r);
37862 return this.regions[config.region];
37866 bindRegion : function(r){
37867 this.regions[r.config.region] = r;
37869 r.on("visibilitychange", this.layout, this);
37870 r.on("paneladded", this.layout, this);
37871 r.on("panelremoved", this.layout, this);
37872 r.on("invalidated", this.layout, this);
37873 r.on("resized", this.onRegionResized, this);
37874 r.on("collapsed", this.onRegionCollapsed, this);
37875 r.on("expanded", this.onRegionExpanded, this);
37879 * Performs a layout update.
37881 layout : function()
37883 if(this.updating) {
37887 // render all the rebions if they have not been done alreayd?
37888 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37889 if(this.regions[region] && !this.regions[region].bodyEl){
37890 this.regions[region].onRender(this.el)
37894 var size = this.getViewSize();
37895 var w = size.width;
37896 var h = size.height;
37901 //var x = 0, y = 0;
37903 var rs = this.regions;
37904 var north = rs["north"];
37905 var south = rs["south"];
37906 var west = rs["west"];
37907 var east = rs["east"];
37908 var center = rs["center"];
37909 //if(this.hideOnLayout){ // not supported anymore
37910 //c.el.setStyle("display", "none");
37912 if(north && north.isVisible()){
37913 var b = north.getBox();
37914 var m = north.getMargins();
37915 b.width = w - (m.left+m.right);
37918 centerY = b.height + b.y + m.bottom;
37919 centerH -= centerY;
37920 north.updateBox(this.safeBox(b));
37922 if(south && south.isVisible()){
37923 var b = south.getBox();
37924 var m = south.getMargins();
37925 b.width = w - (m.left+m.right);
37927 var totalHeight = (b.height + m.top + m.bottom);
37928 b.y = h - totalHeight + m.top;
37929 centerH -= totalHeight;
37930 south.updateBox(this.safeBox(b));
37932 if(west && west.isVisible()){
37933 var b = west.getBox();
37934 var m = west.getMargins();
37935 b.height = centerH - (m.top+m.bottom);
37937 b.y = centerY + m.top;
37938 var totalWidth = (b.width + m.left + m.right);
37939 centerX += totalWidth;
37940 centerW -= totalWidth;
37941 west.updateBox(this.safeBox(b));
37943 if(east && east.isVisible()){
37944 var b = east.getBox();
37945 var m = east.getMargins();
37946 b.height = centerH - (m.top+m.bottom);
37947 var totalWidth = (b.width + m.left + m.right);
37948 b.x = w - totalWidth + m.left;
37949 b.y = centerY + m.top;
37950 centerW -= totalWidth;
37951 east.updateBox(this.safeBox(b));
37954 var m = center.getMargins();
37956 x: centerX + m.left,
37957 y: centerY + m.top,
37958 width: centerW - (m.left+m.right),
37959 height: centerH - (m.top+m.bottom)
37961 //if(this.hideOnLayout){
37962 //center.el.setStyle("display", "block");
37964 center.updateBox(this.safeBox(centerBox));
37967 this.fireEvent("layout", this);
37971 safeBox : function(box){
37972 box.width = Math.max(0, box.width);
37973 box.height = Math.max(0, box.height);
37978 * Adds a ContentPanel (or subclass) to this layout.
37979 * @param {String} target The target region key (north, south, east, west or center).
37980 * @param {Roo.ContentPanel} panel The panel to add
37981 * @return {Roo.ContentPanel} The added panel
37983 add : function(target, panel){
37985 target = target.toLowerCase();
37986 return this.regions[target].add(panel);
37990 * Remove a ContentPanel (or subclass) to this layout.
37991 * @param {String} target The target region key (north, south, east, west or center).
37992 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37993 * @return {Roo.ContentPanel} The removed panel
37995 remove : function(target, panel){
37996 target = target.toLowerCase();
37997 return this.regions[target].remove(panel);
38001 * Searches all regions for a panel with the specified id
38002 * @param {String} panelId
38003 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38005 findPanel : function(panelId){
38006 var rs = this.regions;
38007 for(var target in rs){
38008 if(typeof rs[target] != "function"){
38009 var p = rs[target].getPanel(panelId);
38019 * Searches all regions for a panel with the specified id and activates (shows) it.
38020 * @param {String/ContentPanel} panelId The panels id or the panel itself
38021 * @return {Roo.ContentPanel} The shown panel or null
38023 showPanel : function(panelId) {
38024 var rs = this.regions;
38025 for(var target in rs){
38026 var r = rs[target];
38027 if(typeof r != "function"){
38028 if(r.hasPanel(panelId)){
38029 return r.showPanel(panelId);
38037 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38038 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38041 restoreState : function(provider){
38043 provider = Roo.state.Manager;
38045 var sm = new Roo.LayoutStateManager();
38046 sm.init(this, provider);
38052 * Adds a xtype elements to the layout.
38056 xtype : 'ContentPanel',
38063 xtype : 'NestedLayoutPanel',
38069 items : [ ... list of content panels or nested layout panels.. ]
38073 * @param {Object} cfg Xtype definition of item to add.
38075 addxtype : function(cfg)
38077 // basically accepts a pannel...
38078 // can accept a layout region..!?!?
38079 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38082 // theory? children can only be panels??
38084 //if (!cfg.xtype.match(/Panel$/)) {
38089 if (typeof(cfg.region) == 'undefined') {
38090 Roo.log("Failed to add Panel, region was not set");
38094 var region = cfg.region;
38100 xitems = cfg.items;
38105 if ( region == 'center') {
38106 Roo.log("Center: " + cfg.title);
38112 case 'Content': // ContentPanel (el, cfg)
38113 case 'Scroll': // ContentPanel (el, cfg)
38115 cfg.autoCreate = cfg.autoCreate || true;
38116 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38118 // var el = this.el.createChild();
38119 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38122 this.add(region, ret);
38126 case 'TreePanel': // our new panel!
38127 cfg.el = this.el.createChild();
38128 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38129 this.add(region, ret);
38134 // create a new Layout (which is a Border Layout...
38136 var clayout = cfg.layout;
38137 clayout.el = this.el.createChild();
38138 clayout.items = clayout.items || [];
38142 // replace this exitems with the clayout ones..
38143 xitems = clayout.items;
38145 // force background off if it's in center...
38146 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38147 cfg.background = false;
38149 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38152 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38153 //console.log('adding nested layout panel ' + cfg.toSource());
38154 this.add(region, ret);
38155 nb = {}; /// find first...
38160 // needs grid and region
38162 //var el = this.getRegion(region).el.createChild();
38164 *var el = this.el.createChild();
38165 // create the grid first...
38166 cfg.grid.container = el;
38167 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38170 if (region == 'center' && this.active ) {
38171 cfg.background = false;
38174 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38176 this.add(region, ret);
38178 if (cfg.background) {
38179 // render grid on panel activation (if panel background)
38180 ret.on('activate', function(gp) {
38181 if (!gp.grid.rendered) {
38182 // gp.grid.render(el);
38186 // cfg.grid.render(el);
38192 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38193 // it was the old xcomponent building that caused this before.
38194 // espeically if border is the top element in the tree.
38204 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38206 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38207 this.add(region, ret);
38211 throw "Can not add '" + cfg.xtype + "' to Border";
38217 this.beginUpdate();
38221 Roo.each(xitems, function(i) {
38222 region = nb && i.region ? i.region : false;
38224 var add = ret.addxtype(i);
38227 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38228 if (!i.background) {
38229 abn[region] = nb[region] ;
38236 // make the last non-background panel active..
38237 //if (nb) { Roo.log(abn); }
38240 for(var r in abn) {
38241 region = this.getRegion(r);
38243 // tried using nb[r], but it does not work..
38245 region.showPanel(abn[r]);
38256 factory : function(cfg)
38259 var validRegions = Roo.bootstrap.layout.Border.regions;
38261 var target = cfg.region;
38264 var r = Roo.bootstrap.layout;
38268 return new r.North(cfg);
38270 return new r.South(cfg);
38272 return new r.East(cfg);
38274 return new r.West(cfg);
38276 return new r.Center(cfg);
38278 throw 'Layout region "'+target+'" not supported.';
38285 * Ext JS Library 1.1.1
38286 * Copyright(c) 2006-2007, Ext JS, LLC.
38288 * Originally Released Under LGPL - original licence link has changed is not relivant.
38291 * <script type="text/javascript">
38295 * @class Roo.bootstrap.layout.Basic
38296 * @extends Roo.util.Observable
38297 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38298 * and does not have a titlebar, tabs or any other features. All it does is size and position
38299 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38300 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38301 * @cfg {string} region the region that it inhabits..
38302 * @cfg {bool} skipConfig skip config?
38306 Roo.bootstrap.layout.Basic = function(config){
38308 this.mgr = config.mgr;
38310 this.position = config.region;
38312 var skipConfig = config.skipConfig;
38316 * @scope Roo.BasicLayoutRegion
38320 * @event beforeremove
38321 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38322 * @param {Roo.LayoutRegion} this
38323 * @param {Roo.ContentPanel} panel The panel
38324 * @param {Object} e The cancel event object
38326 "beforeremove" : true,
38328 * @event invalidated
38329 * Fires when the layout for this region is changed.
38330 * @param {Roo.LayoutRegion} this
38332 "invalidated" : true,
38334 * @event visibilitychange
38335 * Fires when this region is shown or hidden
38336 * @param {Roo.LayoutRegion} this
38337 * @param {Boolean} visibility true or false
38339 "visibilitychange" : true,
38341 * @event paneladded
38342 * Fires when a panel is added.
38343 * @param {Roo.LayoutRegion} this
38344 * @param {Roo.ContentPanel} panel The panel
38346 "paneladded" : true,
38348 * @event panelremoved
38349 * Fires when a panel is removed.
38350 * @param {Roo.LayoutRegion} this
38351 * @param {Roo.ContentPanel} panel The panel
38353 "panelremoved" : true,
38355 * @event beforecollapse
38356 * Fires when this region before collapse.
38357 * @param {Roo.LayoutRegion} this
38359 "beforecollapse" : true,
38362 * Fires when this region is collapsed.
38363 * @param {Roo.LayoutRegion} this
38365 "collapsed" : true,
38368 * Fires when this region is expanded.
38369 * @param {Roo.LayoutRegion} this
38374 * Fires when this region is slid into view.
38375 * @param {Roo.LayoutRegion} this
38377 "slideshow" : true,
38380 * Fires when this region slides out of view.
38381 * @param {Roo.LayoutRegion} this
38383 "slidehide" : true,
38385 * @event panelactivated
38386 * Fires when a panel is activated.
38387 * @param {Roo.LayoutRegion} this
38388 * @param {Roo.ContentPanel} panel The activated panel
38390 "panelactivated" : true,
38393 * Fires when the user resizes this region.
38394 * @param {Roo.LayoutRegion} this
38395 * @param {Number} newSize The new size (width for east/west, height for north/south)
38399 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38400 this.panels = new Roo.util.MixedCollection();
38401 this.panels.getKey = this.getPanelId.createDelegate(this);
38403 this.activePanel = null;
38404 // ensure listeners are added...
38406 if (config.listeners || config.events) {
38407 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38408 listeners : config.listeners || {},
38409 events : config.events || {}
38413 if(skipConfig !== true){
38414 this.applyConfig(config);
38418 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38420 getPanelId : function(p){
38424 applyConfig : function(config){
38425 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38426 this.config = config;
38431 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38432 * the width, for horizontal (north, south) the height.
38433 * @param {Number} newSize The new width or height
38435 resizeTo : function(newSize){
38436 var el = this.el ? this.el :
38437 (this.activePanel ? this.activePanel.getEl() : null);
38439 switch(this.position){
38442 el.setWidth(newSize);
38443 this.fireEvent("resized", this, newSize);
38447 el.setHeight(newSize);
38448 this.fireEvent("resized", this, newSize);
38454 getBox : function(){
38455 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38458 getMargins : function(){
38459 return this.margins;
38462 updateBox : function(box){
38464 var el = this.activePanel.getEl();
38465 el.dom.style.left = box.x + "px";
38466 el.dom.style.top = box.y + "px";
38467 this.activePanel.setSize(box.width, box.height);
38471 * Returns the container element for this region.
38472 * @return {Roo.Element}
38474 getEl : function(){
38475 return this.activePanel;
38479 * Returns true if this region is currently visible.
38480 * @return {Boolean}
38482 isVisible : function(){
38483 return this.activePanel ? true : false;
38486 setActivePanel : function(panel){
38487 panel = this.getPanel(panel);
38488 if(this.activePanel && this.activePanel != panel){
38489 this.activePanel.setActiveState(false);
38490 this.activePanel.getEl().setLeftTop(-10000,-10000);
38492 this.activePanel = panel;
38493 panel.setActiveState(true);
38495 panel.setSize(this.box.width, this.box.height);
38497 this.fireEvent("panelactivated", this, panel);
38498 this.fireEvent("invalidated");
38502 * Show the specified panel.
38503 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38504 * @return {Roo.ContentPanel} The shown panel or null
38506 showPanel : function(panel){
38507 panel = this.getPanel(panel);
38509 this.setActivePanel(panel);
38515 * Get the active panel for this region.
38516 * @return {Roo.ContentPanel} The active panel or null
38518 getActivePanel : function(){
38519 return this.activePanel;
38523 * Add the passed ContentPanel(s)
38524 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38525 * @return {Roo.ContentPanel} The panel added (if only one was added)
38527 add : function(panel){
38528 if(arguments.length > 1){
38529 for(var i = 0, len = arguments.length; i < len; i++) {
38530 this.add(arguments[i]);
38534 if(this.hasPanel(panel)){
38535 this.showPanel(panel);
38538 var el = panel.getEl();
38539 if(el.dom.parentNode != this.mgr.el.dom){
38540 this.mgr.el.dom.appendChild(el.dom);
38542 if(panel.setRegion){
38543 panel.setRegion(this);
38545 this.panels.add(panel);
38546 el.setStyle("position", "absolute");
38547 if(!panel.background){
38548 this.setActivePanel(panel);
38549 if(this.config.initialSize && this.panels.getCount()==1){
38550 this.resizeTo(this.config.initialSize);
38553 this.fireEvent("paneladded", this, panel);
38558 * Returns true if the panel is in this region.
38559 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38560 * @return {Boolean}
38562 hasPanel : function(panel){
38563 if(typeof panel == "object"){ // must be panel obj
38564 panel = panel.getId();
38566 return this.getPanel(panel) ? true : false;
38570 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38571 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38572 * @param {Boolean} preservePanel Overrides the config preservePanel option
38573 * @return {Roo.ContentPanel} The panel that was removed
38575 remove : function(panel, preservePanel){
38576 panel = this.getPanel(panel);
38581 this.fireEvent("beforeremove", this, panel, e);
38582 if(e.cancel === true){
38585 var panelId = panel.getId();
38586 this.panels.removeKey(panelId);
38591 * Returns the panel specified or null if it's not in this region.
38592 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38593 * @return {Roo.ContentPanel}
38595 getPanel : function(id){
38596 if(typeof id == "object"){ // must be panel obj
38599 return this.panels.get(id);
38603 * Returns this regions position (north/south/east/west/center).
38606 getPosition: function(){
38607 return this.position;
38611 * Ext JS Library 1.1.1
38612 * Copyright(c) 2006-2007, Ext JS, LLC.
38614 * Originally Released Under LGPL - original licence link has changed is not relivant.
38617 * <script type="text/javascript">
38621 * @class Roo.bootstrap.layout.Region
38622 * @extends Roo.bootstrap.layout.Basic
38623 * This class represents a region in a layout manager.
38625 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38626 * @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})
38627 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38628 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38629 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38630 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38631 * @cfg {String} title The title for the region (overrides panel titles)
38632 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38633 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38634 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38635 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38636 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38637 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38638 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38639 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38640 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38641 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38643 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38644 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38645 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38646 * @cfg {Number} width For East/West panels
38647 * @cfg {Number} height For North/South panels
38648 * @cfg {Boolean} split To show the splitter
38649 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38651 * @cfg {string} cls Extra CSS classes to add to region
38653 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38654 * @cfg {string} region the region that it inhabits..
38657 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38658 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38660 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38661 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38662 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38664 Roo.bootstrap.layout.Region = function(config)
38666 this.applyConfig(config);
38668 var mgr = config.mgr;
38669 var pos = config.region;
38670 config.skipConfig = true;
38671 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38674 this.onRender(mgr.el);
38677 this.visible = true;
38678 this.collapsed = false;
38679 this.unrendered_panels = [];
38682 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38684 position: '', // set by wrapper (eg. north/south etc..)
38685 unrendered_panels : null, // unrendered panels.
38687 tabPosition : false,
38689 mgr: false, // points to 'Border'
38692 createBody : function(){
38693 /** This region's body element
38694 * @type Roo.Element */
38695 this.bodyEl = this.el.createChild({
38697 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38701 onRender: function(ctr, pos)
38703 var dh = Roo.DomHelper;
38704 /** This region's container element
38705 * @type Roo.Element */
38706 this.el = dh.append(ctr.dom, {
38708 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38710 /** This region's title element
38711 * @type Roo.Element */
38713 this.titleEl = dh.append(this.el.dom, {
38715 unselectable: "on",
38716 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38718 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38719 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38723 this.titleEl.enableDisplayMode();
38724 /** This region's title text element
38725 * @type HTMLElement */
38726 this.titleTextEl = this.titleEl.dom.firstChild;
38727 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38729 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38730 this.closeBtn.enableDisplayMode();
38731 this.closeBtn.on("click", this.closeClicked, this);
38732 this.closeBtn.hide();
38734 this.createBody(this.config);
38735 if(this.config.hideWhenEmpty){
38737 this.on("paneladded", this.validateVisibility, this);
38738 this.on("panelremoved", this.validateVisibility, this);
38740 if(this.autoScroll){
38741 this.bodyEl.setStyle("overflow", "auto");
38743 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38745 //if(c.titlebar !== false){
38746 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38747 this.titleEl.hide();
38749 this.titleEl.show();
38750 if(this.config.title){
38751 this.titleTextEl.innerHTML = this.config.title;
38755 if(this.config.collapsed){
38756 this.collapse(true);
38758 if(this.config.hidden){
38762 if (this.unrendered_panels && this.unrendered_panels.length) {
38763 for (var i =0;i< this.unrendered_panels.length; i++) {
38764 this.add(this.unrendered_panels[i]);
38766 this.unrendered_panels = null;
38772 applyConfig : function(c)
38775 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38776 var dh = Roo.DomHelper;
38777 if(c.titlebar !== false){
38778 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38779 this.collapseBtn.on("click", this.collapse, this);
38780 this.collapseBtn.enableDisplayMode();
38782 if(c.showPin === true || this.showPin){
38783 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38784 this.stickBtn.enableDisplayMode();
38785 this.stickBtn.on("click", this.expand, this);
38786 this.stickBtn.hide();
38791 /** This region's collapsed element
38792 * @type Roo.Element */
38795 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38796 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38799 if(c.floatable !== false){
38800 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38801 this.collapsedEl.on("click", this.collapseClick, this);
38804 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38805 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38806 id: "message", unselectable: "on", style:{"float":"left"}});
38807 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38809 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38810 this.expandBtn.on("click", this.expand, this);
38814 if(this.collapseBtn){
38815 this.collapseBtn.setVisible(c.collapsible == true);
38818 this.cmargins = c.cmargins || this.cmargins ||
38819 (this.position == "west" || this.position == "east" ?
38820 {top: 0, left: 2, right:2, bottom: 0} :
38821 {top: 2, left: 0, right:0, bottom: 2});
38823 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38826 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38828 this.autoScroll = c.autoScroll || false;
38833 this.duration = c.duration || .30;
38834 this.slideDuration = c.slideDuration || .45;
38839 * Returns true if this region is currently visible.
38840 * @return {Boolean}
38842 isVisible : function(){
38843 return this.visible;
38847 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38848 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38850 //setCollapsedTitle : function(title){
38851 // title = title || " ";
38852 // if(this.collapsedTitleTextEl){
38853 // this.collapsedTitleTextEl.innerHTML = title;
38857 getBox : function(){
38859 // if(!this.collapsed){
38860 b = this.el.getBox(false, true);
38862 // b = this.collapsedEl.getBox(false, true);
38867 getMargins : function(){
38868 return this.margins;
38869 //return this.collapsed ? this.cmargins : this.margins;
38872 highlight : function(){
38873 this.el.addClass("x-layout-panel-dragover");
38876 unhighlight : function(){
38877 this.el.removeClass("x-layout-panel-dragover");
38880 updateBox : function(box)
38882 if (!this.bodyEl) {
38883 return; // not rendered yet..
38887 if(!this.collapsed){
38888 this.el.dom.style.left = box.x + "px";
38889 this.el.dom.style.top = box.y + "px";
38890 this.updateBody(box.width, box.height);
38892 this.collapsedEl.dom.style.left = box.x + "px";
38893 this.collapsedEl.dom.style.top = box.y + "px";
38894 this.collapsedEl.setSize(box.width, box.height);
38897 this.tabs.autoSizeTabs();
38901 updateBody : function(w, h)
38904 this.el.setWidth(w);
38905 w -= this.el.getBorderWidth("rl");
38906 if(this.config.adjustments){
38907 w += this.config.adjustments[0];
38910 if(h !== null && h > 0){
38911 this.el.setHeight(h);
38912 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38913 h -= this.el.getBorderWidth("tb");
38914 if(this.config.adjustments){
38915 h += this.config.adjustments[1];
38917 this.bodyEl.setHeight(h);
38919 h = this.tabs.syncHeight(h);
38922 if(this.panelSize){
38923 w = w !== null ? w : this.panelSize.width;
38924 h = h !== null ? h : this.panelSize.height;
38926 if(this.activePanel){
38927 var el = this.activePanel.getEl();
38928 w = w !== null ? w : el.getWidth();
38929 h = h !== null ? h : el.getHeight();
38930 this.panelSize = {width: w, height: h};
38931 this.activePanel.setSize(w, h);
38933 if(Roo.isIE && this.tabs){
38934 this.tabs.el.repaint();
38939 * Returns the container element for this region.
38940 * @return {Roo.Element}
38942 getEl : function(){
38947 * Hides this region.
38950 //if(!this.collapsed){
38951 this.el.dom.style.left = "-2000px";
38954 // this.collapsedEl.dom.style.left = "-2000px";
38955 // this.collapsedEl.hide();
38957 this.visible = false;
38958 this.fireEvent("visibilitychange", this, false);
38962 * Shows this region if it was previously hidden.
38965 //if(!this.collapsed){
38968 // this.collapsedEl.show();
38970 this.visible = true;
38971 this.fireEvent("visibilitychange", this, true);
38974 closeClicked : function(){
38975 if(this.activePanel){
38976 this.remove(this.activePanel);
38980 collapseClick : function(e){
38982 e.stopPropagation();
38985 e.stopPropagation();
38991 * Collapses this region.
38992 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38995 collapse : function(skipAnim, skipCheck = false){
38996 if(this.collapsed) {
39000 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39002 this.collapsed = true;
39004 this.split.el.hide();
39006 if(this.config.animate && skipAnim !== true){
39007 this.fireEvent("invalidated", this);
39008 this.animateCollapse();
39010 this.el.setLocation(-20000,-20000);
39012 this.collapsedEl.show();
39013 this.fireEvent("collapsed", this);
39014 this.fireEvent("invalidated", this);
39020 animateCollapse : function(){
39025 * Expands this region if it was previously collapsed.
39026 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39027 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39030 expand : function(e, skipAnim){
39032 e.stopPropagation();
39034 if(!this.collapsed || this.el.hasActiveFx()) {
39038 this.afterSlideIn();
39041 this.collapsed = false;
39042 if(this.config.animate && skipAnim !== true){
39043 this.animateExpand();
39047 this.split.el.show();
39049 this.collapsedEl.setLocation(-2000,-2000);
39050 this.collapsedEl.hide();
39051 this.fireEvent("invalidated", this);
39052 this.fireEvent("expanded", this);
39056 animateExpand : function(){
39060 initTabs : function()
39062 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39064 var ts = new Roo.bootstrap.panel.Tabs({
39065 el: this.bodyEl.dom,
39067 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39068 disableTooltips: this.config.disableTabTips,
39069 toolbar : this.config.toolbar
39072 if(this.config.hideTabs){
39073 ts.stripWrap.setDisplayed(false);
39076 ts.resizeTabs = this.config.resizeTabs === true;
39077 ts.minTabWidth = this.config.minTabWidth || 40;
39078 ts.maxTabWidth = this.config.maxTabWidth || 250;
39079 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39080 ts.monitorResize = false;
39081 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39082 ts.bodyEl.addClass('roo-layout-tabs-body');
39083 this.panels.each(this.initPanelAsTab, this);
39086 initPanelAsTab : function(panel){
39087 var ti = this.tabs.addTab(
39091 this.config.closeOnTab && panel.isClosable(),
39094 if(panel.tabTip !== undefined){
39095 ti.setTooltip(panel.tabTip);
39097 ti.on("activate", function(){
39098 this.setActivePanel(panel);
39101 if(this.config.closeOnTab){
39102 ti.on("beforeclose", function(t, e){
39104 this.remove(panel);
39108 panel.tabItem = ti;
39113 updatePanelTitle : function(panel, title)
39115 if(this.activePanel == panel){
39116 this.updateTitle(title);
39119 var ti = this.tabs.getTab(panel.getEl().id);
39121 if(panel.tabTip !== undefined){
39122 ti.setTooltip(panel.tabTip);
39127 updateTitle : function(title){
39128 if(this.titleTextEl && !this.config.title){
39129 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39133 setActivePanel : function(panel)
39135 panel = this.getPanel(panel);
39136 if(this.activePanel && this.activePanel != panel){
39137 if(this.activePanel.setActiveState(false) === false){
39141 this.activePanel = panel;
39142 panel.setActiveState(true);
39143 if(this.panelSize){
39144 panel.setSize(this.panelSize.width, this.panelSize.height);
39147 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39149 this.updateTitle(panel.getTitle());
39151 this.fireEvent("invalidated", this);
39153 this.fireEvent("panelactivated", this, panel);
39157 * Shows the specified panel.
39158 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39159 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39161 showPanel : function(panel)
39163 panel = this.getPanel(panel);
39166 var tab = this.tabs.getTab(panel.getEl().id);
39167 if(tab.isHidden()){
39168 this.tabs.unhideTab(tab.id);
39172 this.setActivePanel(panel);
39179 * Get the active panel for this region.
39180 * @return {Roo.ContentPanel} The active panel or null
39182 getActivePanel : function(){
39183 return this.activePanel;
39186 validateVisibility : function(){
39187 if(this.panels.getCount() < 1){
39188 this.updateTitle(" ");
39189 this.closeBtn.hide();
39192 if(!this.isVisible()){
39199 * Adds the passed ContentPanel(s) to this region.
39200 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39201 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39203 add : function(panel)
39205 if(arguments.length > 1){
39206 for(var i = 0, len = arguments.length; i < len; i++) {
39207 this.add(arguments[i]);
39212 // if we have not been rendered yet, then we can not really do much of this..
39213 if (!this.bodyEl) {
39214 this.unrendered_panels.push(panel);
39221 if(this.hasPanel(panel)){
39222 this.showPanel(panel);
39225 panel.setRegion(this);
39226 this.panels.add(panel);
39227 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39228 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39229 // and hide them... ???
39230 this.bodyEl.dom.appendChild(panel.getEl().dom);
39231 if(panel.background !== true){
39232 this.setActivePanel(panel);
39234 this.fireEvent("paneladded", this, panel);
39241 this.initPanelAsTab(panel);
39245 if(panel.background !== true){
39246 this.tabs.activate(panel.getEl().id);
39248 this.fireEvent("paneladded", this, panel);
39253 * Hides the tab for the specified panel.
39254 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39256 hidePanel : function(panel){
39257 if(this.tabs && (panel = this.getPanel(panel))){
39258 this.tabs.hideTab(panel.getEl().id);
39263 * Unhides the tab for a previously hidden panel.
39264 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39266 unhidePanel : function(panel){
39267 if(this.tabs && (panel = this.getPanel(panel))){
39268 this.tabs.unhideTab(panel.getEl().id);
39272 clearPanels : function(){
39273 while(this.panels.getCount() > 0){
39274 this.remove(this.panels.first());
39279 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39280 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39281 * @param {Boolean} preservePanel Overrides the config preservePanel option
39282 * @return {Roo.ContentPanel} The panel that was removed
39284 remove : function(panel, preservePanel)
39286 panel = this.getPanel(panel);
39291 this.fireEvent("beforeremove", this, panel, e);
39292 if(e.cancel === true){
39295 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39296 var panelId = panel.getId();
39297 this.panels.removeKey(panelId);
39299 document.body.appendChild(panel.getEl().dom);
39302 this.tabs.removeTab(panel.getEl().id);
39303 }else if (!preservePanel){
39304 this.bodyEl.dom.removeChild(panel.getEl().dom);
39306 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39307 var p = this.panels.first();
39308 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39309 tempEl.appendChild(p.getEl().dom);
39310 this.bodyEl.update("");
39311 this.bodyEl.dom.appendChild(p.getEl().dom);
39313 this.updateTitle(p.getTitle());
39315 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39316 this.setActivePanel(p);
39318 panel.setRegion(null);
39319 if(this.activePanel == panel){
39320 this.activePanel = null;
39322 if(this.config.autoDestroy !== false && preservePanel !== true){
39323 try{panel.destroy();}catch(e){}
39325 this.fireEvent("panelremoved", this, panel);
39330 * Returns the TabPanel component used by this region
39331 * @return {Roo.TabPanel}
39333 getTabs : function(){
39337 createTool : function(parentEl, className){
39338 var btn = Roo.DomHelper.append(parentEl, {
39340 cls: "x-layout-tools-button",
39343 cls: "roo-layout-tools-button-inner " + className,
39347 btn.addClassOnOver("roo-layout-tools-button-over");
39352 * Ext JS Library 1.1.1
39353 * Copyright(c) 2006-2007, Ext JS, LLC.
39355 * Originally Released Under LGPL - original licence link has changed is not relivant.
39358 * <script type="text/javascript">
39364 * @class Roo.SplitLayoutRegion
39365 * @extends Roo.LayoutRegion
39366 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39368 Roo.bootstrap.layout.Split = function(config){
39369 this.cursor = config.cursor;
39370 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39373 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39375 splitTip : "Drag to resize.",
39376 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39377 useSplitTips : false,
39379 applyConfig : function(config){
39380 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39383 onRender : function(ctr,pos) {
39385 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39386 if(!this.config.split){
39391 var splitEl = Roo.DomHelper.append(ctr.dom, {
39393 id: this.el.id + "-split",
39394 cls: "roo-layout-split roo-layout-split-"+this.position,
39397 /** The SplitBar for this region
39398 * @type Roo.SplitBar */
39399 // does not exist yet...
39400 Roo.log([this.position, this.orientation]);
39402 this.split = new Roo.bootstrap.SplitBar({
39403 dragElement : splitEl,
39404 resizingElement: this.el,
39405 orientation : this.orientation
39408 this.split.on("moved", this.onSplitMove, this);
39409 this.split.useShim = this.config.useShim === true;
39410 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39411 if(this.useSplitTips){
39412 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39414 //if(config.collapsible){
39415 // this.split.el.on("dblclick", this.collapse, this);
39418 if(typeof this.config.minSize != "undefined"){
39419 this.split.minSize = this.config.minSize;
39421 if(typeof this.config.maxSize != "undefined"){
39422 this.split.maxSize = this.config.maxSize;
39424 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39425 this.hideSplitter();
39430 getHMaxSize : function(){
39431 var cmax = this.config.maxSize || 10000;
39432 var center = this.mgr.getRegion("center");
39433 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39436 getVMaxSize : function(){
39437 var cmax = this.config.maxSize || 10000;
39438 var center = this.mgr.getRegion("center");
39439 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39442 onSplitMove : function(split, newSize){
39443 this.fireEvent("resized", this, newSize);
39447 * Returns the {@link Roo.SplitBar} for this region.
39448 * @return {Roo.SplitBar}
39450 getSplitBar : function(){
39455 this.hideSplitter();
39456 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39459 hideSplitter : function(){
39461 this.split.el.setLocation(-2000,-2000);
39462 this.split.el.hide();
39468 this.split.el.show();
39470 Roo.bootstrap.layout.Split.superclass.show.call(this);
39473 beforeSlide: function(){
39474 if(Roo.isGecko){// firefox overflow auto bug workaround
39475 this.bodyEl.clip();
39477 this.tabs.bodyEl.clip();
39479 if(this.activePanel){
39480 this.activePanel.getEl().clip();
39482 if(this.activePanel.beforeSlide){
39483 this.activePanel.beforeSlide();
39489 afterSlide : function(){
39490 if(Roo.isGecko){// firefox overflow auto bug workaround
39491 this.bodyEl.unclip();
39493 this.tabs.bodyEl.unclip();
39495 if(this.activePanel){
39496 this.activePanel.getEl().unclip();
39497 if(this.activePanel.afterSlide){
39498 this.activePanel.afterSlide();
39504 initAutoHide : function(){
39505 if(this.autoHide !== false){
39506 if(!this.autoHideHd){
39507 var st = new Roo.util.DelayedTask(this.slideIn, this);
39508 this.autoHideHd = {
39509 "mouseout": function(e){
39510 if(!e.within(this.el, true)){
39514 "mouseover" : function(e){
39520 this.el.on(this.autoHideHd);
39524 clearAutoHide : function(){
39525 if(this.autoHide !== false){
39526 this.el.un("mouseout", this.autoHideHd.mouseout);
39527 this.el.un("mouseover", this.autoHideHd.mouseover);
39531 clearMonitor : function(){
39532 Roo.get(document).un("click", this.slideInIf, this);
39535 // these names are backwards but not changed for compat
39536 slideOut : function(){
39537 if(this.isSlid || this.el.hasActiveFx()){
39540 this.isSlid = true;
39541 if(this.collapseBtn){
39542 this.collapseBtn.hide();
39544 this.closeBtnState = this.closeBtn.getStyle('display');
39545 this.closeBtn.hide();
39547 this.stickBtn.show();
39550 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39551 this.beforeSlide();
39552 this.el.setStyle("z-index", 10001);
39553 this.el.slideIn(this.getSlideAnchor(), {
39554 callback: function(){
39556 this.initAutoHide();
39557 Roo.get(document).on("click", this.slideInIf, this);
39558 this.fireEvent("slideshow", this);
39565 afterSlideIn : function(){
39566 this.clearAutoHide();
39567 this.isSlid = false;
39568 this.clearMonitor();
39569 this.el.setStyle("z-index", "");
39570 if(this.collapseBtn){
39571 this.collapseBtn.show();
39573 this.closeBtn.setStyle('display', this.closeBtnState);
39575 this.stickBtn.hide();
39577 this.fireEvent("slidehide", this);
39580 slideIn : function(cb){
39581 if(!this.isSlid || this.el.hasActiveFx()){
39585 this.isSlid = false;
39586 this.beforeSlide();
39587 this.el.slideOut(this.getSlideAnchor(), {
39588 callback: function(){
39589 this.el.setLeftTop(-10000, -10000);
39591 this.afterSlideIn();
39599 slideInIf : function(e){
39600 if(!e.within(this.el)){
39605 animateCollapse : function(){
39606 this.beforeSlide();
39607 this.el.setStyle("z-index", 20000);
39608 var anchor = this.getSlideAnchor();
39609 this.el.slideOut(anchor, {
39610 callback : function(){
39611 this.el.setStyle("z-index", "");
39612 this.collapsedEl.slideIn(anchor, {duration:.3});
39614 this.el.setLocation(-10000,-10000);
39616 this.fireEvent("collapsed", this);
39623 animateExpand : function(){
39624 this.beforeSlide();
39625 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39626 this.el.setStyle("z-index", 20000);
39627 this.collapsedEl.hide({
39630 this.el.slideIn(this.getSlideAnchor(), {
39631 callback : function(){
39632 this.el.setStyle("z-index", "");
39635 this.split.el.show();
39637 this.fireEvent("invalidated", this);
39638 this.fireEvent("expanded", this);
39666 getAnchor : function(){
39667 return this.anchors[this.position];
39670 getCollapseAnchor : function(){
39671 return this.canchors[this.position];
39674 getSlideAnchor : function(){
39675 return this.sanchors[this.position];
39678 getAlignAdj : function(){
39679 var cm = this.cmargins;
39680 switch(this.position){
39696 getExpandAdj : function(){
39697 var c = this.collapsedEl, cm = this.cmargins;
39698 switch(this.position){
39700 return [-(cm.right+c.getWidth()+cm.left), 0];
39703 return [cm.right+c.getWidth()+cm.left, 0];
39706 return [0, -(cm.top+cm.bottom+c.getHeight())];
39709 return [0, cm.top+cm.bottom+c.getHeight()];
39715 * Ext JS Library 1.1.1
39716 * Copyright(c) 2006-2007, Ext JS, LLC.
39718 * Originally Released Under LGPL - original licence link has changed is not relivant.
39721 * <script type="text/javascript">
39724 * These classes are private internal classes
39726 Roo.bootstrap.layout.Center = function(config){
39727 config.region = "center";
39728 Roo.bootstrap.layout.Region.call(this, config);
39729 this.visible = true;
39730 this.minWidth = config.minWidth || 20;
39731 this.minHeight = config.minHeight || 20;
39734 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39736 // center panel can't be hidden
39740 // center panel can't be hidden
39743 getMinWidth: function(){
39744 return this.minWidth;
39747 getMinHeight: function(){
39748 return this.minHeight;
39762 Roo.bootstrap.layout.North = function(config)
39764 config.region = 'north';
39765 config.cursor = 'n-resize';
39767 Roo.bootstrap.layout.Split.call(this, config);
39771 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39772 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39773 this.split.el.addClass("roo-layout-split-v");
39775 //var size = config.initialSize || config.height;
39776 //if(this.el && typeof size != "undefined"){
39777 // this.el.setHeight(size);
39780 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39782 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39785 onRender : function(ctr, pos)
39787 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39788 var size = this.config.initialSize || this.config.height;
39789 if(this.el && typeof size != "undefined"){
39790 this.el.setHeight(size);
39795 getBox : function(){
39796 if(this.collapsed){
39797 return this.collapsedEl.getBox();
39799 var box = this.el.getBox();
39801 box.height += this.split.el.getHeight();
39806 updateBox : function(box){
39807 if(this.split && !this.collapsed){
39808 box.height -= this.split.el.getHeight();
39809 this.split.el.setLeft(box.x);
39810 this.split.el.setTop(box.y+box.height);
39811 this.split.el.setWidth(box.width);
39813 if(this.collapsed){
39814 this.updateBody(box.width, null);
39816 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39824 Roo.bootstrap.layout.South = function(config){
39825 config.region = 'south';
39826 config.cursor = 's-resize';
39827 Roo.bootstrap.layout.Split.call(this, config);
39829 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39830 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39831 this.split.el.addClass("roo-layout-split-v");
39836 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39837 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39839 onRender : function(ctr, pos)
39841 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39842 var size = this.config.initialSize || this.config.height;
39843 if(this.el && typeof size != "undefined"){
39844 this.el.setHeight(size);
39849 getBox : function(){
39850 if(this.collapsed){
39851 return this.collapsedEl.getBox();
39853 var box = this.el.getBox();
39855 var sh = this.split.el.getHeight();
39862 updateBox : function(box){
39863 if(this.split && !this.collapsed){
39864 var sh = this.split.el.getHeight();
39867 this.split.el.setLeft(box.x);
39868 this.split.el.setTop(box.y-sh);
39869 this.split.el.setWidth(box.width);
39871 if(this.collapsed){
39872 this.updateBody(box.width, null);
39874 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39878 Roo.bootstrap.layout.East = function(config){
39879 config.region = "east";
39880 config.cursor = "e-resize";
39881 Roo.bootstrap.layout.Split.call(this, config);
39883 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39884 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39885 this.split.el.addClass("roo-layout-split-h");
39889 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39890 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39892 onRender : function(ctr, pos)
39894 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39895 var size = this.config.initialSize || this.config.width;
39896 if(this.el && typeof size != "undefined"){
39897 this.el.setWidth(size);
39902 getBox : function(){
39903 if(this.collapsed){
39904 return this.collapsedEl.getBox();
39906 var box = this.el.getBox();
39908 var sw = this.split.el.getWidth();
39915 updateBox : function(box){
39916 if(this.split && !this.collapsed){
39917 var sw = this.split.el.getWidth();
39919 this.split.el.setLeft(box.x);
39920 this.split.el.setTop(box.y);
39921 this.split.el.setHeight(box.height);
39924 if(this.collapsed){
39925 this.updateBody(null, box.height);
39927 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39931 Roo.bootstrap.layout.West = function(config){
39932 config.region = "west";
39933 config.cursor = "w-resize";
39935 Roo.bootstrap.layout.Split.call(this, config);
39937 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39938 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39939 this.split.el.addClass("roo-layout-split-h");
39943 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39944 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39946 onRender: function(ctr, pos)
39948 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39949 var size = this.config.initialSize || this.config.width;
39950 if(typeof size != "undefined"){
39951 this.el.setWidth(size);
39955 getBox : function(){
39956 if(this.collapsed){
39957 return this.collapsedEl.getBox();
39959 var box = this.el.getBox();
39960 if (box.width == 0) {
39961 box.width = this.config.width; // kludge?
39964 box.width += this.split.el.getWidth();
39969 updateBox : function(box){
39970 if(this.split && !this.collapsed){
39971 var sw = this.split.el.getWidth();
39973 this.split.el.setLeft(box.x+box.width);
39974 this.split.el.setTop(box.y);
39975 this.split.el.setHeight(box.height);
39977 if(this.collapsed){
39978 this.updateBody(null, box.height);
39980 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39982 });Roo.namespace("Roo.bootstrap.panel");/*
39984 * Ext JS Library 1.1.1
39985 * Copyright(c) 2006-2007, Ext JS, LLC.
39987 * Originally Released Under LGPL - original licence link has changed is not relivant.
39990 * <script type="text/javascript">
39993 * @class Roo.ContentPanel
39994 * @extends Roo.util.Observable
39995 * A basic ContentPanel element.
39996 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39997 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39998 * @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
39999 * @cfg {Boolean} closable True if the panel can be closed/removed
40000 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40001 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40002 * @cfg {Toolbar} toolbar A toolbar for this panel
40003 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40004 * @cfg {String} title The title for this panel
40005 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40006 * @cfg {String} url Calls {@link #setUrl} with this value
40007 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40008 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40009 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40010 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40011 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40012 * @cfg {Boolean} badges render the badges
40013 * @cfg {String} cls extra classes to use
40014 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40017 * Create a new ContentPanel.
40018 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40019 * @param {String/Object} config A string to set only the title or a config object
40020 * @param {String} content (optional) Set the HTML content for this panel
40021 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40023 Roo.bootstrap.panel.Content = function( config){
40025 this.tpl = config.tpl || false;
40027 var el = config.el;
40028 var content = config.content;
40030 if(config.autoCreate){ // xtype is available if this is called from factory
40033 this.el = Roo.get(el);
40034 if(!this.el && config && config.autoCreate){
40035 if(typeof config.autoCreate == "object"){
40036 if(!config.autoCreate.id){
40037 config.autoCreate.id = config.id||el;
40039 this.el = Roo.DomHelper.append(document.body,
40040 config.autoCreate, true);
40044 cls: (config.cls || '') +
40045 (config.background ? ' bg-' + config.background : '') +
40046 " roo-layout-inactive-content",
40049 if (config.iframe) {
40053 style : 'border: 0px',
40054 src : 'about:blank'
40060 elcfg.html = config.html;
40064 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40065 if (config.iframe) {
40066 this.iframeEl = this.el.select('iframe',true).first();
40071 this.closable = false;
40072 this.loaded = false;
40073 this.active = false;
40076 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40078 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40080 this.wrapEl = this.el; //this.el.wrap();
40082 if (config.toolbar.items) {
40083 ti = config.toolbar.items ;
40084 delete config.toolbar.items ;
40088 this.toolbar.render(this.wrapEl, 'before');
40089 for(var i =0;i < ti.length;i++) {
40090 // Roo.log(['add child', items[i]]);
40091 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40093 this.toolbar.items = nitems;
40094 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40095 delete config.toolbar;
40099 // xtype created footer. - not sure if will work as we normally have to render first..
40100 if (this.footer && !this.footer.el && this.footer.xtype) {
40101 if (!this.wrapEl) {
40102 this.wrapEl = this.el.wrap();
40105 this.footer.container = this.wrapEl.createChild();
40107 this.footer = Roo.factory(this.footer, Roo);
40112 if(typeof config == "string"){
40113 this.title = config;
40115 Roo.apply(this, config);
40119 this.resizeEl = Roo.get(this.resizeEl, true);
40121 this.resizeEl = this.el;
40123 // handle view.xtype
40131 * Fires when this panel is activated.
40132 * @param {Roo.ContentPanel} this
40136 * @event deactivate
40137 * Fires when this panel is activated.
40138 * @param {Roo.ContentPanel} this
40140 "deactivate" : true,
40144 * Fires when this panel is resized if fitToFrame is true.
40145 * @param {Roo.ContentPanel} this
40146 * @param {Number} width The width after any component adjustments
40147 * @param {Number} height The height after any component adjustments
40153 * Fires when this tab is created
40154 * @param {Roo.ContentPanel} this
40165 if(this.autoScroll && !this.iframe){
40166 this.resizeEl.setStyle("overflow", "auto");
40168 // fix randome scrolling
40169 //this.el.on('scroll', function() {
40170 // Roo.log('fix random scolling');
40171 // this.scrollTo('top',0);
40174 content = content || this.content;
40176 this.setContent(content);
40178 if(config && config.url){
40179 this.setUrl(this.url, this.params, this.loadOnce);
40184 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40186 if (this.view && typeof(this.view.xtype) != 'undefined') {
40187 this.view.el = this.el.appendChild(document.createElement("div"));
40188 this.view = Roo.factory(this.view);
40189 this.view.render && this.view.render(false, '');
40193 this.fireEvent('render', this);
40196 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40206 setRegion : function(region){
40207 this.region = region;
40208 this.setActiveClass(region && !this.background);
40212 setActiveClass: function(state)
40215 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40216 this.el.setStyle('position','relative');
40218 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40219 this.el.setStyle('position', 'absolute');
40224 * Returns the toolbar for this Panel if one was configured.
40225 * @return {Roo.Toolbar}
40227 getToolbar : function(){
40228 return this.toolbar;
40231 setActiveState : function(active)
40233 this.active = active;
40234 this.setActiveClass(active);
40236 if(this.fireEvent("deactivate", this) === false){
40241 this.fireEvent("activate", this);
40245 * Updates this panel's element (not for iframe)
40246 * @param {String} content The new content
40247 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40249 setContent : function(content, loadScripts){
40254 this.el.update(content, loadScripts);
40257 ignoreResize : function(w, h){
40258 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40261 this.lastSize = {width: w, height: h};
40266 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40267 * @return {Roo.UpdateManager} The UpdateManager
40269 getUpdateManager : function(){
40273 return this.el.getUpdateManager();
40276 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40277 * Does not work with IFRAME contents
40278 * @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:
40281 url: "your-url.php",
40282 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40283 callback: yourFunction,
40284 scope: yourObject, //(optional scope)
40287 text: "Loading...",
40293 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40294 * 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.
40295 * @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}
40296 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40297 * @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.
40298 * @return {Roo.ContentPanel} this
40306 var um = this.el.getUpdateManager();
40307 um.update.apply(um, arguments);
40313 * 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.
40314 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40315 * @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)
40316 * @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)
40317 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40319 setUrl : function(url, params, loadOnce){
40321 this.iframeEl.dom.src = url;
40325 if(this.refreshDelegate){
40326 this.removeListener("activate", this.refreshDelegate);
40328 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40329 this.on("activate", this.refreshDelegate);
40330 return this.el.getUpdateManager();
40333 _handleRefresh : function(url, params, loadOnce){
40334 if(!loadOnce || !this.loaded){
40335 var updater = this.el.getUpdateManager();
40336 updater.update(url, params, this._setLoaded.createDelegate(this));
40340 _setLoaded : function(){
40341 this.loaded = true;
40345 * Returns this panel's id
40348 getId : function(){
40353 * Returns this panel's element - used by regiosn to add.
40354 * @return {Roo.Element}
40356 getEl : function(){
40357 return this.wrapEl || this.el;
40362 adjustForComponents : function(width, height)
40364 //Roo.log('adjustForComponents ');
40365 if(this.resizeEl != this.el){
40366 width -= this.el.getFrameWidth('lr');
40367 height -= this.el.getFrameWidth('tb');
40370 var te = this.toolbar.getEl();
40371 te.setWidth(width);
40372 height -= te.getHeight();
40375 var te = this.footer.getEl();
40376 te.setWidth(width);
40377 height -= te.getHeight();
40381 if(this.adjustments){
40382 width += this.adjustments[0];
40383 height += this.adjustments[1];
40385 return {"width": width, "height": height};
40388 setSize : function(width, height){
40389 if(this.fitToFrame && !this.ignoreResize(width, height)){
40390 if(this.fitContainer && this.resizeEl != this.el){
40391 this.el.setSize(width, height);
40393 var size = this.adjustForComponents(width, height);
40395 this.iframeEl.setSize(width,height);
40398 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40399 this.fireEvent('resize', this, size.width, size.height);
40406 * Returns this panel's title
40409 getTitle : function(){
40411 if (typeof(this.title) != 'object') {
40416 for (var k in this.title) {
40417 if (!this.title.hasOwnProperty(k)) {
40421 if (k.indexOf('-') >= 0) {
40422 var s = k.split('-');
40423 for (var i = 0; i<s.length; i++) {
40424 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40427 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40434 * Set this panel's title
40435 * @param {String} title
40437 setTitle : function(title){
40438 this.title = title;
40440 this.region.updatePanelTitle(this, title);
40445 * Returns true is this panel was configured to be closable
40446 * @return {Boolean}
40448 isClosable : function(){
40449 return this.closable;
40452 beforeSlide : function(){
40454 this.resizeEl.clip();
40457 afterSlide : function(){
40459 this.resizeEl.unclip();
40463 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40464 * Will fail silently if the {@link #setUrl} method has not been called.
40465 * This does not activate the panel, just updates its content.
40467 refresh : function(){
40468 if(this.refreshDelegate){
40469 this.loaded = false;
40470 this.refreshDelegate();
40475 * Destroys this panel
40477 destroy : function(){
40478 this.el.removeAllListeners();
40479 var tempEl = document.createElement("span");
40480 tempEl.appendChild(this.el.dom);
40481 tempEl.innerHTML = "";
40487 * form - if the content panel contains a form - this is a reference to it.
40488 * @type {Roo.form.Form}
40492 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40493 * This contains a reference to it.
40499 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40509 * @param {Object} cfg Xtype definition of item to add.
40513 getChildContainer: function () {
40514 return this.getEl();
40519 var ret = new Roo.factory(cfg);
40524 if (cfg.xtype.match(/^Form$/)) {
40527 //if (this.footer) {
40528 // el = this.footer.container.insertSibling(false, 'before');
40530 el = this.el.createChild();
40533 this.form = new Roo.form.Form(cfg);
40536 if ( this.form.allItems.length) {
40537 this.form.render(el.dom);
40541 // should only have one of theses..
40542 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40543 // views.. should not be just added - used named prop 'view''
40545 cfg.el = this.el.appendChild(document.createElement("div"));
40548 var ret = new Roo.factory(cfg);
40550 ret.render && ret.render(false, ''); // render blank..
40560 * @class Roo.bootstrap.panel.Grid
40561 * @extends Roo.bootstrap.panel.Content
40563 * Create a new GridPanel.
40564 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40565 * @param {Object} config A the config object
40571 Roo.bootstrap.panel.Grid = function(config)
40575 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40576 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40578 config.el = this.wrapper;
40579 //this.el = this.wrapper;
40581 if (config.container) {
40582 // ctor'ed from a Border/panel.grid
40585 this.wrapper.setStyle("overflow", "hidden");
40586 this.wrapper.addClass('roo-grid-container');
40591 if(config.toolbar){
40592 var tool_el = this.wrapper.createChild();
40593 this.toolbar = Roo.factory(config.toolbar);
40595 if (config.toolbar.items) {
40596 ti = config.toolbar.items ;
40597 delete config.toolbar.items ;
40601 this.toolbar.render(tool_el);
40602 for(var i =0;i < ti.length;i++) {
40603 // Roo.log(['add child', items[i]]);
40604 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40606 this.toolbar.items = nitems;
40608 delete config.toolbar;
40611 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40612 config.grid.scrollBody = true;;
40613 config.grid.monitorWindowResize = false; // turn off autosizing
40614 config.grid.autoHeight = false;
40615 config.grid.autoWidth = false;
40617 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40619 if (config.background) {
40620 // render grid on panel activation (if panel background)
40621 this.on('activate', function(gp) {
40622 if (!gp.grid.rendered) {
40623 gp.grid.render(this.wrapper);
40624 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40629 this.grid.render(this.wrapper);
40630 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40633 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40634 // ??? needed ??? config.el = this.wrapper;
40639 // xtype created footer. - not sure if will work as we normally have to render first..
40640 if (this.footer && !this.footer.el && this.footer.xtype) {
40642 var ctr = this.grid.getView().getFooterPanel(true);
40643 this.footer.dataSource = this.grid.dataSource;
40644 this.footer = Roo.factory(this.footer, Roo);
40645 this.footer.render(ctr);
40655 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40656 getId : function(){
40657 return this.grid.id;
40661 * Returns the grid for this panel
40662 * @return {Roo.bootstrap.Table}
40664 getGrid : function(){
40668 setSize : function(width, height){
40669 if(!this.ignoreResize(width, height)){
40670 var grid = this.grid;
40671 var size = this.adjustForComponents(width, height);
40672 // tfoot is not a footer?
40675 var gridel = grid.getGridEl();
40676 gridel.setSize(size.width, size.height);
40678 var tbd = grid.getGridEl().select('tbody', true).first();
40679 var thd = grid.getGridEl().select('thead',true).first();
40680 var tbf= grid.getGridEl().select('tfoot', true).first();
40683 size.height -= tbf.getHeight();
40686 size.height -= thd.getHeight();
40689 tbd.setSize(size.width, size.height );
40690 // this is for the account management tab -seems to work there.
40691 var thd = grid.getGridEl().select('thead',true).first();
40693 // tbd.setSize(size.width, size.height - thd.getHeight());
40702 beforeSlide : function(){
40703 this.grid.getView().scroller.clip();
40706 afterSlide : function(){
40707 this.grid.getView().scroller.unclip();
40710 destroy : function(){
40711 this.grid.destroy();
40713 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40718 * @class Roo.bootstrap.panel.Nest
40719 * @extends Roo.bootstrap.panel.Content
40721 * Create a new Panel, that can contain a layout.Border.
40724 * @param {Roo.BorderLayout} layout The layout for this panel
40725 * @param {String/Object} config A string to set only the title or a config object
40727 Roo.bootstrap.panel.Nest = function(config)
40729 // construct with only one argument..
40730 /* FIXME - implement nicer consturctors
40731 if (layout.layout) {
40733 layout = config.layout;
40734 delete config.layout;
40736 if (layout.xtype && !layout.getEl) {
40737 // then layout needs constructing..
40738 layout = Roo.factory(layout, Roo);
40742 config.el = config.layout.getEl();
40744 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40746 config.layout.monitorWindowResize = false; // turn off autosizing
40747 this.layout = config.layout;
40748 this.layout.getEl().addClass("roo-layout-nested-layout");
40749 this.layout.parent = this;
40756 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40758 setSize : function(width, height){
40759 if(!this.ignoreResize(width, height)){
40760 var size = this.adjustForComponents(width, height);
40761 var el = this.layout.getEl();
40762 if (size.height < 1) {
40763 el.setWidth(size.width);
40765 el.setSize(size.width, size.height);
40767 var touch = el.dom.offsetWidth;
40768 this.layout.layout();
40769 // ie requires a double layout on the first pass
40770 if(Roo.isIE && !this.initialized){
40771 this.initialized = true;
40772 this.layout.layout();
40777 // activate all subpanels if not currently active..
40779 setActiveState : function(active){
40780 this.active = active;
40781 this.setActiveClass(active);
40784 this.fireEvent("deactivate", this);
40788 this.fireEvent("activate", this);
40789 // not sure if this should happen before or after..
40790 if (!this.layout) {
40791 return; // should not happen..
40794 for (var r in this.layout.regions) {
40795 reg = this.layout.getRegion(r);
40796 if (reg.getActivePanel()) {
40797 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40798 reg.setActivePanel(reg.getActivePanel());
40801 if (!reg.panels.length) {
40804 reg.showPanel(reg.getPanel(0));
40813 * Returns the nested BorderLayout for this panel
40814 * @return {Roo.BorderLayout}
40816 getLayout : function(){
40817 return this.layout;
40821 * Adds a xtype elements to the layout of the nested panel
40825 xtype : 'ContentPanel',
40832 xtype : 'NestedLayoutPanel',
40838 items : [ ... list of content panels or nested layout panels.. ]
40842 * @param {Object} cfg Xtype definition of item to add.
40844 addxtype : function(cfg) {
40845 return this.layout.addxtype(cfg);
40850 * Ext JS Library 1.1.1
40851 * Copyright(c) 2006-2007, Ext JS, LLC.
40853 * Originally Released Under LGPL - original licence link has changed is not relivant.
40856 * <script type="text/javascript">
40859 * @class Roo.TabPanel
40860 * @extends Roo.util.Observable
40861 * A lightweight tab container.
40865 // basic tabs 1, built from existing content
40866 var tabs = new Roo.TabPanel("tabs1");
40867 tabs.addTab("script", "View Script");
40868 tabs.addTab("markup", "View Markup");
40869 tabs.activate("script");
40871 // more advanced tabs, built from javascript
40872 var jtabs = new Roo.TabPanel("jtabs");
40873 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40875 // set up the UpdateManager
40876 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40877 var updater = tab2.getUpdateManager();
40878 updater.setDefaultUrl("ajax1.htm");
40879 tab2.on('activate', updater.refresh, updater, true);
40881 // Use setUrl for Ajax loading
40882 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40883 tab3.setUrl("ajax2.htm", null, true);
40886 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40889 jtabs.activate("jtabs-1");
40892 * Create a new TabPanel.
40893 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40894 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40896 Roo.bootstrap.panel.Tabs = function(config){
40898 * The container element for this TabPanel.
40899 * @type Roo.Element
40901 this.el = Roo.get(config.el);
40904 if(typeof config == "boolean"){
40905 this.tabPosition = config ? "bottom" : "top";
40907 Roo.apply(this, config);
40911 if(this.tabPosition == "bottom"){
40912 // if tabs are at the bottom = create the body first.
40913 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40914 this.el.addClass("roo-tabs-bottom");
40916 // next create the tabs holders
40918 if (this.tabPosition == "west"){
40920 var reg = this.region; // fake it..
40922 if (!reg.mgr.parent) {
40925 reg = reg.mgr.parent.region;
40927 Roo.log("got nest?");
40929 if (reg.mgr.getRegion('west')) {
40930 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40931 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40932 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40933 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40934 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40942 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40943 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40944 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40945 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40950 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40953 // finally - if tabs are at the top, then create the body last..
40954 if(this.tabPosition != "bottom"){
40955 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40956 * @type Roo.Element
40958 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40959 this.el.addClass("roo-tabs-top");
40963 this.bodyEl.setStyle("position", "relative");
40965 this.active = null;
40966 this.activateDelegate = this.activate.createDelegate(this);
40971 * Fires when the active tab changes
40972 * @param {Roo.TabPanel} this
40973 * @param {Roo.TabPanelItem} activePanel The new active tab
40977 * @event beforetabchange
40978 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40979 * @param {Roo.TabPanel} this
40980 * @param {Object} e Set cancel to true on this object to cancel the tab change
40981 * @param {Roo.TabPanelItem} tab The tab being changed to
40983 "beforetabchange" : true
40986 Roo.EventManager.onWindowResize(this.onResize, this);
40987 this.cpad = this.el.getPadding("lr");
40988 this.hiddenCount = 0;
40991 // toolbar on the tabbar support...
40992 if (this.toolbar) {
40993 alert("no toolbar support yet");
40994 this.toolbar = false;
40996 var tcfg = this.toolbar;
40997 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40998 this.toolbar = new Roo.Toolbar(tcfg);
40999 if (Roo.isSafari) {
41000 var tbl = tcfg.container.child('table', true);
41001 tbl.setAttribute('width', '100%');
41009 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41012 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41014 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41016 tabPosition : "top",
41018 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41020 currentTabWidth : 0,
41022 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41026 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41030 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41032 preferredTabWidth : 175,
41034 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41036 resizeTabs : false,
41038 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41040 monitorResize : true,
41042 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41044 toolbar : false, // set by caller..
41046 region : false, /// set by caller
41048 disableTooltips : true, // not used yet...
41051 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41052 * @param {String} id The id of the div to use <b>or create</b>
41053 * @param {String} text The text for the tab
41054 * @param {String} content (optional) Content to put in the TabPanelItem body
41055 * @param {Boolean} closable (optional) True to create a close icon on the tab
41056 * @return {Roo.TabPanelItem} The created TabPanelItem
41058 addTab : function(id, text, content, closable, tpl)
41060 var item = new Roo.bootstrap.panel.TabItem({
41064 closable : closable,
41067 this.addTabItem(item);
41069 item.setContent(content);
41075 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41076 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41077 * @return {Roo.TabPanelItem}
41079 getTab : function(id){
41080 return this.items[id];
41084 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41085 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41087 hideTab : function(id){
41088 var t = this.items[id];
41091 this.hiddenCount++;
41092 this.autoSizeTabs();
41097 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41098 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41100 unhideTab : function(id){
41101 var t = this.items[id];
41103 t.setHidden(false);
41104 this.hiddenCount--;
41105 this.autoSizeTabs();
41110 * Adds an existing {@link Roo.TabPanelItem}.
41111 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41113 addTabItem : function(item)
41115 this.items[item.id] = item;
41116 this.items.push(item);
41117 this.autoSizeTabs();
41118 // if(this.resizeTabs){
41119 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41120 // this.autoSizeTabs();
41122 // item.autoSize();
41127 * Removes a {@link Roo.TabPanelItem}.
41128 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41130 removeTab : function(id){
41131 var items = this.items;
41132 var tab = items[id];
41133 if(!tab) { return; }
41134 var index = items.indexOf(tab);
41135 if(this.active == tab && items.length > 1){
41136 var newTab = this.getNextAvailable(index);
41141 this.stripEl.dom.removeChild(tab.pnode.dom);
41142 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41143 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41145 items.splice(index, 1);
41146 delete this.items[tab.id];
41147 tab.fireEvent("close", tab);
41148 tab.purgeListeners();
41149 this.autoSizeTabs();
41152 getNextAvailable : function(start){
41153 var items = this.items;
41155 // look for a next tab that will slide over to
41156 // replace the one being removed
41157 while(index < items.length){
41158 var item = items[++index];
41159 if(item && !item.isHidden()){
41163 // if one isn't found select the previous tab (on the left)
41166 var item = items[--index];
41167 if(item && !item.isHidden()){
41175 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41176 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41178 disableTab : function(id){
41179 var tab = this.items[id];
41180 if(tab && this.active != tab){
41186 * Enables a {@link Roo.TabPanelItem} that is disabled.
41187 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41189 enableTab : function(id){
41190 var tab = this.items[id];
41195 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41196 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41197 * @return {Roo.TabPanelItem} The TabPanelItem.
41199 activate : function(id)
41201 //Roo.log('activite:' + id);
41203 var tab = this.items[id];
41207 if(tab == this.active || tab.disabled){
41211 this.fireEvent("beforetabchange", this, e, tab);
41212 if(e.cancel !== true && !tab.disabled){
41214 this.active.hide();
41216 this.active = this.items[id];
41217 this.active.show();
41218 this.fireEvent("tabchange", this, this.active);
41224 * Gets the active {@link Roo.TabPanelItem}.
41225 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41227 getActiveTab : function(){
41228 return this.active;
41232 * Updates the tab body element to fit the height of the container element
41233 * for overflow scrolling
41234 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41236 syncHeight : function(targetHeight){
41237 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41238 var bm = this.bodyEl.getMargins();
41239 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41240 this.bodyEl.setHeight(newHeight);
41244 onResize : function(){
41245 if(this.monitorResize){
41246 this.autoSizeTabs();
41251 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41253 beginUpdate : function(){
41254 this.updating = true;
41258 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41260 endUpdate : function(){
41261 this.updating = false;
41262 this.autoSizeTabs();
41266 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41268 autoSizeTabs : function()
41270 var count = this.items.length;
41271 var vcount = count - this.hiddenCount;
41274 this.stripEl.hide();
41276 this.stripEl.show();
41279 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41284 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41285 var availWidth = Math.floor(w / vcount);
41286 var b = this.stripBody;
41287 if(b.getWidth() > w){
41288 var tabs = this.items;
41289 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41290 if(availWidth < this.minTabWidth){
41291 /*if(!this.sleft){ // incomplete scrolling code
41292 this.createScrollButtons();
41295 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41298 if(this.currentTabWidth < this.preferredTabWidth){
41299 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41305 * Returns the number of tabs in this TabPanel.
41308 getCount : function(){
41309 return this.items.length;
41313 * Resizes all the tabs to the passed width
41314 * @param {Number} The new width
41316 setTabWidth : function(width){
41317 this.currentTabWidth = width;
41318 for(var i = 0, len = this.items.length; i < len; i++) {
41319 if(!this.items[i].isHidden()) {
41320 this.items[i].setWidth(width);
41326 * Destroys this TabPanel
41327 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41329 destroy : function(removeEl){
41330 Roo.EventManager.removeResizeListener(this.onResize, this);
41331 for(var i = 0, len = this.items.length; i < len; i++){
41332 this.items[i].purgeListeners();
41334 if(removeEl === true){
41335 this.el.update("");
41340 createStrip : function(container)
41342 var strip = document.createElement("nav");
41343 strip.className = Roo.bootstrap.version == 4 ?
41344 "navbar-light bg-light" :
41345 "navbar navbar-default"; //"x-tabs-wrap";
41346 container.appendChild(strip);
41350 createStripList : function(strip)
41352 // div wrapper for retard IE
41353 // returns the "tr" element.
41354 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41355 //'<div class="x-tabs-strip-wrap">'+
41356 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41357 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41358 return strip.firstChild; //.firstChild.firstChild.firstChild;
41360 createBody : function(container)
41362 var body = document.createElement("div");
41363 Roo.id(body, "tab-body");
41364 //Roo.fly(body).addClass("x-tabs-body");
41365 Roo.fly(body).addClass("tab-content");
41366 container.appendChild(body);
41369 createItemBody :function(bodyEl, id){
41370 var body = Roo.getDom(id);
41372 body = document.createElement("div");
41375 //Roo.fly(body).addClass("x-tabs-item-body");
41376 Roo.fly(body).addClass("tab-pane");
41377 bodyEl.insertBefore(body, bodyEl.firstChild);
41381 createStripElements : function(stripEl, text, closable, tpl)
41383 var td = document.createElement("li"); // was td..
41384 td.className = 'nav-item';
41386 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41389 stripEl.appendChild(td);
41391 td.className = "x-tabs-closable";
41392 if(!this.closeTpl){
41393 this.closeTpl = new Roo.Template(
41394 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41395 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41396 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41399 var el = this.closeTpl.overwrite(td, {"text": text});
41400 var close = el.getElementsByTagName("div")[0];
41401 var inner = el.getElementsByTagName("em")[0];
41402 return {"el": el, "close": close, "inner": inner};
41405 // not sure what this is..
41406 // if(!this.tabTpl){
41407 //this.tabTpl = new Roo.Template(
41408 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41409 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41411 // this.tabTpl = new Roo.Template(
41412 // '<a href="#">' +
41413 // '<span unselectable="on"' +
41414 // (this.disableTooltips ? '' : ' title="{text}"') +
41415 // ' >{text}</span></a>'
41421 var template = tpl || this.tabTpl || false;
41424 template = new Roo.Template(
41425 Roo.bootstrap.version == 4 ?
41427 '<a class="nav-link" href="#" unselectable="on"' +
41428 (this.disableTooltips ? '' : ' title="{text}"') +
41431 '<a class="nav-link" href="#">' +
41432 '<span unselectable="on"' +
41433 (this.disableTooltips ? '' : ' title="{text}"') +
41434 ' >{text}</span></a>'
41439 switch (typeof(template)) {
41443 template = new Roo.Template(template);
41449 var el = template.overwrite(td, {"text": text});
41451 var inner = el.getElementsByTagName("span")[0];
41453 return {"el": el, "inner": inner};
41461 * @class Roo.TabPanelItem
41462 * @extends Roo.util.Observable
41463 * Represents an individual item (tab plus body) in a TabPanel.
41464 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41465 * @param {String} id The id of this TabPanelItem
41466 * @param {String} text The text for the tab of this TabPanelItem
41467 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41469 Roo.bootstrap.panel.TabItem = function(config){
41471 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41472 * @type Roo.TabPanel
41474 this.tabPanel = config.panel;
41476 * The id for this TabPanelItem
41479 this.id = config.id;
41481 this.disabled = false;
41483 this.text = config.text;
41485 this.loaded = false;
41486 this.closable = config.closable;
41489 * The body element for this TabPanelItem.
41490 * @type Roo.Element
41492 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41493 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41494 this.bodyEl.setStyle("display", "block");
41495 this.bodyEl.setStyle("zoom", "1");
41496 //this.hideAction();
41498 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41500 this.el = Roo.get(els.el);
41501 this.inner = Roo.get(els.inner, true);
41502 this.textEl = Roo.bootstrap.version == 4 ?
41503 this.el : Roo.get(this.el.dom.firstChild, true);
41505 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41506 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41509 // this.el.on("mousedown", this.onTabMouseDown, this);
41510 this.el.on("click", this.onTabClick, this);
41512 if(config.closable){
41513 var c = Roo.get(els.close, true);
41514 c.dom.title = this.closeText;
41515 c.addClassOnOver("close-over");
41516 c.on("click", this.closeClick, this);
41522 * Fires when this tab becomes the active tab.
41523 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41524 * @param {Roo.TabPanelItem} this
41528 * @event beforeclose
41529 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41530 * @param {Roo.TabPanelItem} this
41531 * @param {Object} e Set cancel to true on this object to cancel the close.
41533 "beforeclose": true,
41536 * Fires when this tab is closed.
41537 * @param {Roo.TabPanelItem} this
41541 * @event deactivate
41542 * Fires when this tab is no longer the active tab.
41543 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41544 * @param {Roo.TabPanelItem} this
41546 "deactivate" : true
41548 this.hidden = false;
41550 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41553 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41555 purgeListeners : function(){
41556 Roo.util.Observable.prototype.purgeListeners.call(this);
41557 this.el.removeAllListeners();
41560 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41563 this.status_node.addClass("active");
41566 this.tabPanel.stripWrap.repaint();
41568 this.fireEvent("activate", this.tabPanel, this);
41572 * Returns true if this tab is the active tab.
41573 * @return {Boolean}
41575 isActive : function(){
41576 return this.tabPanel.getActiveTab() == this;
41580 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41583 this.status_node.removeClass("active");
41585 this.fireEvent("deactivate", this.tabPanel, this);
41588 hideAction : function(){
41589 this.bodyEl.hide();
41590 this.bodyEl.setStyle("position", "absolute");
41591 this.bodyEl.setLeft("-20000px");
41592 this.bodyEl.setTop("-20000px");
41595 showAction : function(){
41596 this.bodyEl.setStyle("position", "relative");
41597 this.bodyEl.setTop("");
41598 this.bodyEl.setLeft("");
41599 this.bodyEl.show();
41603 * Set the tooltip for the tab.
41604 * @param {String} tooltip The tab's tooltip
41606 setTooltip : function(text){
41607 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41608 this.textEl.dom.qtip = text;
41609 this.textEl.dom.removeAttribute('title');
41611 this.textEl.dom.title = text;
41615 onTabClick : function(e){
41616 e.preventDefault();
41617 this.tabPanel.activate(this.id);
41620 onTabMouseDown : function(e){
41621 e.preventDefault();
41622 this.tabPanel.activate(this.id);
41625 getWidth : function(){
41626 return this.inner.getWidth();
41629 setWidth : function(width){
41630 var iwidth = width - this.linode.getPadding("lr");
41631 this.inner.setWidth(iwidth);
41632 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41633 this.linode.setWidth(width);
41637 * Show or hide the tab
41638 * @param {Boolean} hidden True to hide or false to show.
41640 setHidden : function(hidden){
41641 this.hidden = hidden;
41642 this.linode.setStyle("display", hidden ? "none" : "");
41646 * Returns true if this tab is "hidden"
41647 * @return {Boolean}
41649 isHidden : function(){
41650 return this.hidden;
41654 * Returns the text for this tab
41657 getText : function(){
41661 autoSize : function(){
41662 //this.el.beginMeasure();
41663 this.textEl.setWidth(1);
41665 * #2804 [new] Tabs in Roojs
41666 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41668 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41669 //this.el.endMeasure();
41673 * Sets the text for the tab (Note: this also sets the tooltip text)
41674 * @param {String} text The tab's text and tooltip
41676 setText : function(text){
41678 this.textEl.update(text);
41679 this.setTooltip(text);
41680 //if(!this.tabPanel.resizeTabs){
41681 // this.autoSize();
41685 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41687 activate : function(){
41688 this.tabPanel.activate(this.id);
41692 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41694 disable : function(){
41695 if(this.tabPanel.active != this){
41696 this.disabled = true;
41697 this.status_node.addClass("disabled");
41702 * Enables this TabPanelItem if it was previously disabled.
41704 enable : function(){
41705 this.disabled = false;
41706 this.status_node.removeClass("disabled");
41710 * Sets the content for this TabPanelItem.
41711 * @param {String} content The content
41712 * @param {Boolean} loadScripts true to look for and load scripts
41714 setContent : function(content, loadScripts){
41715 this.bodyEl.update(content, loadScripts);
41719 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41720 * @return {Roo.UpdateManager} The UpdateManager
41722 getUpdateManager : function(){
41723 return this.bodyEl.getUpdateManager();
41727 * Set a URL to be used to load the content for this TabPanelItem.
41728 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41729 * @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)
41730 * @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)
41731 * @return {Roo.UpdateManager} The UpdateManager
41733 setUrl : function(url, params, loadOnce){
41734 if(this.refreshDelegate){
41735 this.un('activate', this.refreshDelegate);
41737 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41738 this.on("activate", this.refreshDelegate);
41739 return this.bodyEl.getUpdateManager();
41743 _handleRefresh : function(url, params, loadOnce){
41744 if(!loadOnce || !this.loaded){
41745 var updater = this.bodyEl.getUpdateManager();
41746 updater.update(url, params, this._setLoaded.createDelegate(this));
41751 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41752 * Will fail silently if the setUrl method has not been called.
41753 * This does not activate the panel, just updates its content.
41755 refresh : function(){
41756 if(this.refreshDelegate){
41757 this.loaded = false;
41758 this.refreshDelegate();
41763 _setLoaded : function(){
41764 this.loaded = true;
41768 closeClick : function(e){
41771 this.fireEvent("beforeclose", this, o);
41772 if(o.cancel !== true){
41773 this.tabPanel.removeTab(this.id);
41777 * The text displayed in the tooltip for the close icon.
41780 closeText : "Close this tab"
41783 * This script refer to:
41784 * Title: International Telephone Input
41785 * Author: Jack O'Connor
41786 * Code version: v12.1.12
41787 * Availability: https://github.com/jackocnr/intl-tel-input.git
41790 Roo.bootstrap.PhoneInputData = function() {
41793 "Afghanistan (افغانستان)",
41798 "Albania (Shqipëri)",
41803 "Algeria (الجزائر)",
41828 "Antigua and Barbuda",
41838 "Armenia (Հայաստան)",
41854 "Austria (Österreich)",
41859 "Azerbaijan (Azərbaycan)",
41869 "Bahrain (البحرين)",
41874 "Bangladesh (বাংলাদেশ)",
41884 "Belarus (Беларусь)",
41889 "Belgium (België)",
41919 "Bosnia and Herzegovina (Босна и Херцеговина)",
41934 "British Indian Ocean Territory",
41939 "British Virgin Islands",
41949 "Bulgaria (България)",
41959 "Burundi (Uburundi)",
41964 "Cambodia (កម្ពុជា)",
41969 "Cameroon (Cameroun)",
41978 ["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"]
41981 "Cape Verde (Kabu Verdi)",
41986 "Caribbean Netherlands",
41997 "Central African Republic (République centrafricaine)",
42017 "Christmas Island",
42023 "Cocos (Keeling) Islands",
42034 "Comoros (جزر القمر)",
42039 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42044 "Congo (Republic) (Congo-Brazzaville)",
42064 "Croatia (Hrvatska)",
42085 "Czech Republic (Česká republika)",
42090 "Denmark (Danmark)",
42105 "Dominican Republic (República Dominicana)",
42109 ["809", "829", "849"]
42127 "Equatorial Guinea (Guinea Ecuatorial)",
42147 "Falkland Islands (Islas Malvinas)",
42152 "Faroe Islands (Føroyar)",
42173 "French Guiana (Guyane française)",
42178 "French Polynesia (Polynésie française)",
42193 "Georgia (საქართველო)",
42198 "Germany (Deutschland)",
42218 "Greenland (Kalaallit Nunaat)",
42255 "Guinea-Bissau (Guiné Bissau)",
42280 "Hungary (Magyarország)",
42285 "Iceland (Ísland)",
42305 "Iraq (العراق)",
42321 "Israel (ישראל)",
42348 "Jordan (الأردن)",
42353 "Kazakhstan (Казахстан)",
42374 "Kuwait (الكويت)",
42379 "Kyrgyzstan (Кыргызстан)",
42389 "Latvia (Latvija)",
42394 "Lebanon (لبنان)",
42409 "Libya (ليبيا)",
42419 "Lithuania (Lietuva)",
42434 "Macedonia (FYROM) (Македонија)",
42439 "Madagascar (Madagasikara)",
42469 "Marshall Islands",
42479 "Mauritania (موريتانيا)",
42484 "Mauritius (Moris)",
42505 "Moldova (Republica Moldova)",
42515 "Mongolia (Монгол)",
42520 "Montenegro (Crna Gora)",
42530 "Morocco (المغرب)",
42536 "Mozambique (Moçambique)",
42541 "Myanmar (Burma) (မြန်မာ)",
42546 "Namibia (Namibië)",
42561 "Netherlands (Nederland)",
42566 "New Caledonia (Nouvelle-Calédonie)",
42601 "North Korea (조선 민주주의 인민 공화국)",
42606 "Northern Mariana Islands",
42622 "Pakistan (پاکستان)",
42632 "Palestine (فلسطين)",
42642 "Papua New Guinea",
42684 "Réunion (La Réunion)",
42690 "Romania (România)",
42706 "Saint Barthélemy",
42717 "Saint Kitts and Nevis",
42727 "Saint Martin (Saint-Martin (partie française))",
42733 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42738 "Saint Vincent and the Grenadines",
42753 "São Tomé and Príncipe (São Tomé e Príncipe)",
42758 "Saudi Arabia (المملكة العربية السعودية)",
42763 "Senegal (Sénégal)",
42793 "Slovakia (Slovensko)",
42798 "Slovenia (Slovenija)",
42808 "Somalia (Soomaaliya)",
42818 "South Korea (대한민국)",
42823 "South Sudan (جنوب السودان)",
42833 "Sri Lanka (ශ්රී ලංකාව)",
42838 "Sudan (السودان)",
42848 "Svalbard and Jan Mayen",
42859 "Sweden (Sverige)",
42864 "Switzerland (Schweiz)",
42869 "Syria (سوريا)",
42914 "Trinidad and Tobago",
42919 "Tunisia (تونس)",
42924 "Turkey (Türkiye)",
42934 "Turks and Caicos Islands",
42944 "U.S. Virgin Islands",
42954 "Ukraine (Україна)",
42959 "United Arab Emirates (الإمارات العربية المتحدة)",
42981 "Uzbekistan (Oʻzbekiston)",
42991 "Vatican City (Città del Vaticano)",
43002 "Vietnam (Việt Nam)",
43007 "Wallis and Futuna (Wallis-et-Futuna)",
43012 "Western Sahara (الصحراء الغربية)",
43018 "Yemen (اليمن)",
43042 * This script refer to:
43043 * Title: International Telephone Input
43044 * Author: Jack O'Connor
43045 * Code version: v12.1.12
43046 * Availability: https://github.com/jackocnr/intl-tel-input.git
43050 * @class Roo.bootstrap.PhoneInput
43051 * @extends Roo.bootstrap.TriggerField
43052 * An input with International dial-code selection
43054 * @cfg {String} defaultDialCode default '+852'
43055 * @cfg {Array} preferedCountries default []
43058 * Create a new PhoneInput.
43059 * @param {Object} config Configuration options
43062 Roo.bootstrap.PhoneInput = function(config) {
43063 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43066 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43068 listWidth: undefined,
43070 selectedClass: 'active',
43072 invalidClass : "has-warning",
43074 validClass: 'has-success',
43076 allowed: '0123456789',
43081 * @cfg {String} defaultDialCode The default dial code when initializing the input
43083 defaultDialCode: '+852',
43086 * @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
43088 preferedCountries: false,
43090 getAutoCreate : function()
43092 var data = Roo.bootstrap.PhoneInputData();
43093 var align = this.labelAlign || this.parentLabelAlign();
43096 this.allCountries = [];
43097 this.dialCodeMapping = [];
43099 for (var i = 0; i < data.length; i++) {
43101 this.allCountries[i] = {
43105 priority: c[3] || 0,
43106 areaCodes: c[4] || null
43108 this.dialCodeMapping[c[2]] = {
43111 priority: c[3] || 0,
43112 areaCodes: c[4] || null
43124 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43125 maxlength: this.max_length,
43126 cls : 'form-control tel-input',
43127 autocomplete: 'new-password'
43130 var hiddenInput = {
43133 cls: 'hidden-tel-input'
43137 hiddenInput.name = this.name;
43140 if (this.disabled) {
43141 input.disabled = true;
43144 var flag_container = {
43161 cls: this.hasFeedback ? 'has-feedback' : '',
43167 cls: 'dial-code-holder',
43174 cls: 'roo-select2-container input-group',
43181 if (this.fieldLabel.length) {
43184 tooltip: 'This field is required'
43190 cls: 'control-label',
43196 html: this.fieldLabel
43199 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43205 if(this.indicatorpos == 'right') {
43206 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43213 if(align == 'left') {
43221 if(this.labelWidth > 12){
43222 label.style = "width: " + this.labelWidth + 'px';
43224 if(this.labelWidth < 13 && this.labelmd == 0){
43225 this.labelmd = this.labelWidth;
43227 if(this.labellg > 0){
43228 label.cls += ' col-lg-' + this.labellg;
43229 input.cls += ' col-lg-' + (12 - this.labellg);
43231 if(this.labelmd > 0){
43232 label.cls += ' col-md-' + this.labelmd;
43233 container.cls += ' col-md-' + (12 - this.labelmd);
43235 if(this.labelsm > 0){
43236 label.cls += ' col-sm-' + this.labelsm;
43237 container.cls += ' col-sm-' + (12 - this.labelsm);
43239 if(this.labelxs > 0){
43240 label.cls += ' col-xs-' + this.labelxs;
43241 container.cls += ' col-xs-' + (12 - this.labelxs);
43251 var settings = this;
43253 ['xs','sm','md','lg'].map(function(size){
43254 if (settings[size]) {
43255 cfg.cls += ' col-' + size + '-' + settings[size];
43259 this.store = new Roo.data.Store({
43260 proxy : new Roo.data.MemoryProxy({}),
43261 reader : new Roo.data.JsonReader({
43272 'name' : 'dialCode',
43276 'name' : 'priority',
43280 'name' : 'areaCodes',
43287 if(!this.preferedCountries) {
43288 this.preferedCountries = [
43295 var p = this.preferedCountries.reverse();
43298 for (var i = 0; i < p.length; i++) {
43299 for (var j = 0; j < this.allCountries.length; j++) {
43300 if(this.allCountries[j].iso2 == p[i]) {
43301 var t = this.allCountries[j];
43302 this.allCountries.splice(j,1);
43303 this.allCountries.unshift(t);
43309 this.store.proxy.data = {
43311 data: this.allCountries
43317 initEvents : function()
43320 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43322 this.indicator = this.indicatorEl();
43323 this.flag = this.flagEl();
43324 this.dialCodeHolder = this.dialCodeHolderEl();
43326 this.trigger = this.el.select('div.flag-box',true).first();
43327 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43332 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43333 _this.list.setWidth(lw);
43336 this.list.on('mouseover', this.onViewOver, this);
43337 this.list.on('mousemove', this.onViewMove, this);
43338 this.inputEl().on("keyup", this.onKeyUp, this);
43339 this.inputEl().on("keypress", this.onKeyPress, this);
43341 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43343 this.view = new Roo.View(this.list, this.tpl, {
43344 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43347 this.view.on('click', this.onViewClick, this);
43348 this.setValue(this.defaultDialCode);
43351 onTriggerClick : function(e)
43353 Roo.log('trigger click');
43358 if(this.isExpanded()){
43360 this.hasFocus = false;
43362 this.store.load({});
43363 this.hasFocus = true;
43368 isExpanded : function()
43370 return this.list.isVisible();
43373 collapse : function()
43375 if(!this.isExpanded()){
43379 Roo.get(document).un('mousedown', this.collapseIf, this);
43380 Roo.get(document).un('mousewheel', this.collapseIf, this);
43381 this.fireEvent('collapse', this);
43385 expand : function()
43389 if(this.isExpanded() || !this.hasFocus){
43393 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43394 this.list.setWidth(lw);
43397 this.restrictHeight();
43399 Roo.get(document).on('mousedown', this.collapseIf, this);
43400 Roo.get(document).on('mousewheel', this.collapseIf, this);
43402 this.fireEvent('expand', this);
43405 restrictHeight : function()
43407 this.list.alignTo(this.inputEl(), this.listAlign);
43408 this.list.alignTo(this.inputEl(), this.listAlign);
43411 onViewOver : function(e, t)
43413 if(this.inKeyMode){
43416 var item = this.view.findItemFromChild(t);
43419 var index = this.view.indexOf(item);
43420 this.select(index, false);
43425 onViewClick : function(view, doFocus, el, e)
43427 var index = this.view.getSelectedIndexes()[0];
43429 var r = this.store.getAt(index);
43432 this.onSelect(r, index);
43434 if(doFocus !== false && !this.blockFocus){
43435 this.inputEl().focus();
43439 onViewMove : function(e, t)
43441 this.inKeyMode = false;
43444 select : function(index, scrollIntoView)
43446 this.selectedIndex = index;
43447 this.view.select(index);
43448 if(scrollIntoView !== false){
43449 var el = this.view.getNode(index);
43451 this.list.scrollChildIntoView(el, false);
43456 createList : function()
43458 this.list = Roo.get(document.body).createChild({
43460 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43461 style: 'display:none'
43464 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43467 collapseIf : function(e)
43469 var in_combo = e.within(this.el);
43470 var in_list = e.within(this.list);
43471 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43473 if (in_combo || in_list || is_list) {
43479 onSelect : function(record, index)
43481 if(this.fireEvent('beforeselect', this, record, index) !== false){
43483 this.setFlagClass(record.data.iso2);
43484 this.setDialCode(record.data.dialCode);
43485 this.hasFocus = false;
43487 this.fireEvent('select', this, record, index);
43491 flagEl : function()
43493 var flag = this.el.select('div.flag',true).first();
43500 dialCodeHolderEl : function()
43502 var d = this.el.select('input.dial-code-holder',true).first();
43509 setDialCode : function(v)
43511 this.dialCodeHolder.dom.value = '+'+v;
43514 setFlagClass : function(n)
43516 this.flag.dom.className = 'flag '+n;
43519 getValue : function()
43521 var v = this.inputEl().getValue();
43522 if(this.dialCodeHolder) {
43523 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43528 setValue : function(v)
43530 var d = this.getDialCode(v);
43532 //invalid dial code
43533 if(v.length == 0 || !d || d.length == 0) {
43535 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43536 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43542 this.setFlagClass(this.dialCodeMapping[d].iso2);
43543 this.setDialCode(d);
43544 this.inputEl().dom.value = v.replace('+'+d,'');
43545 this.hiddenEl().dom.value = this.getValue();
43550 getDialCode : function(v)
43554 if (v.length == 0) {
43555 return this.dialCodeHolder.dom.value;
43559 if (v.charAt(0) != "+") {
43562 var numericChars = "";
43563 for (var i = 1; i < v.length; i++) {
43564 var c = v.charAt(i);
43567 if (this.dialCodeMapping[numericChars]) {
43568 dialCode = v.substr(1, i);
43570 if (numericChars.length == 4) {
43580 this.setValue(this.defaultDialCode);
43584 hiddenEl : function()
43586 return this.el.select('input.hidden-tel-input',true).first();
43589 // after setting val
43590 onKeyUp : function(e){
43591 this.setValue(this.getValue());
43594 onKeyPress : function(e){
43595 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43602 * @class Roo.bootstrap.MoneyField
43603 * @extends Roo.bootstrap.ComboBox
43604 * Bootstrap MoneyField class
43607 * Create a new MoneyField.
43608 * @param {Object} config Configuration options
43611 Roo.bootstrap.MoneyField = function(config) {
43613 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43617 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43620 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43622 allowDecimals : true,
43624 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43626 decimalSeparator : ".",
43628 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43630 decimalPrecision : 0,
43632 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43634 allowNegative : true,
43636 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43640 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43642 minValue : Number.NEGATIVE_INFINITY,
43644 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43646 maxValue : Number.MAX_VALUE,
43648 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43650 minText : "The minimum value for this field is {0}",
43652 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43654 maxText : "The maximum value for this field is {0}",
43656 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43657 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43659 nanText : "{0} is not a valid number",
43661 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43665 * @cfg {String} defaults currency of the MoneyField
43666 * value should be in lkey
43668 defaultCurrency : false,
43670 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43672 thousandsDelimiter : false,
43674 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43685 getAutoCreate : function()
43687 var align = this.labelAlign || this.parentLabelAlign();
43699 cls : 'form-control roo-money-amount-input',
43700 autocomplete: 'new-password'
43703 var hiddenInput = {
43707 cls: 'hidden-number-input'
43710 if(this.max_length) {
43711 input.maxlength = this.max_length;
43715 hiddenInput.name = this.name;
43718 if (this.disabled) {
43719 input.disabled = true;
43722 var clg = 12 - this.inputlg;
43723 var cmd = 12 - this.inputmd;
43724 var csm = 12 - this.inputsm;
43725 var cxs = 12 - this.inputxs;
43729 cls : 'row roo-money-field',
43733 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43737 cls: 'roo-select2-container input-group',
43741 cls : 'form-control roo-money-currency-input',
43742 autocomplete: 'new-password',
43744 name : this.currencyName
43748 cls : 'input-group-addon',
43762 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43766 cls: this.hasFeedback ? 'has-feedback' : '',
43777 if (this.fieldLabel.length) {
43780 tooltip: 'This field is required'
43786 cls: 'control-label',
43792 html: this.fieldLabel
43795 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43801 if(this.indicatorpos == 'right') {
43802 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43809 if(align == 'left') {
43817 if(this.labelWidth > 12){
43818 label.style = "width: " + this.labelWidth + 'px';
43820 if(this.labelWidth < 13 && this.labelmd == 0){
43821 this.labelmd = this.labelWidth;
43823 if(this.labellg > 0){
43824 label.cls += ' col-lg-' + this.labellg;
43825 input.cls += ' col-lg-' + (12 - this.labellg);
43827 if(this.labelmd > 0){
43828 label.cls += ' col-md-' + this.labelmd;
43829 container.cls += ' col-md-' + (12 - this.labelmd);
43831 if(this.labelsm > 0){
43832 label.cls += ' col-sm-' + this.labelsm;
43833 container.cls += ' col-sm-' + (12 - this.labelsm);
43835 if(this.labelxs > 0){
43836 label.cls += ' col-xs-' + this.labelxs;
43837 container.cls += ' col-xs-' + (12 - this.labelxs);
43848 var settings = this;
43850 ['xs','sm','md','lg'].map(function(size){
43851 if (settings[size]) {
43852 cfg.cls += ' col-' + size + '-' + settings[size];
43859 initEvents : function()
43861 this.indicator = this.indicatorEl();
43863 this.initCurrencyEvent();
43865 this.initNumberEvent();
43868 initCurrencyEvent : function()
43871 throw "can not find store for combo";
43874 this.store = Roo.factory(this.store, Roo.data);
43875 this.store.parent = this;
43879 this.triggerEl = this.el.select('.input-group-addon', true).first();
43881 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43886 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43887 _this.list.setWidth(lw);
43890 this.list.on('mouseover', this.onViewOver, this);
43891 this.list.on('mousemove', this.onViewMove, this);
43892 this.list.on('scroll', this.onViewScroll, this);
43895 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43898 this.view = new Roo.View(this.list, this.tpl, {
43899 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43902 this.view.on('click', this.onViewClick, this);
43904 this.store.on('beforeload', this.onBeforeLoad, this);
43905 this.store.on('load', this.onLoad, this);
43906 this.store.on('loadexception', this.onLoadException, this);
43908 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43909 "up" : function(e){
43910 this.inKeyMode = true;
43914 "down" : function(e){
43915 if(!this.isExpanded()){
43916 this.onTriggerClick();
43918 this.inKeyMode = true;
43923 "enter" : function(e){
43926 if(this.fireEvent("specialkey", this, e)){
43927 this.onViewClick(false);
43933 "esc" : function(e){
43937 "tab" : function(e){
43940 if(this.fireEvent("specialkey", this, e)){
43941 this.onViewClick(false);
43949 doRelay : function(foo, bar, hname){
43950 if(hname == 'down' || this.scope.isExpanded()){
43951 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43959 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43963 initNumberEvent : function(e)
43965 this.inputEl().on("keydown" , this.fireKey, this);
43966 this.inputEl().on("focus", this.onFocus, this);
43967 this.inputEl().on("blur", this.onBlur, this);
43969 this.inputEl().relayEvent('keyup', this);
43971 if(this.indicator){
43972 this.indicator.addClass('invisible');
43975 this.originalValue = this.getValue();
43977 if(this.validationEvent == 'keyup'){
43978 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43979 this.inputEl().on('keyup', this.filterValidation, this);
43981 else if(this.validationEvent !== false){
43982 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43985 if(this.selectOnFocus){
43986 this.on("focus", this.preFocus, this);
43989 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43990 this.inputEl().on("keypress", this.filterKeys, this);
43992 this.inputEl().relayEvent('keypress', this);
43995 var allowed = "0123456789";
43997 if(this.allowDecimals){
43998 allowed += this.decimalSeparator;
44001 if(this.allowNegative){
44005 if(this.thousandsDelimiter) {
44009 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44011 var keyPress = function(e){
44013 var k = e.getKey();
44015 var c = e.getCharCode();
44018 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44019 allowed.indexOf(String.fromCharCode(c)) === -1
44025 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44029 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44034 this.inputEl().on("keypress", keyPress, this);
44038 onTriggerClick : function(e)
44045 this.loadNext = false;
44047 if(this.isExpanded()){
44052 this.hasFocus = true;
44054 if(this.triggerAction == 'all') {
44055 this.doQuery(this.allQuery, true);
44059 this.doQuery(this.getRawValue());
44062 getCurrency : function()
44064 var v = this.currencyEl().getValue();
44069 restrictHeight : function()
44071 this.list.alignTo(this.currencyEl(), this.listAlign);
44072 this.list.alignTo(this.currencyEl(), this.listAlign);
44075 onViewClick : function(view, doFocus, el, e)
44077 var index = this.view.getSelectedIndexes()[0];
44079 var r = this.store.getAt(index);
44082 this.onSelect(r, index);
44086 onSelect : function(record, index){
44088 if(this.fireEvent('beforeselect', this, record, index) !== false){
44090 this.setFromCurrencyData(index > -1 ? record.data : false);
44094 this.fireEvent('select', this, record, index);
44098 setFromCurrencyData : function(o)
44102 this.lastCurrency = o;
44104 if (this.currencyField) {
44105 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44107 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44110 this.lastSelectionText = currency;
44112 //setting default currency
44113 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44114 this.setCurrency(this.defaultCurrency);
44118 this.setCurrency(currency);
44121 setFromData : function(o)
44125 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44127 this.setFromCurrencyData(c);
44132 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44134 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44137 this.setValue(value);
44141 setCurrency : function(v)
44143 this.currencyValue = v;
44146 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44151 setValue : function(v)
44153 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44159 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44161 this.inputEl().dom.value = (v == '') ? '' :
44162 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44164 if(!this.allowZero && v === '0') {
44165 this.hiddenEl().dom.value = '';
44166 this.inputEl().dom.value = '';
44173 getRawValue : function()
44175 var v = this.inputEl().getValue();
44180 getValue : function()
44182 return this.fixPrecision(this.parseValue(this.getRawValue()));
44185 parseValue : function(value)
44187 if(this.thousandsDelimiter) {
44189 r = new RegExp(",", "g");
44190 value = value.replace(r, "");
44193 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44194 return isNaN(value) ? '' : value;
44198 fixPrecision : function(value)
44200 if(this.thousandsDelimiter) {
44202 r = new RegExp(",", "g");
44203 value = value.replace(r, "");
44206 var nan = isNaN(value);
44208 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44209 return nan ? '' : value;
44211 return parseFloat(value).toFixed(this.decimalPrecision);
44214 decimalPrecisionFcn : function(v)
44216 return Math.floor(v);
44219 validateValue : function(value)
44221 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44225 var num = this.parseValue(value);
44228 this.markInvalid(String.format(this.nanText, value));
44232 if(num < this.minValue){
44233 this.markInvalid(String.format(this.minText, this.minValue));
44237 if(num > this.maxValue){
44238 this.markInvalid(String.format(this.maxText, this.maxValue));
44245 validate : function()
44247 if(this.disabled || this.allowBlank){
44252 var currency = this.getCurrency();
44254 if(this.validateValue(this.getRawValue()) && currency.length){
44259 this.markInvalid();
44263 getName: function()
44268 beforeBlur : function()
44274 var v = this.parseValue(this.getRawValue());
44281 onBlur : function()
44285 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44286 //this.el.removeClass(this.focusClass);
44289 this.hasFocus = false;
44291 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44295 var v = this.getValue();
44297 if(String(v) !== String(this.startValue)){
44298 this.fireEvent('change', this, v, this.startValue);
44301 this.fireEvent("blur", this);
44304 inputEl : function()
44306 return this.el.select('.roo-money-amount-input', true).first();
44309 currencyEl : function()
44311 return this.el.select('.roo-money-currency-input', true).first();
44314 hiddenEl : function()
44316 return this.el.select('input.hidden-number-input',true).first();
44320 * @class Roo.bootstrap.BezierSignature
44321 * @extends Roo.bootstrap.Component
44322 * Bootstrap BezierSignature class
44323 * This script refer to:
44324 * Title: Signature Pad
44326 * Availability: https://github.com/szimek/signature_pad
44329 * Create a new BezierSignature
44330 * @param {Object} config The config object
44333 Roo.bootstrap.BezierSignature = function(config){
44334 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44340 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44347 mouse_btn_down: true,
44350 * @cfg {int} canvas height
44352 canvas_height: '200px',
44355 * @cfg {float|function} Radius of a single dot.
44360 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44365 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44370 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44375 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44380 * @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.
44382 bg_color: 'rgba(0, 0, 0, 0)',
44385 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44387 dot_color: 'black',
44390 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44392 velocity_filter_weight: 0.7,
44395 * @cfg {function} Callback when stroke begin.
44400 * @cfg {function} Callback when stroke end.
44404 getAutoCreate : function()
44406 var cls = 'roo-signature column';
44409 cls += ' ' + this.cls;
44419 for(var i = 0; i < col_sizes.length; i++) {
44420 if(this[col_sizes[i]]) {
44421 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44431 cls: 'roo-signature-body',
44435 cls: 'roo-signature-body-canvas',
44436 height: this.canvas_height,
44437 width: this.canvas_width
44444 style: 'display: none'
44452 initEvents: function()
44454 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44456 var canvas = this.canvasEl();
44458 // mouse && touch event swapping...
44459 canvas.dom.style.touchAction = 'none';
44460 canvas.dom.style.msTouchAction = 'none';
44462 this.mouse_btn_down = false;
44463 canvas.on('mousedown', this._handleMouseDown, this);
44464 canvas.on('mousemove', this._handleMouseMove, this);
44465 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44467 if (window.PointerEvent) {
44468 canvas.on('pointerdown', this._handleMouseDown, this);
44469 canvas.on('pointermove', this._handleMouseMove, this);
44470 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44473 if ('ontouchstart' in window) {
44474 canvas.on('touchstart', this._handleTouchStart, this);
44475 canvas.on('touchmove', this._handleTouchMove, this);
44476 canvas.on('touchend', this._handleTouchEnd, this);
44479 Roo.EventManager.onWindowResize(this.resize, this, true);
44481 // file input event
44482 this.fileEl().on('change', this.uploadImage, this);
44489 resize: function(){
44491 var canvas = this.canvasEl().dom;
44492 var ctx = this.canvasElCtx();
44493 var img_data = false;
44495 if(canvas.width > 0) {
44496 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44498 // setting canvas width will clean img data
44501 var style = window.getComputedStyle ?
44502 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44504 var padding_left = parseInt(style.paddingLeft) || 0;
44505 var padding_right = parseInt(style.paddingRight) || 0;
44507 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44510 ctx.putImageData(img_data, 0, 0);
44514 _handleMouseDown: function(e)
44516 if (e.browserEvent.which === 1) {
44517 this.mouse_btn_down = true;
44518 this.strokeBegin(e);
44522 _handleMouseMove: function (e)
44524 if (this.mouse_btn_down) {
44525 this.strokeMoveUpdate(e);
44529 _handleMouseUp: function (e)
44531 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44532 this.mouse_btn_down = false;
44537 _handleTouchStart: function (e) {
44539 e.preventDefault();
44540 if (e.browserEvent.targetTouches.length === 1) {
44541 // var touch = e.browserEvent.changedTouches[0];
44542 // this.strokeBegin(touch);
44544 this.strokeBegin(e); // assume e catching the correct xy...
44548 _handleTouchMove: function (e) {
44549 e.preventDefault();
44550 // var touch = event.targetTouches[0];
44551 // _this._strokeMoveUpdate(touch);
44552 this.strokeMoveUpdate(e);
44555 _handleTouchEnd: function (e) {
44556 var wasCanvasTouched = e.target === this.canvasEl().dom;
44557 if (wasCanvasTouched) {
44558 e.preventDefault();
44559 // var touch = event.changedTouches[0];
44560 // _this._strokeEnd(touch);
44565 reset: function () {
44566 this._lastPoints = [];
44567 this._lastVelocity = 0;
44568 this._lastWidth = (this.min_width + this.max_width) / 2;
44569 this.canvasElCtx().fillStyle = this.dot_color;
44572 strokeMoveUpdate: function(e)
44574 this.strokeUpdate(e);
44576 if (this.throttle) {
44577 this.throttleStroke(this.strokeUpdate, this.throttle);
44580 this.strokeUpdate(e);
44584 strokeBegin: function(e)
44586 var newPointGroup = {
44587 color: this.dot_color,
44591 if (typeof this.onBegin === 'function') {
44595 this.curve_data.push(newPointGroup);
44597 this.strokeUpdate(e);
44600 strokeUpdate: function(e)
44602 var rect = this.canvasEl().dom.getBoundingClientRect();
44603 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44604 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44605 var lastPoints = lastPointGroup.points;
44606 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44607 var isLastPointTooClose = lastPoint
44608 ? point.distanceTo(lastPoint) <= this.min_distance
44610 var color = lastPointGroup.color;
44611 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44612 var curve = this.addPoint(point);
44614 this.drawDot({color: color, point: point});
44617 this.drawCurve({color: color, curve: curve});
44627 strokeEnd: function(e)
44629 this.strokeUpdate(e);
44630 if (typeof this.onEnd === 'function') {
44635 addPoint: function (point) {
44636 var _lastPoints = this._lastPoints;
44637 _lastPoints.push(point);
44638 if (_lastPoints.length > 2) {
44639 if (_lastPoints.length === 3) {
44640 _lastPoints.unshift(_lastPoints[0]);
44642 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44643 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44644 _lastPoints.shift();
44650 calculateCurveWidths: function (startPoint, endPoint) {
44651 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44652 (1 - this.velocity_filter_weight) * this._lastVelocity;
44654 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44657 start: this._lastWidth
44660 this._lastVelocity = velocity;
44661 this._lastWidth = newWidth;
44665 drawDot: function (_a) {
44666 var color = _a.color, point = _a.point;
44667 var ctx = this.canvasElCtx();
44668 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44670 this.drawCurveSegment(point.x, point.y, width);
44672 ctx.fillStyle = color;
44676 drawCurve: function (_a) {
44677 var color = _a.color, curve = _a.curve;
44678 var ctx = this.canvasElCtx();
44679 var widthDelta = curve.endWidth - curve.startWidth;
44680 var drawSteps = Math.floor(curve.length()) * 2;
44682 ctx.fillStyle = color;
44683 for (var i = 0; i < drawSteps; i += 1) {
44684 var t = i / drawSteps;
44690 var x = uuu * curve.startPoint.x;
44691 x += 3 * uu * t * curve.control1.x;
44692 x += 3 * u * tt * curve.control2.x;
44693 x += ttt * curve.endPoint.x;
44694 var y = uuu * curve.startPoint.y;
44695 y += 3 * uu * t * curve.control1.y;
44696 y += 3 * u * tt * curve.control2.y;
44697 y += ttt * curve.endPoint.y;
44698 var width = curve.startWidth + ttt * widthDelta;
44699 this.drawCurveSegment(x, y, width);
44705 drawCurveSegment: function (x, y, width) {
44706 var ctx = this.canvasElCtx();
44708 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44709 this.is_empty = false;
44714 var ctx = this.canvasElCtx();
44715 var canvas = this.canvasEl().dom;
44716 ctx.fillStyle = this.bg_color;
44717 ctx.clearRect(0, 0, canvas.width, canvas.height);
44718 ctx.fillRect(0, 0, canvas.width, canvas.height);
44719 this.curve_data = [];
44721 this.is_empty = true;
44726 return this.el.select('input',true).first();
44729 canvasEl: function()
44731 return this.el.select('canvas',true).first();
44734 canvasElCtx: function()
44736 return this.el.select('canvas',true).first().dom.getContext('2d');
44739 getImage: function(type)
44741 if(this.is_empty) {
44746 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44749 drawFromImage: function(img_src)
44751 var img = new Image();
44753 img.onload = function(){
44754 this.canvasElCtx().drawImage(img, 0, 0);
44759 this.is_empty = false;
44762 selectImage: function()
44764 this.fileEl().dom.click();
44767 uploadImage: function(e)
44769 var reader = new FileReader();
44771 reader.onload = function(e){
44772 var img = new Image();
44773 img.onload = function(){
44775 this.canvasElCtx().drawImage(img, 0, 0);
44777 img.src = e.target.result;
44780 reader.readAsDataURL(e.target.files[0]);
44783 // Bezier Point Constructor
44784 Point: (function () {
44785 function Point(x, y, time) {
44788 this.time = time || Date.now();
44790 Point.prototype.distanceTo = function (start) {
44791 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44793 Point.prototype.equals = function (other) {
44794 return this.x === other.x && this.y === other.y && this.time === other.time;
44796 Point.prototype.velocityFrom = function (start) {
44797 return this.time !== start.time
44798 ? this.distanceTo(start) / (this.time - start.time)
44805 // Bezier Constructor
44806 Bezier: (function () {
44807 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44808 this.startPoint = startPoint;
44809 this.control2 = control2;
44810 this.control1 = control1;
44811 this.endPoint = endPoint;
44812 this.startWidth = startWidth;
44813 this.endWidth = endWidth;
44815 Bezier.fromPoints = function (points, widths, scope) {
44816 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44817 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44818 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44820 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44821 var dx1 = s1.x - s2.x;
44822 var dy1 = s1.y - s2.y;
44823 var dx2 = s2.x - s3.x;
44824 var dy2 = s2.y - s3.y;
44825 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44826 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44827 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44828 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44829 var dxm = m1.x - m2.x;
44830 var dym = m1.y - m2.y;
44831 var k = l2 / (l1 + l2);
44832 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44833 var tx = s2.x - cm.x;
44834 var ty = s2.y - cm.y;
44836 c1: new scope.Point(m1.x + tx, m1.y + ty),
44837 c2: new scope.Point(m2.x + tx, m2.y + ty)
44840 Bezier.prototype.length = function () {
44845 for (var i = 0; i <= steps; i += 1) {
44847 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44848 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44850 var xdiff = cx - px;
44851 var ydiff = cy - py;
44852 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44859 Bezier.prototype.point = function (t, start, c1, c2, end) {
44860 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44861 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44862 + (3.0 * c2 * (1.0 - t) * t * t)
44863 + (end * t * t * t);
44868 throttleStroke: function(fn, wait) {
44869 if (wait === void 0) { wait = 250; }
44871 var timeout = null;
44875 var later = function () {
44876 previous = Date.now();
44878 result = fn.apply(storedContext, storedArgs);
44880 storedContext = null;
44884 return function wrapper() {
44886 for (var _i = 0; _i < arguments.length; _i++) {
44887 args[_i] = arguments[_i];
44889 var now = Date.now();
44890 var remaining = wait - (now - previous);
44891 storedContext = this;
44893 if (remaining <= 0 || remaining > wait) {
44895 clearTimeout(timeout);
44899 result = fn.apply(storedContext, storedArgs);
44901 storedContext = null;
44905 else if (!timeout) {
44906 timeout = window.setTimeout(later, remaining);