2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8458 onDblClick : function(e,el)
8460 var cell = Roo.get(el);
8462 if(!cell || (!this.cellSelection && !this.rowSelection)){
8466 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467 cell = cell.findParent('td', false, true);
8470 if(!cell || typeof(cell) == 'undefined'){
8474 var row = cell.findParent('tr', false, true);
8476 if(!row || typeof(row) == 'undefined'){
8480 var cellIndex = cell.dom.cellIndex;
8481 var rowIndex = this.getRowIndex(row);
8483 if(this.cellSelection){
8484 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8487 if(this.rowSelection){
8488 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8492 sort : function(e,el)
8494 var col = Roo.get(el);
8496 if(!col.hasClass('sortable')){
8500 var sort = col.attr('sort');
8503 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8507 this.store.sortInfo = {field : sort, direction : dir};
8510 Roo.log("calling footer first");
8511 this.footer.onClick('first');
8514 this.store.load({ params : { start : 0 } });
8518 renderHeader : function()
8526 this.totalWidth = 0;
8528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530 var config = cm.config[i];
8534 cls : 'x-hcol-' + i,
8537 html: cm.getColumnHeader(i)
8540 var tooltip = cm.getColumnTooltip(i);
8542 c.tooltip = tooltip;
8548 if(typeof(config.sortable) != 'undefined' && config.sortable){
8550 c.html = '<i class="fa"></i>' + c.html;
8553 // could use BS4 hidden-..-down
8555 if(typeof(config.lgHeader) != 'undefined'){
8556 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8559 if(typeof(config.mdHeader) != 'undefined'){
8560 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8563 if(typeof(config.smHeader) != 'undefined'){
8564 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8567 if(typeof(config.xsHeader) != 'undefined'){
8568 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8575 if(typeof(config.tooltip) != 'undefined'){
8576 c.tooltip = config.tooltip;
8579 if(typeof(config.colspan) != 'undefined'){
8580 c.colspan = config.colspan;
8583 if(typeof(config.hidden) != 'undefined' && config.hidden){
8584 c.style += ' display:none;';
8587 if(typeof(config.dataIndex) != 'undefined'){
8588 c.sort = config.dataIndex;
8593 if(typeof(config.align) != 'undefined' && config.align.length){
8594 c.style += ' text-align:' + config.align + ';';
8597 if(typeof(config.width) != 'undefined'){
8598 c.style += ' width:' + config.width + 'px;';
8599 this.totalWidth += config.width;
8601 this.totalWidth += 100; // assume minimum of 100 per column?
8604 if(typeof(config.cls) != 'undefined'){
8605 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8608 ['xs','sm','md','lg'].map(function(size){
8610 if(typeof(config[size]) == 'undefined'){
8614 if (!config[size]) { // 0 = hidden
8615 // BS 4 '0' is treated as hide that column and below.
8616 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8620 c.cls += ' col-' + size + '-' + config[size] + (
8621 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8633 renderBody : function()
8643 colspan : this.cm.getColumnCount()
8653 renderFooter : function()
8663 colspan : this.cm.getColumnCount()
8677 // Roo.log('ds onload');
8682 var ds = this.store;
8684 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8685 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8686 if (_this.store.sortInfo) {
8688 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8689 e.select('i', true).addClass(['fa-arrow-up']);
8692 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8693 e.select('i', true).addClass(['fa-arrow-down']);
8698 var tbody = this.mainBody;
8700 if(ds.getCount() > 0){
8701 ds.data.each(function(d,rowIndex){
8702 var row = this.renderRow(cm, ds, rowIndex);
8704 tbody.createChild(row);
8708 if(row.cellObjects.length){
8709 Roo.each(row.cellObjects, function(r){
8710 _this.renderCellObject(r);
8717 var tfoot = this.el.select('tfoot', true).first();
8719 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8721 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8723 var total = this.ds.getTotalCount();
8725 if(this.footer.pageSize < total){
8726 this.mainFoot.show();
8730 Roo.each(this.el.select('tbody td', true).elements, function(e){
8731 e.on('mouseover', _this.onMouseover, _this);
8734 Roo.each(this.el.select('tbody td', true).elements, function(e){
8735 e.on('mouseout', _this.onMouseout, _this);
8737 this.fireEvent('rowsrendered', this);
8743 onUpdate : function(ds,record)
8745 this.refreshRow(record);
8749 onRemove : function(ds, record, index, isUpdate){
8750 if(isUpdate !== true){
8751 this.fireEvent("beforerowremoved", this, index, record);
8753 var bt = this.mainBody.dom;
8755 var rows = this.el.select('tbody > tr', true).elements;
8757 if(typeof(rows[index]) != 'undefined'){
8758 bt.removeChild(rows[index].dom);
8761 // if(bt.rows[index]){
8762 // bt.removeChild(bt.rows[index]);
8765 if(isUpdate !== true){
8766 //this.stripeRows(index);
8767 //this.syncRowHeights(index, index);
8769 this.fireEvent("rowremoved", this, index, record);
8773 onAdd : function(ds, records, rowIndex)
8775 //Roo.log('on Add called');
8776 // - note this does not handle multiple adding very well..
8777 var bt = this.mainBody.dom;
8778 for (var i =0 ; i < records.length;i++) {
8779 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8780 //Roo.log(records[i]);
8781 //Roo.log(this.store.getAt(rowIndex+i));
8782 this.insertRow(this.store, rowIndex + i, false);
8789 refreshRow : function(record){
8790 var ds = this.store, index;
8791 if(typeof record == 'number'){
8793 record = ds.getAt(index);
8795 index = ds.indexOf(record);
8797 return; // should not happen - but seems to
8800 this.insertRow(ds, index, true);
8802 this.onRemove(ds, record, index+1, true);
8804 //this.syncRowHeights(index, index);
8806 this.fireEvent("rowupdated", this, index, record);
8809 insertRow : function(dm, rowIndex, isUpdate){
8812 this.fireEvent("beforerowsinserted", this, rowIndex);
8814 //var s = this.getScrollState();
8815 var row = this.renderRow(this.cm, this.store, rowIndex);
8816 // insert before rowIndex..
8817 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8821 if(row.cellObjects.length){
8822 Roo.each(row.cellObjects, function(r){
8823 _this.renderCellObject(r);
8828 this.fireEvent("rowsinserted", this, rowIndex);
8829 //this.syncRowHeights(firstRow, lastRow);
8830 //this.stripeRows(firstRow);
8837 getRowDom : function(rowIndex)
8839 var rows = this.el.select('tbody > tr', true).elements;
8841 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8844 // returns the object tree for a tr..
8847 renderRow : function(cm, ds, rowIndex)
8849 var d = ds.getAt(rowIndex);
8853 cls : 'x-row-' + rowIndex,
8857 var cellObjects = [];
8859 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8860 var config = cm.config[i];
8862 var renderer = cm.getRenderer(i);
8866 if(typeof(renderer) !== 'undefined'){
8867 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8869 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8870 // and are rendered into the cells after the row is rendered - using the id for the element.
8872 if(typeof(value) === 'object'){
8882 rowIndex : rowIndex,
8887 this.fireEvent('rowclass', this, rowcfg);
8891 // this might end up displaying HTML?
8892 // this is too messy... - better to only do it on columsn you know are going to be too long
8893 //tooltip : (typeof(value) === 'object') ? '' : value,
8894 cls : rowcfg.rowClass + ' x-col-' + i,
8896 html: (typeof(value) === 'object') ? '' : value
8903 if(typeof(config.colspan) != 'undefined'){
8904 td.colspan = config.colspan;
8907 if(typeof(config.hidden) != 'undefined' && config.hidden){
8908 td.style += ' display:none;';
8911 if(typeof(config.align) != 'undefined' && config.align.length){
8912 td.style += ' text-align:' + config.align + ';';
8914 if(typeof(config.valign) != 'undefined' && config.valign.length){
8915 td.style += ' vertical-align:' + config.valign + ';';
8918 if(typeof(config.width) != 'undefined'){
8919 td.style += ' width:' + config.width + 'px;';
8922 if(typeof(config.cursor) != 'undefined'){
8923 td.style += ' cursor:' + config.cursor + ';';
8926 if(typeof(config.cls) != 'undefined'){
8927 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8930 ['xs','sm','md','lg'].map(function(size){
8932 if(typeof(config[size]) == 'undefined'){
8938 if (!config[size]) { // 0 = hidden
8939 // BS 4 '0' is treated as hide that column and below.
8940 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8944 td.cls += ' col-' + size + '-' + config[size] + (
8945 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8955 row.cellObjects = cellObjects;
8963 onBeforeLoad : function()
8972 this.el.select('tbody', true).first().dom.innerHTML = '';
8975 * Show or hide a row.
8976 * @param {Number} rowIndex to show or hide
8977 * @param {Boolean} state hide
8979 setRowVisibility : function(rowIndex, state)
8981 var bt = this.mainBody.dom;
8983 var rows = this.el.select('tbody > tr', true).elements;
8985 if(typeof(rows[rowIndex]) == 'undefined'){
8988 rows[rowIndex].dom.style.display = state ? '' : 'none';
8992 getSelectionModel : function(){
8994 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8996 return this.selModel;
8999 * Render the Roo.bootstrap object from renderder
9001 renderCellObject : function(r)
9005 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9007 var t = r.cfg.render(r.container);
9010 Roo.each(r.cfg.cn, function(c){
9012 container: t.getChildContainer(),
9015 _this.renderCellObject(child);
9020 getRowIndex : function(row)
9024 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9035 * Returns the grid's underlying element = used by panel.Grid
9036 * @return {Element} The element
9038 getGridEl : function(){
9042 * Forces a resize - used by panel.Grid
9043 * @return {Element} The element
9045 autoSize : function()
9047 //var ctr = Roo.get(this.container.dom.parentElement);
9048 var ctr = Roo.get(this.el.dom);
9050 var thd = this.getGridEl().select('thead',true).first();
9051 var tbd = this.getGridEl().select('tbody', true).first();
9052 var tfd = this.getGridEl().select('tfoot', true).first();
9054 var cw = ctr.getWidth();
9055 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9059 tbd.setWidth(ctr.getWidth());
9060 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9061 // this needs fixing for various usage - currently only hydra job advers I think..
9063 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9065 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9068 cw = Math.max(cw, this.totalWidth);
9069 this.getGridEl().select('tbody tr',true).setWidth(cw);
9071 // resize 'expandable coloumn?
9073 return; // we doe not have a view in this design..
9076 onBodyScroll: function()
9078 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9080 this.mainHead.setStyle({
9081 'position' : 'relative',
9082 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9088 var scrollHeight = this.mainBody.dom.scrollHeight;
9090 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9092 var height = this.mainBody.getHeight();
9094 if(scrollHeight - height == scrollTop) {
9096 var total = this.ds.getTotalCount();
9098 if(this.footer.cursor + this.footer.pageSize < total){
9100 this.footer.ds.load({
9102 start : this.footer.cursor + this.footer.pageSize,
9103 limit : this.footer.pageSize
9113 onHeaderChange : function()
9115 var header = this.renderHeader();
9116 var table = this.el.select('table', true).first();
9118 this.mainHead.remove();
9119 this.mainHead = table.createChild(header, this.mainBody, false);
9121 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9122 e.on('click', this.sort, this);
9128 onHiddenChange : function(colModel, colIndex, hidden)
9130 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9131 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9133 this.CSS.updateRule(thSelector, "display", "");
9134 this.CSS.updateRule(tdSelector, "display", "");
9137 this.CSS.updateRule(thSelector, "display", "none");
9138 this.CSS.updateRule(tdSelector, "display", "none");
9141 this.onHeaderChange();
9145 setColumnWidth: function(col_index, width)
9147 // width = "md-2 xs-2..."
9148 if(!this.colModel.config[col_index]) {
9152 var w = width.split(" ");
9154 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9156 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9159 for(var j = 0; j < w.length; j++) {
9165 var size_cls = w[j].split("-");
9167 if(!Number.isInteger(size_cls[1] * 1)) {
9171 if(!this.colModel.config[col_index][size_cls[0]]) {
9175 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9179 h_row[0].classList.replace(
9180 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9181 "col-"+size_cls[0]+"-"+size_cls[1]
9184 for(var i = 0; i < rows.length; i++) {
9186 var size_cls = w[j].split("-");
9188 if(!Number.isInteger(size_cls[1] * 1)) {
9192 if(!this.colModel.config[col_index][size_cls[0]]) {
9196 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9200 rows[i].classList.replace(
9201 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9202 "col-"+size_cls[0]+"-"+size_cls[1]
9206 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9221 * @class Roo.bootstrap.TableCell
9222 * @extends Roo.bootstrap.Component
9223 * Bootstrap TableCell class
9224 * @cfg {String} html cell contain text
9225 * @cfg {String} cls cell class
9226 * @cfg {String} tag cell tag (td|th) default td
9227 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9228 * @cfg {String} align Aligns the content in a cell
9229 * @cfg {String} axis Categorizes cells
9230 * @cfg {String} bgcolor Specifies the background color of a cell
9231 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9232 * @cfg {Number} colspan Specifies the number of columns a cell should span
9233 * @cfg {String} headers Specifies one or more header cells a cell is related to
9234 * @cfg {Number} height Sets the height of a cell
9235 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9236 * @cfg {Number} rowspan Sets the number of rows a cell should span
9237 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9238 * @cfg {String} valign Vertical aligns the content in a cell
9239 * @cfg {Number} width Specifies the width of a cell
9242 * Create a new TableCell
9243 * @param {Object} config The config object
9246 Roo.bootstrap.TableCell = function(config){
9247 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9250 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9270 getAutoCreate : function(){
9271 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9291 cfg.align=this.align
9297 cfg.bgcolor=this.bgcolor
9300 cfg.charoff=this.charoff
9303 cfg.colspan=this.colspan
9306 cfg.headers=this.headers
9309 cfg.height=this.height
9312 cfg.nowrap=this.nowrap
9315 cfg.rowspan=this.rowspan
9318 cfg.scope=this.scope
9321 cfg.valign=this.valign
9324 cfg.width=this.width
9343 * @class Roo.bootstrap.TableRow
9344 * @extends Roo.bootstrap.Component
9345 * Bootstrap TableRow class
9346 * @cfg {String} cls row class
9347 * @cfg {String} align Aligns the content in a table row
9348 * @cfg {String} bgcolor Specifies a background color for a table row
9349 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9350 * @cfg {String} valign Vertical aligns the content in a table row
9353 * Create a new TableRow
9354 * @param {Object} config The config object
9357 Roo.bootstrap.TableRow = function(config){
9358 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9361 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9369 getAutoCreate : function(){
9370 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9380 cfg.align = this.align;
9383 cfg.bgcolor = this.bgcolor;
9386 cfg.charoff = this.charoff;
9389 cfg.valign = this.valign;
9407 * @class Roo.bootstrap.TableBody
9408 * @extends Roo.bootstrap.Component
9409 * Bootstrap TableBody class
9410 * @cfg {String} cls element class
9411 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9412 * @cfg {String} align Aligns the content inside the element
9413 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9414 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9417 * Create a new TableBody
9418 * @param {Object} config The config object
9421 Roo.bootstrap.TableBody = function(config){
9422 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9425 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9433 getAutoCreate : function(){
9434 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9448 cfg.align = this.align;
9451 cfg.charoff = this.charoff;
9454 cfg.valign = this.valign;
9461 // initEvents : function()
9468 // this.store = Roo.factory(this.store, Roo.data);
9469 // this.store.on('load', this.onLoad, this);
9471 // this.store.load();
9475 // onLoad: function ()
9477 // this.fireEvent('load', this);
9487 * Ext JS Library 1.1.1
9488 * Copyright(c) 2006-2007, Ext JS, LLC.
9490 * Originally Released Under LGPL - original licence link has changed is not relivant.
9493 * <script type="text/javascript">
9496 // as we use this in bootstrap.
9497 Roo.namespace('Roo.form');
9499 * @class Roo.form.Action
9500 * Internal Class used to handle form actions
9502 * @param {Roo.form.BasicForm} el The form element or its id
9503 * @param {Object} config Configuration options
9508 // define the action interface
9509 Roo.form.Action = function(form, options){
9511 this.options = options || {};
9514 * Client Validation Failed
9517 Roo.form.Action.CLIENT_INVALID = 'client';
9519 * Server Validation Failed
9522 Roo.form.Action.SERVER_INVALID = 'server';
9524 * Connect to Server Failed
9527 Roo.form.Action.CONNECT_FAILURE = 'connect';
9529 * Reading Data from Server Failed
9532 Roo.form.Action.LOAD_FAILURE = 'load';
9534 Roo.form.Action.prototype = {
9536 failureType : undefined,
9537 response : undefined,
9541 run : function(options){
9546 success : function(response){
9551 handleResponse : function(response){
9555 // default connection failure
9556 failure : function(response){
9558 this.response = response;
9559 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9560 this.form.afterAction(this, false);
9563 processResponse : function(response){
9564 this.response = response;
9565 if(!response.responseText){
9568 this.result = this.handleResponse(response);
9572 // utility functions used internally
9573 getUrl : function(appendParams){
9574 var url = this.options.url || this.form.url || this.form.el.dom.action;
9576 var p = this.getParams();
9578 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9584 getMethod : function(){
9585 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9588 getParams : function(){
9589 var bp = this.form.baseParams;
9590 var p = this.options.params;
9592 if(typeof p == "object"){
9593 p = Roo.urlEncode(Roo.applyIf(p, bp));
9594 }else if(typeof p == 'string' && bp){
9595 p += '&' + Roo.urlEncode(bp);
9598 p = Roo.urlEncode(bp);
9603 createCallback : function(){
9605 success: this.success,
9606 failure: this.failure,
9608 timeout: (this.form.timeout*1000),
9609 upload: this.form.fileUpload ? this.success : undefined
9614 Roo.form.Action.Submit = function(form, options){
9615 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9618 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9621 haveProgress : false,
9622 uploadComplete : false,
9624 // uploadProgress indicator.
9625 uploadProgress : function()
9627 if (!this.form.progressUrl) {
9631 if (!this.haveProgress) {
9632 Roo.MessageBox.progress("Uploading", "Uploading");
9634 if (this.uploadComplete) {
9635 Roo.MessageBox.hide();
9639 this.haveProgress = true;
9641 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9643 var c = new Roo.data.Connection();
9645 url : this.form.progressUrl,
9650 success : function(req){
9651 //console.log(data);
9655 rdata = Roo.decode(req.responseText)
9657 Roo.log("Invalid data from server..");
9661 if (!rdata || !rdata.success) {
9663 Roo.MessageBox.alert(Roo.encode(rdata));
9666 var data = rdata.data;
9668 if (this.uploadComplete) {
9669 Roo.MessageBox.hide();
9674 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9675 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9678 this.uploadProgress.defer(2000,this);
9681 failure: function(data) {
9682 Roo.log('progress url failed ');
9693 // run get Values on the form, so it syncs any secondary forms.
9694 this.form.getValues();
9696 var o = this.options;
9697 var method = this.getMethod();
9698 var isPost = method == 'POST';
9699 if(o.clientValidation === false || this.form.isValid()){
9701 if (this.form.progressUrl) {
9702 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9703 (new Date() * 1) + '' + Math.random());
9708 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9709 form:this.form.el.dom,
9710 url:this.getUrl(!isPost),
9712 params:isPost ? this.getParams() : null,
9713 isUpload: this.form.fileUpload,
9714 formData : this.form.formData
9717 this.uploadProgress();
9719 }else if (o.clientValidation !== false){ // client validation failed
9720 this.failureType = Roo.form.Action.CLIENT_INVALID;
9721 this.form.afterAction(this, false);
9725 success : function(response)
9727 this.uploadComplete= true;
9728 if (this.haveProgress) {
9729 Roo.MessageBox.hide();
9733 var result = this.processResponse(response);
9734 if(result === true || result.success){
9735 this.form.afterAction(this, true);
9739 this.form.markInvalid(result.errors);
9740 this.failureType = Roo.form.Action.SERVER_INVALID;
9742 this.form.afterAction(this, false);
9744 failure : function(response)
9746 this.uploadComplete= true;
9747 if (this.haveProgress) {
9748 Roo.MessageBox.hide();
9751 this.response = response;
9752 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9753 this.form.afterAction(this, false);
9756 handleResponse : function(response){
9757 if(this.form.errorReader){
9758 var rs = this.form.errorReader.read(response);
9761 for(var i = 0, len = rs.records.length; i < len; i++) {
9762 var r = rs.records[i];
9766 if(errors.length < 1){
9770 success : rs.success,
9776 ret = Roo.decode(response.responseText);
9780 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9790 Roo.form.Action.Load = function(form, options){
9791 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9792 this.reader = this.form.reader;
9795 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9800 Roo.Ajax.request(Roo.apply(
9801 this.createCallback(), {
9802 method:this.getMethod(),
9803 url:this.getUrl(false),
9804 params:this.getParams()
9808 success : function(response){
9810 var result = this.processResponse(response);
9811 if(result === true || !result.success || !result.data){
9812 this.failureType = Roo.form.Action.LOAD_FAILURE;
9813 this.form.afterAction(this, false);
9816 this.form.clearInvalid();
9817 this.form.setValues(result.data);
9818 this.form.afterAction(this, true);
9821 handleResponse : function(response){
9822 if(this.form.reader){
9823 var rs = this.form.reader.read(response);
9824 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9826 success : rs.success,
9830 return Roo.decode(response.responseText);
9834 Roo.form.Action.ACTION_TYPES = {
9835 'load' : Roo.form.Action.Load,
9836 'submit' : Roo.form.Action.Submit
9845 * @class Roo.bootstrap.Form
9846 * @extends Roo.bootstrap.Component
9847 * Bootstrap Form class
9848 * @cfg {String} method GET | POST (default POST)
9849 * @cfg {String} labelAlign top | left (default top)
9850 * @cfg {String} align left | right - for navbars
9851 * @cfg {Boolean} loadMask load mask when submit (default true)
9856 * @param {Object} config The config object
9860 Roo.bootstrap.Form = function(config){
9862 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9864 Roo.bootstrap.Form.popover.apply();
9868 * @event clientvalidation
9869 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9870 * @param {Form} this
9871 * @param {Boolean} valid true if the form has passed client-side validation
9873 clientvalidation: true,
9875 * @event beforeaction
9876 * Fires before any action is performed. Return false to cancel the action.
9877 * @param {Form} this
9878 * @param {Action} action The action to be performed
9882 * @event actionfailed
9883 * Fires when an action fails.
9884 * @param {Form} this
9885 * @param {Action} action The action that failed
9887 actionfailed : true,
9889 * @event actioncomplete
9890 * Fires when an action is completed.
9891 * @param {Form} this
9892 * @param {Action} action The action that completed
9894 actioncomplete : true
9898 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9901 * @cfg {String} method
9902 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9907 * The URL to use for form actions if one isn't supplied in the action options.
9910 * @cfg {Boolean} fileUpload
9911 * Set to true if this form is a file upload.
9915 * @cfg {Object} baseParams
9916 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9920 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9924 * @cfg {Sting} align (left|right) for navbar forms
9929 activeAction : null,
9932 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9933 * element by passing it or its id or mask the form itself by passing in true.
9936 waitMsgTarget : false,
9941 * @cfg {Boolean} errorMask (true|false) default false
9946 * @cfg {Number} maskOffset Default 100
9951 * @cfg {Boolean} maskBody
9955 getAutoCreate : function(){
9959 method : this.method || 'POST',
9960 id : this.id || Roo.id(),
9963 if (this.parent().xtype.match(/^Nav/)) {
9964 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9968 if (this.labelAlign == 'left' ) {
9969 cfg.cls += ' form-horizontal';
9975 initEvents : function()
9977 this.el.on('submit', this.onSubmit, this);
9978 // this was added as random key presses on the form where triggering form submit.
9979 this.el.on('keypress', function(e) {
9980 if (e.getCharCode() != 13) {
9983 // we might need to allow it for textareas.. and some other items.
9984 // check e.getTarget().
9986 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9990 Roo.log("keypress blocked");
9998 onSubmit : function(e){
10003 * Returns true if client-side validation on the form is successful.
10006 isValid : function(){
10007 var items = this.getItems();
10009 var target = false;
10011 items.each(function(f){
10017 Roo.log('invalid field: ' + f.name);
10021 if(!target && f.el.isVisible(true)){
10027 if(this.errorMask && !valid){
10028 Roo.bootstrap.Form.popover.mask(this, target);
10035 * Returns true if any fields in this form have changed since their original load.
10038 isDirty : function(){
10040 var items = this.getItems();
10041 items.each(function(f){
10051 * Performs a predefined action (submit or load) or custom actions you define on this form.
10052 * @param {String} actionName The name of the action type
10053 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10054 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10055 * accept other config options):
10057 Property Type Description
10058 ---------------- --------------- ----------------------------------------------------------------------------------
10059 url String The url for the action (defaults to the form's url)
10060 method String The form method to use (defaults to the form's method, or POST if not defined)
10061 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10062 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10063 validate the form on the client (defaults to false)
10065 * @return {BasicForm} this
10067 doAction : function(action, options){
10068 if(typeof action == 'string'){
10069 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10071 if(this.fireEvent('beforeaction', this, action) !== false){
10072 this.beforeAction(action);
10073 action.run.defer(100, action);
10079 beforeAction : function(action){
10080 var o = action.options;
10085 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10087 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10090 // not really supported yet.. ??
10092 //if(this.waitMsgTarget === true){
10093 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10094 //}else if(this.waitMsgTarget){
10095 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10096 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10098 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10104 afterAction : function(action, success){
10105 this.activeAction = null;
10106 var o = action.options;
10111 Roo.get(document.body).unmask();
10117 //if(this.waitMsgTarget === true){
10118 // this.el.unmask();
10119 //}else if(this.waitMsgTarget){
10120 // this.waitMsgTarget.unmask();
10122 // Roo.MessageBox.updateProgress(1);
10123 // Roo.MessageBox.hide();
10130 Roo.callback(o.success, o.scope, [this, action]);
10131 this.fireEvent('actioncomplete', this, action);
10135 // failure condition..
10136 // we have a scenario where updates need confirming.
10137 // eg. if a locking scenario exists..
10138 // we look for { errors : { needs_confirm : true }} in the response.
10140 (typeof(action.result) != 'undefined') &&
10141 (typeof(action.result.errors) != 'undefined') &&
10142 (typeof(action.result.errors.needs_confirm) != 'undefined')
10145 Roo.log("not supported yet");
10148 Roo.MessageBox.confirm(
10149 "Change requires confirmation",
10150 action.result.errorMsg,
10155 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10165 Roo.callback(o.failure, o.scope, [this, action]);
10166 // show an error message if no failed handler is set..
10167 if (!this.hasListener('actionfailed')) {
10168 Roo.log("need to add dialog support");
10170 Roo.MessageBox.alert("Error",
10171 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10172 action.result.errorMsg :
10173 "Saving Failed, please check your entries or try again"
10178 this.fireEvent('actionfailed', this, action);
10183 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10184 * @param {String} id The value to search for
10187 findField : function(id){
10188 var items = this.getItems();
10189 var field = items.get(id);
10191 items.each(function(f){
10192 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10199 return field || null;
10202 * Mark fields in this form invalid in bulk.
10203 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10204 * @return {BasicForm} this
10206 markInvalid : function(errors){
10207 if(errors instanceof Array){
10208 for(var i = 0, len = errors.length; i < len; i++){
10209 var fieldError = errors[i];
10210 var f = this.findField(fieldError.id);
10212 f.markInvalid(fieldError.msg);
10218 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10219 field.markInvalid(errors[id]);
10223 //Roo.each(this.childForms || [], function (f) {
10224 // f.markInvalid(errors);
10231 * Set values for fields in this form in bulk.
10232 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10233 * @return {BasicForm} this
10235 setValues : function(values){
10236 if(values instanceof Array){ // array of objects
10237 for(var i = 0, len = values.length; i < len; i++){
10239 var f = this.findField(v.id);
10241 f.setValue(v.value);
10242 if(this.trackResetOnLoad){
10243 f.originalValue = f.getValue();
10247 }else{ // object hash
10250 if(typeof values[id] != 'function' && (field = this.findField(id))){
10252 if (field.setFromData &&
10253 field.valueField &&
10254 field.displayField &&
10255 // combos' with local stores can
10256 // be queried via setValue()
10257 // to set their value..
10258 (field.store && !field.store.isLocal)
10262 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10263 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10264 field.setFromData(sd);
10266 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10268 field.setFromData(values);
10271 field.setValue(values[id]);
10275 if(this.trackResetOnLoad){
10276 field.originalValue = field.getValue();
10282 //Roo.each(this.childForms || [], function (f) {
10283 // f.setValues(values);
10290 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10291 * they are returned as an array.
10292 * @param {Boolean} asString
10295 getValues : function(asString){
10296 //if (this.childForms) {
10297 // copy values from the child forms
10298 // Roo.each(this.childForms, function (f) {
10299 // this.setValues(f.getValues());
10305 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10306 if(asString === true){
10309 return Roo.urlDecode(fs);
10313 * Returns the fields in this form as an object with key/value pairs.
10314 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10317 getFieldValues : function(with_hidden)
10319 var items = this.getItems();
10321 items.each(function(f){
10323 if (!f.getName()) {
10327 var v = f.getValue();
10329 if (f.inputType =='radio') {
10330 if (typeof(ret[f.getName()]) == 'undefined') {
10331 ret[f.getName()] = ''; // empty..
10334 if (!f.el.dom.checked) {
10338 v = f.el.dom.value;
10342 if(f.xtype == 'MoneyField'){
10343 ret[f.currencyName] = f.getCurrency();
10346 // not sure if this supported any more..
10347 if ((typeof(v) == 'object') && f.getRawValue) {
10348 v = f.getRawValue() ; // dates..
10350 // combo boxes where name != hiddenName...
10351 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10352 ret[f.name] = f.getRawValue();
10354 ret[f.getName()] = v;
10361 * Clears all invalid messages in this form.
10362 * @return {BasicForm} this
10364 clearInvalid : function(){
10365 var items = this.getItems();
10367 items.each(function(f){
10375 * Resets this form.
10376 * @return {BasicForm} this
10378 reset : function(){
10379 var items = this.getItems();
10380 items.each(function(f){
10384 Roo.each(this.childForms || [], function (f) {
10392 getItems : function()
10394 var r=new Roo.util.MixedCollection(false, function(o){
10395 return o.id || (o.id = Roo.id());
10397 var iter = function(el) {
10404 Roo.each(el.items,function(e) {
10413 hideFields : function(items)
10415 Roo.each(items, function(i){
10417 var f = this.findField(i);
10428 showFields : function(items)
10430 Roo.each(items, function(i){
10432 var f = this.findField(i);
10445 Roo.apply(Roo.bootstrap.Form, {
10461 intervalID : false,
10467 if(this.isApplied){
10472 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10473 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10474 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10475 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10478 this.maskEl.top.enableDisplayMode("block");
10479 this.maskEl.left.enableDisplayMode("block");
10480 this.maskEl.bottom.enableDisplayMode("block");
10481 this.maskEl.right.enableDisplayMode("block");
10483 this.toolTip = new Roo.bootstrap.Tooltip({
10484 cls : 'roo-form-error-popover',
10486 'left' : ['r-l', [-2,0], 'right'],
10487 'right' : ['l-r', [2,0], 'left'],
10488 'bottom' : ['tl-bl', [0,2], 'top'],
10489 'top' : [ 'bl-tl', [0,-2], 'bottom']
10493 this.toolTip.render(Roo.get(document.body));
10495 this.toolTip.el.enableDisplayMode("block");
10497 Roo.get(document.body).on('click', function(){
10501 Roo.get(document.body).on('touchstart', function(){
10505 this.isApplied = true
10508 mask : function(form, target)
10512 this.target = target;
10514 if(!this.form.errorMask || !target.el){
10518 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10520 Roo.log(scrollable);
10522 var ot = this.target.el.calcOffsetsTo(scrollable);
10524 var scrollTo = ot[1] - this.form.maskOffset;
10526 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10528 scrollable.scrollTo('top', scrollTo);
10530 var box = this.target.el.getBox();
10532 var zIndex = Roo.bootstrap.Modal.zIndex++;
10535 this.maskEl.top.setStyle('position', 'absolute');
10536 this.maskEl.top.setStyle('z-index', zIndex);
10537 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10538 this.maskEl.top.setLeft(0);
10539 this.maskEl.top.setTop(0);
10540 this.maskEl.top.show();
10542 this.maskEl.left.setStyle('position', 'absolute');
10543 this.maskEl.left.setStyle('z-index', zIndex);
10544 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10545 this.maskEl.left.setLeft(0);
10546 this.maskEl.left.setTop(box.y - this.padding);
10547 this.maskEl.left.show();
10549 this.maskEl.bottom.setStyle('position', 'absolute');
10550 this.maskEl.bottom.setStyle('z-index', zIndex);
10551 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10552 this.maskEl.bottom.setLeft(0);
10553 this.maskEl.bottom.setTop(box.bottom + this.padding);
10554 this.maskEl.bottom.show();
10556 this.maskEl.right.setStyle('position', 'absolute');
10557 this.maskEl.right.setStyle('z-index', zIndex);
10558 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10559 this.maskEl.right.setLeft(box.right + this.padding);
10560 this.maskEl.right.setTop(box.y - this.padding);
10561 this.maskEl.right.show();
10563 this.toolTip.bindEl = this.target.el;
10565 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10567 var tip = this.target.blankText;
10569 if(this.target.getValue() !== '' ) {
10571 if (this.target.invalidText.length) {
10572 tip = this.target.invalidText;
10573 } else if (this.target.regexText.length){
10574 tip = this.target.regexText;
10578 this.toolTip.show(tip);
10580 this.intervalID = window.setInterval(function() {
10581 Roo.bootstrap.Form.popover.unmask();
10584 window.onwheel = function(){ return false;};
10586 (function(){ this.isMasked = true; }).defer(500, this);
10590 unmask : function()
10592 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10596 this.maskEl.top.setStyle('position', 'absolute');
10597 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10598 this.maskEl.top.hide();
10600 this.maskEl.left.setStyle('position', 'absolute');
10601 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10602 this.maskEl.left.hide();
10604 this.maskEl.bottom.setStyle('position', 'absolute');
10605 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10606 this.maskEl.bottom.hide();
10608 this.maskEl.right.setStyle('position', 'absolute');
10609 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10610 this.maskEl.right.hide();
10612 this.toolTip.hide();
10614 this.toolTip.el.hide();
10616 window.onwheel = function(){ return true;};
10618 if(this.intervalID){
10619 window.clearInterval(this.intervalID);
10620 this.intervalID = false;
10623 this.isMasked = false;
10633 * Ext JS Library 1.1.1
10634 * Copyright(c) 2006-2007, Ext JS, LLC.
10636 * Originally Released Under LGPL - original licence link has changed is not relivant.
10639 * <script type="text/javascript">
10642 * @class Roo.form.VTypes
10643 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10646 Roo.form.VTypes = function(){
10647 // closure these in so they are only created once.
10648 var alpha = /^[a-zA-Z_]+$/;
10649 var alphanum = /^[a-zA-Z0-9_]+$/;
10650 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10651 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10653 // All these messages and functions are configurable
10656 * The function used to validate email addresses
10657 * @param {String} value The email address
10659 'email' : function(v){
10660 return email.test(v);
10663 * The error text to display when the email validation function returns false
10666 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10668 * The keystroke filter mask to be applied on email input
10671 'emailMask' : /[a-z0-9_\.\-@]/i,
10674 * The function used to validate URLs
10675 * @param {String} value The URL
10677 'url' : function(v){
10678 return url.test(v);
10681 * The error text to display when the url validation function returns false
10684 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10687 * The function used to validate alpha values
10688 * @param {String} value The value
10690 'alpha' : function(v){
10691 return alpha.test(v);
10694 * The error text to display when the alpha validation function returns false
10697 'alphaText' : 'This field should only contain letters and _',
10699 * The keystroke filter mask to be applied on alpha input
10702 'alphaMask' : /[a-z_]/i,
10705 * The function used to validate alphanumeric values
10706 * @param {String} value The value
10708 'alphanum' : function(v){
10709 return alphanum.test(v);
10712 * The error text to display when the alphanumeric validation function returns false
10715 'alphanumText' : 'This field should only contain letters, numbers and _',
10717 * The keystroke filter mask to be applied on alphanumeric input
10720 'alphanumMask' : /[a-z0-9_]/i
10730 * @class Roo.bootstrap.Input
10731 * @extends Roo.bootstrap.Component
10732 * Bootstrap Input class
10733 * @cfg {Boolean} disabled is it disabled
10734 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10735 * @cfg {String} name name of the input
10736 * @cfg {string} fieldLabel - the label associated
10737 * @cfg {string} placeholder - placeholder to put in text.
10738 * @cfg {string} before - input group add on before
10739 * @cfg {string} after - input group add on after
10740 * @cfg {string} size - (lg|sm) or leave empty..
10741 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10742 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10743 * @cfg {Number} md colspan out of 12 for computer-sized screens
10744 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10745 * @cfg {string} value default value of the input
10746 * @cfg {Number} labelWidth set the width of label
10747 * @cfg {Number} labellg set the width of label (1-12)
10748 * @cfg {Number} labelmd set the width of label (1-12)
10749 * @cfg {Number} labelsm set the width of label (1-12)
10750 * @cfg {Number} labelxs set the width of label (1-12)
10751 * @cfg {String} labelAlign (top|left)
10752 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10753 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10754 * @cfg {String} indicatorpos (left|right) default left
10755 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10756 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10757 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10759 * @cfg {String} align (left|center|right) Default left
10760 * @cfg {Boolean} forceFeedback (true|false) Default false
10763 * Create a new Input
10764 * @param {Object} config The config object
10767 Roo.bootstrap.Input = function(config){
10769 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10774 * Fires when this field receives input focus.
10775 * @param {Roo.form.Field} this
10780 * Fires when this field loses input focus.
10781 * @param {Roo.form.Field} this
10785 * @event specialkey
10786 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10787 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10788 * @param {Roo.form.Field} this
10789 * @param {Roo.EventObject} e The event object
10794 * Fires just before the field blurs if the field value has changed.
10795 * @param {Roo.form.Field} this
10796 * @param {Mixed} newValue The new value
10797 * @param {Mixed} oldValue The original value
10802 * Fires after the field has been marked as invalid.
10803 * @param {Roo.form.Field} this
10804 * @param {String} msg The validation message
10809 * Fires after the field has been validated with no errors.
10810 * @param {Roo.form.Field} this
10815 * Fires after the key up
10816 * @param {Roo.form.Field} this
10817 * @param {Roo.EventObject} e The event Object
10822 * Fires after the user pastes into input
10823 * @param {Roo.form.Field} this
10824 * @param {Roo.EventObject} e The event Object
10830 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10832 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10833 automatic validation (defaults to "keyup").
10835 validationEvent : "keyup",
10837 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10839 validateOnBlur : true,
10841 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10843 validationDelay : 250,
10845 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10847 focusClass : "x-form-focus", // not needed???
10851 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10853 invalidClass : "has-warning",
10856 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10858 validClass : "has-success",
10861 * @cfg {Boolean} hasFeedback (true|false) default true
10863 hasFeedback : true,
10866 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10868 invalidFeedbackClass : "glyphicon-warning-sign",
10871 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10873 validFeedbackClass : "glyphicon-ok",
10876 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10878 selectOnFocus : false,
10881 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10885 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10890 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10892 disableKeyFilter : false,
10895 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10899 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10903 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10905 blankText : "Please complete this mandatory field",
10908 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10912 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10914 maxLength : Number.MAX_VALUE,
10916 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10918 minLengthText : "The minimum length for this field is {0}",
10920 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10922 maxLengthText : "The maximum length for this field is {0}",
10926 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10927 * If available, this function will be called only after the basic validators all return true, and will be passed the
10928 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10932 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10933 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10934 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10938 * @cfg {String} regexText -- Depricated - use Invalid Text
10943 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10949 autocomplete: false,
10953 inputType : 'text',
10956 placeholder: false,
10961 preventMark: false,
10962 isFormField : true,
10965 labelAlign : false,
10968 formatedValue : false,
10969 forceFeedback : false,
10971 indicatorpos : 'left',
10981 parentLabelAlign : function()
10984 while (parent.parent()) {
10985 parent = parent.parent();
10986 if (typeof(parent.labelAlign) !='undefined') {
10987 return parent.labelAlign;
10994 getAutoCreate : function()
10996 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11002 if(this.inputType != 'hidden'){
11003 cfg.cls = 'form-group' //input-group
11009 type : this.inputType,
11010 value : this.value,
11011 cls : 'form-control',
11012 placeholder : this.placeholder || '',
11013 autocomplete : this.autocomplete || 'new-password'
11015 if (this.inputType == 'file') {
11016 input.style = 'overflow:hidden'; // why not in CSS?
11019 if(this.capture.length){
11020 input.capture = this.capture;
11023 if(this.accept.length){
11024 input.accept = this.accept + "/*";
11028 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11031 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11032 input.maxLength = this.maxLength;
11035 if (this.disabled) {
11036 input.disabled=true;
11039 if (this.readOnly) {
11040 input.readonly=true;
11044 input.name = this.name;
11048 input.cls += ' input-' + this.size;
11052 ['xs','sm','md','lg'].map(function(size){
11053 if (settings[size]) {
11054 cfg.cls += ' col-' + size + '-' + settings[size];
11058 var inputblock = input;
11062 cls: 'glyphicon form-control-feedback'
11065 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11068 cls : 'has-feedback',
11076 if (this.before || this.after) {
11079 cls : 'input-group',
11083 if (this.before && typeof(this.before) == 'string') {
11085 inputblock.cn.push({
11087 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11091 if (this.before && typeof(this.before) == 'object') {
11092 this.before = Roo.factory(this.before);
11094 inputblock.cn.push({
11096 cls : 'roo-input-before input-group-prepend input-group-' +
11097 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11101 inputblock.cn.push(input);
11103 if (this.after && typeof(this.after) == 'string') {
11104 inputblock.cn.push({
11106 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11110 if (this.after && typeof(this.after) == 'object') {
11111 this.after = Roo.factory(this.after);
11113 inputblock.cn.push({
11115 cls : 'roo-input-after input-group-append input-group-' +
11116 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11120 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11121 inputblock.cls += ' has-feedback';
11122 inputblock.cn.push(feedback);
11127 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11128 tooltip : 'This field is required'
11130 if (this.allowBlank ) {
11131 indicator.style = this.allowBlank ? ' display:none' : '';
11133 if (align ==='left' && this.fieldLabel.length) {
11135 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11142 cls : 'control-label col-form-label',
11143 html : this.fieldLabel
11154 var labelCfg = cfg.cn[1];
11155 var contentCfg = cfg.cn[2];
11157 if(this.indicatorpos == 'right'){
11162 cls : 'control-label col-form-label',
11166 html : this.fieldLabel
11180 labelCfg = cfg.cn[0];
11181 contentCfg = cfg.cn[1];
11185 if(this.labelWidth > 12){
11186 labelCfg.style = "width: " + this.labelWidth + 'px';
11189 if(this.labelWidth < 13 && this.labelmd == 0){
11190 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11193 if(this.labellg > 0){
11194 labelCfg.cls += ' col-lg-' + this.labellg;
11195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11198 if(this.labelmd > 0){
11199 labelCfg.cls += ' col-md-' + this.labelmd;
11200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11203 if(this.labelsm > 0){
11204 labelCfg.cls += ' col-sm-' + this.labelsm;
11205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11208 if(this.labelxs > 0){
11209 labelCfg.cls += ' col-xs-' + this.labelxs;
11210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11214 } else if ( this.fieldLabel.length) {
11221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11222 tooltip : 'This field is required',
11223 style : this.allowBlank ? ' display:none' : ''
11227 //cls : 'input-group-addon',
11228 html : this.fieldLabel
11236 if(this.indicatorpos == 'right'){
11241 //cls : 'input-group-addon',
11242 html : this.fieldLabel
11247 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11248 tooltip : 'This field is required',
11249 style : this.allowBlank ? ' display:none' : ''
11269 if (this.parentType === 'Navbar' && this.parent().bar) {
11270 cfg.cls += ' navbar-form';
11273 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11274 // on BS4 we do this only if not form
11275 cfg.cls += ' navbar-form';
11283 * return the real input element.
11285 inputEl: function ()
11287 return this.el.select('input.form-control',true).first();
11290 tooltipEl : function()
11292 return this.inputEl();
11295 indicatorEl : function()
11297 if (Roo.bootstrap.version == 4) {
11298 return false; // not enabled in v4 yet.
11301 var indicator = this.el.select('i.roo-required-indicator',true).first();
11311 setDisabled : function(v)
11313 var i = this.inputEl().dom;
11315 i.removeAttribute('disabled');
11319 i.setAttribute('disabled','true');
11321 initEvents : function()
11324 this.inputEl().on("keydown" , this.fireKey, this);
11325 this.inputEl().on("focus", this.onFocus, this);
11326 this.inputEl().on("blur", this.onBlur, this);
11328 this.inputEl().relayEvent('keyup', this);
11329 this.inputEl().relayEvent('paste', this);
11331 this.indicator = this.indicatorEl();
11333 if(this.indicator){
11334 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11337 // reference to original value for reset
11338 this.originalValue = this.getValue();
11339 //Roo.form.TextField.superclass.initEvents.call(this);
11340 if(this.validationEvent == 'keyup'){
11341 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11342 this.inputEl().on('keyup', this.filterValidation, this);
11344 else if(this.validationEvent !== false){
11345 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11348 if(this.selectOnFocus){
11349 this.on("focus", this.preFocus, this);
11352 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11353 this.inputEl().on("keypress", this.filterKeys, this);
11355 this.inputEl().relayEvent('keypress', this);
11358 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11359 this.el.on("click", this.autoSize, this);
11362 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11363 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11366 if (typeof(this.before) == 'object') {
11367 this.before.render(this.el.select('.roo-input-before',true).first());
11369 if (typeof(this.after) == 'object') {
11370 this.after.render(this.el.select('.roo-input-after',true).first());
11373 this.inputEl().on('change', this.onChange, this);
11376 filterValidation : function(e){
11377 if(!e.isNavKeyPress()){
11378 this.validationTask.delay(this.validationDelay);
11382 * Validates the field value
11383 * @return {Boolean} True if the value is valid, else false
11385 validate : function(){
11386 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11387 if(this.disabled || this.validateValue(this.getRawValue())){
11392 this.markInvalid();
11398 * Validates a value according to the field's validation rules and marks the field as invalid
11399 * if the validation fails
11400 * @param {Mixed} value The value to validate
11401 * @return {Boolean} True if the value is valid, else false
11403 validateValue : function(value)
11405 if(this.getVisibilityEl().hasClass('hidden')){
11409 if(value.length < 1) { // if it's blank
11410 if(this.allowBlank){
11416 if(value.length < this.minLength){
11419 if(value.length > this.maxLength){
11423 var vt = Roo.form.VTypes;
11424 if(!vt[this.vtype](value, this)){
11428 if(typeof this.validator == "function"){
11429 var msg = this.validator(value);
11433 if (typeof(msg) == 'string') {
11434 this.invalidText = msg;
11438 if(this.regex && !this.regex.test(value)){
11446 fireKey : function(e){
11447 //Roo.log('field ' + e.getKey());
11448 if(e.isNavKeyPress()){
11449 this.fireEvent("specialkey", this, e);
11452 focus : function (selectText){
11454 this.inputEl().focus();
11455 if(selectText === true){
11456 this.inputEl().dom.select();
11462 onFocus : function(){
11463 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11464 // this.el.addClass(this.focusClass);
11466 if(!this.hasFocus){
11467 this.hasFocus = true;
11468 this.startValue = this.getValue();
11469 this.fireEvent("focus", this);
11473 beforeBlur : Roo.emptyFn,
11477 onBlur : function(){
11479 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11480 //this.el.removeClass(this.focusClass);
11482 this.hasFocus = false;
11483 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11486 var v = this.getValue();
11487 if(String(v) !== String(this.startValue)){
11488 this.fireEvent('change', this, v, this.startValue);
11490 this.fireEvent("blur", this);
11493 onChange : function(e)
11495 var v = this.getValue();
11496 if(String(v) !== String(this.startValue)){
11497 this.fireEvent('change', this, v, this.startValue);
11503 * Resets the current field value to the originally loaded value and clears any validation messages
11505 reset : function(){
11506 this.setValue(this.originalValue);
11510 * Returns the name of the field
11511 * @return {Mixed} name The name field
11513 getName: function(){
11517 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11518 * @return {Mixed} value The field value
11520 getValue : function(){
11522 var v = this.inputEl().getValue();
11527 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11528 * @return {Mixed} value The field value
11530 getRawValue : function(){
11531 var v = this.inputEl().getValue();
11537 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11538 * @param {Mixed} value The value to set
11540 setRawValue : function(v){
11541 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11544 selectText : function(start, end){
11545 var v = this.getRawValue();
11547 start = start === undefined ? 0 : start;
11548 end = end === undefined ? v.length : end;
11549 var d = this.inputEl().dom;
11550 if(d.setSelectionRange){
11551 d.setSelectionRange(start, end);
11552 }else if(d.createTextRange){
11553 var range = d.createTextRange();
11554 range.moveStart("character", start);
11555 range.moveEnd("character", v.length-end);
11562 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11563 * @param {Mixed} value The value to set
11565 setValue : function(v){
11568 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11574 processValue : function(value){
11575 if(this.stripCharsRe){
11576 var newValue = value.replace(this.stripCharsRe, '');
11577 if(newValue !== value){
11578 this.setRawValue(newValue);
11585 preFocus : function(){
11587 if(this.selectOnFocus){
11588 this.inputEl().dom.select();
11591 filterKeys : function(e){
11592 var k = e.getKey();
11593 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11596 var c = e.getCharCode(), cc = String.fromCharCode(c);
11597 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11600 if(!this.maskRe.test(cc)){
11605 * Clear any invalid styles/messages for this field
11607 clearInvalid : function(){
11609 if(!this.el || this.preventMark){ // not rendered
11614 this.el.removeClass([this.invalidClass, 'is-invalid']);
11616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11626 if(this.indicator){
11627 this.indicator.removeClass('visible');
11628 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11631 this.fireEvent('valid', this);
11635 * Mark this field as valid
11637 markValid : function()
11639 if(!this.el || this.preventMark){ // not rendered...
11643 this.el.removeClass([this.invalidClass, this.validClass]);
11644 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11646 var feedback = this.el.select('.form-control-feedback', true).first();
11649 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11652 if(this.indicator){
11653 this.indicator.removeClass('visible');
11654 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11662 if(this.allowBlank && !this.getRawValue().length){
11665 if (Roo.bootstrap.version == 3) {
11666 this.el.addClass(this.validClass);
11668 this.inputEl().addClass('is-valid');
11671 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11673 var feedback = this.el.select('.form-control-feedback', true).first();
11676 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11677 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11682 this.fireEvent('valid', this);
11686 * Mark this field as invalid
11687 * @param {String} msg The validation message
11689 markInvalid : function(msg)
11691 if(!this.el || this.preventMark){ // not rendered
11695 this.el.removeClass([this.invalidClass, this.validClass]);
11696 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11698 var feedback = this.el.select('.form-control-feedback', true).first();
11701 this.el.select('.form-control-feedback', true).first().removeClass(
11702 [this.invalidFeedbackClass, this.validFeedbackClass]);
11709 if(this.allowBlank && !this.getRawValue().length){
11713 if(this.indicator){
11714 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11715 this.indicator.addClass('visible');
11717 if (Roo.bootstrap.version == 3) {
11718 this.el.addClass(this.invalidClass);
11720 this.inputEl().addClass('is-invalid');
11725 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11727 var feedback = this.el.select('.form-control-feedback', true).first();
11730 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11732 if(this.getValue().length || this.forceFeedback){
11733 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11740 this.fireEvent('invalid', this, msg);
11743 SafariOnKeyDown : function(event)
11745 // this is a workaround for a password hang bug on chrome/ webkit.
11746 if (this.inputEl().dom.type != 'password') {
11750 var isSelectAll = false;
11752 if(this.inputEl().dom.selectionEnd > 0){
11753 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11755 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11756 event.preventDefault();
11761 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11763 event.preventDefault();
11764 // this is very hacky as keydown always get's upper case.
11766 var cc = String.fromCharCode(event.getCharCode());
11767 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11771 adjustWidth : function(tag, w){
11772 tag = tag.toLowerCase();
11773 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11774 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11775 if(tag == 'input'){
11778 if(tag == 'textarea'){
11781 }else if(Roo.isOpera){
11782 if(tag == 'input'){
11785 if(tag == 'textarea'){
11793 setFieldLabel : function(v)
11795 if(!this.rendered){
11799 if(this.indicatorEl()){
11800 var ar = this.el.select('label > span',true);
11802 if (ar.elements.length) {
11803 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804 this.fieldLabel = v;
11808 var br = this.el.select('label',true);
11810 if(br.elements.length) {
11811 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812 this.fieldLabel = v;
11816 Roo.log('Cannot Found any of label > span || label in input');
11820 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11821 this.fieldLabel = v;
11836 * @class Roo.bootstrap.TextArea
11837 * @extends Roo.bootstrap.Input
11838 * Bootstrap TextArea class
11839 * @cfg {Number} cols Specifies the visible width of a text area
11840 * @cfg {Number} rows Specifies the visible number of lines in a text area
11841 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11842 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11843 * @cfg {string} html text
11846 * Create a new TextArea
11847 * @param {Object} config The config object
11850 Roo.bootstrap.TextArea = function(config){
11851 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11855 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11865 getAutoCreate : function(){
11867 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11873 if(this.inputType != 'hidden'){
11874 cfg.cls = 'form-group' //input-group
11882 value : this.value || '',
11883 html: this.html || '',
11884 cls : 'form-control',
11885 placeholder : this.placeholder || ''
11889 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11890 input.maxLength = this.maxLength;
11894 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11898 input.cols = this.cols;
11901 if (this.readOnly) {
11902 input.readonly = true;
11906 input.name = this.name;
11910 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11914 ['xs','sm','md','lg'].map(function(size){
11915 if (settings[size]) {
11916 cfg.cls += ' col-' + size + '-' + settings[size];
11920 var inputblock = input;
11922 if(this.hasFeedback && !this.allowBlank){
11926 cls: 'glyphicon form-control-feedback'
11930 cls : 'has-feedback',
11939 if (this.before || this.after) {
11942 cls : 'input-group',
11946 inputblock.cn.push({
11948 cls : 'input-group-addon',
11953 inputblock.cn.push(input);
11955 if(this.hasFeedback && !this.allowBlank){
11956 inputblock.cls += ' has-feedback';
11957 inputblock.cn.push(feedback);
11961 inputblock.cn.push({
11963 cls : 'input-group-addon',
11970 if (align ==='left' && this.fieldLabel.length) {
11975 cls : 'control-label',
11976 html : this.fieldLabel
11987 if(this.labelWidth > 12){
11988 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11991 if(this.labelWidth < 13 && this.labelmd == 0){
11992 this.labelmd = this.labelWidth;
11995 if(this.labellg > 0){
11996 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11997 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12000 if(this.labelmd > 0){
12001 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12002 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12005 if(this.labelsm > 0){
12006 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12007 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12010 if(this.labelxs > 0){
12011 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12012 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12015 } else if ( this.fieldLabel.length) {
12020 //cls : 'input-group-addon',
12021 html : this.fieldLabel
12039 if (this.disabled) {
12040 input.disabled=true;
12047 * return the real textarea element.
12049 inputEl: function ()
12051 return this.el.select('textarea.form-control',true).first();
12055 * Clear any invalid styles/messages for this field
12057 clearInvalid : function()
12060 if(!this.el || this.preventMark){ // not rendered
12064 var label = this.el.select('label', true).first();
12065 var icon = this.el.select('i.fa-star', true).first();
12070 this.el.removeClass( this.validClass);
12071 this.inputEl().removeClass('is-invalid');
12073 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12075 var feedback = this.el.select('.form-control-feedback', true).first();
12078 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12083 this.fireEvent('valid', this);
12087 * Mark this field as valid
12089 markValid : function()
12091 if(!this.el || this.preventMark){ // not rendered
12095 this.el.removeClass([this.invalidClass, this.validClass]);
12096 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12098 var feedback = this.el.select('.form-control-feedback', true).first();
12101 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12104 if(this.disabled || this.allowBlank){
12108 var label = this.el.select('label', true).first();
12109 var icon = this.el.select('i.fa-star', true).first();
12114 if (Roo.bootstrap.version == 3) {
12115 this.el.addClass(this.validClass);
12117 this.inputEl().addClass('is-valid');
12121 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12123 var feedback = this.el.select('.form-control-feedback', true).first();
12126 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12127 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12132 this.fireEvent('valid', this);
12136 * Mark this field as invalid
12137 * @param {String} msg The validation message
12139 markInvalid : function(msg)
12141 if(!this.el || this.preventMark){ // not rendered
12145 this.el.removeClass([this.invalidClass, this.validClass]);
12146 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12148 var feedback = this.el.select('.form-control-feedback', true).first();
12151 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12154 if(this.disabled || this.allowBlank){
12158 var label = this.el.select('label', true).first();
12159 var icon = this.el.select('i.fa-star', true).first();
12161 if(!this.getValue().length && label && !icon){
12162 this.el.createChild({
12164 cls : 'text-danger fa fa-lg fa-star',
12165 tooltip : 'This field is required',
12166 style : 'margin-right:5px;'
12170 if (Roo.bootstrap.version == 3) {
12171 this.el.addClass(this.invalidClass);
12173 this.inputEl().addClass('is-invalid');
12176 // fixme ... this may be depricated need to test..
12177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179 var feedback = this.el.select('.form-control-feedback', true).first();
12182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12184 if(this.getValue().length || this.forceFeedback){
12185 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12192 this.fireEvent('invalid', this, msg);
12200 * trigger field - base class for combo..
12205 * @class Roo.bootstrap.TriggerField
12206 * @extends Roo.bootstrap.Input
12207 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12208 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12209 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12210 * for which you can provide a custom implementation. For example:
12212 var trigger = new Roo.bootstrap.TriggerField();
12213 trigger.onTriggerClick = myTriggerFn;
12214 trigger.applyTo('my-field');
12217 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12218 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12219 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12220 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12221 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12224 * Create a new TriggerField.
12225 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12226 * to the base TextField)
12228 Roo.bootstrap.TriggerField = function(config){
12229 this.mimicing = false;
12230 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12233 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12235 * @cfg {String} triggerClass A CSS class to apply to the trigger
12238 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12243 * @cfg {Boolean} removable (true|false) special filter default false
12247 /** @cfg {Boolean} grow @hide */
12248 /** @cfg {Number} growMin @hide */
12249 /** @cfg {Number} growMax @hide */
12255 autoSize: Roo.emptyFn,
12259 deferHeight : true,
12262 actionMode : 'wrap',
12267 getAutoCreate : function(){
12269 var align = this.labelAlign || this.parentLabelAlign();
12274 cls: 'form-group' //input-group
12281 type : this.inputType,
12282 cls : 'form-control',
12283 autocomplete: 'new-password',
12284 placeholder : this.placeholder || ''
12288 input.name = this.name;
12291 input.cls += ' input-' + this.size;
12294 if (this.disabled) {
12295 input.disabled=true;
12298 var inputblock = input;
12300 if(this.hasFeedback && !this.allowBlank){
12304 cls: 'glyphicon form-control-feedback'
12307 if(this.removable && !this.editable ){
12309 cls : 'has-feedback',
12315 cls : 'roo-combo-removable-btn close'
12322 cls : 'has-feedback',
12331 if(this.removable && !this.editable ){
12333 cls : 'roo-removable',
12339 cls : 'roo-combo-removable-btn close'
12346 if (this.before || this.after) {
12349 cls : 'input-group',
12353 inputblock.cn.push({
12355 cls : 'input-group-addon input-group-prepend input-group-text',
12360 inputblock.cn.push(input);
12362 if(this.hasFeedback && !this.allowBlank){
12363 inputblock.cls += ' has-feedback';
12364 inputblock.cn.push(feedback);
12368 inputblock.cn.push({
12370 cls : 'input-group-addon input-group-append input-group-text',
12379 var ibwrap = inputblock;
12384 cls: 'roo-select2-choices',
12388 cls: 'roo-select2-search-field',
12400 cls: 'roo-select2-container input-group',
12405 cls: 'form-hidden-field'
12411 if(!this.multiple && this.showToggleBtn){
12417 if (this.caret != false) {
12420 cls: 'fa fa-' + this.caret
12427 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12429 Roo.bootstrap.version == 3 ? caret : '',
12432 cls: 'combobox-clear',
12446 combobox.cls += ' roo-select2-container-multi';
12450 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12451 tooltip : 'This field is required'
12453 if (Roo.bootstrap.version == 4) {
12456 style : 'display:none'
12461 if (align ==='left' && this.fieldLabel.length) {
12463 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12470 cls : 'control-label',
12471 html : this.fieldLabel
12483 var labelCfg = cfg.cn[1];
12484 var contentCfg = cfg.cn[2];
12486 if(this.indicatorpos == 'right'){
12491 cls : 'control-label',
12495 html : this.fieldLabel
12509 labelCfg = cfg.cn[0];
12510 contentCfg = cfg.cn[1];
12513 if(this.labelWidth > 12){
12514 labelCfg.style = "width: " + this.labelWidth + 'px';
12517 if(this.labelWidth < 13 && this.labelmd == 0){
12518 this.labelmd = this.labelWidth;
12521 if(this.labellg > 0){
12522 labelCfg.cls += ' col-lg-' + this.labellg;
12523 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12526 if(this.labelmd > 0){
12527 labelCfg.cls += ' col-md-' + this.labelmd;
12528 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12531 if(this.labelsm > 0){
12532 labelCfg.cls += ' col-sm-' + this.labelsm;
12533 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12536 if(this.labelxs > 0){
12537 labelCfg.cls += ' col-xs-' + this.labelxs;
12538 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12541 } else if ( this.fieldLabel.length) {
12542 // Roo.log(" label");
12547 //cls : 'input-group-addon',
12548 html : this.fieldLabel
12556 if(this.indicatorpos == 'right'){
12564 html : this.fieldLabel
12578 // Roo.log(" no label && no align");
12585 ['xs','sm','md','lg'].map(function(size){
12586 if (settings[size]) {
12587 cfg.cls += ' col-' + size + '-' + settings[size];
12598 onResize : function(w, h){
12599 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12600 // if(typeof w == 'number'){
12601 // var x = w - this.trigger.getWidth();
12602 // this.inputEl().setWidth(this.adjustWidth('input', x));
12603 // this.trigger.setStyle('left', x+'px');
12608 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12611 getResizeEl : function(){
12612 return this.inputEl();
12616 getPositionEl : function(){
12617 return this.inputEl();
12621 alignErrorIcon : function(){
12622 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12626 initEvents : function(){
12630 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12631 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12632 if(!this.multiple && this.showToggleBtn){
12633 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12634 if(this.hideTrigger){
12635 this.trigger.setDisplayed(false);
12637 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12641 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12644 if(this.removable && !this.editable && !this.tickable){
12645 var close = this.closeTriggerEl();
12648 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12649 close.on('click', this.removeBtnClick, this, close);
12653 //this.trigger.addClassOnOver('x-form-trigger-over');
12654 //this.trigger.addClassOnClick('x-form-trigger-click');
12657 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12661 closeTriggerEl : function()
12663 var close = this.el.select('.roo-combo-removable-btn', true).first();
12664 return close ? close : false;
12667 removeBtnClick : function(e, h, el)
12669 e.preventDefault();
12671 if(this.fireEvent("remove", this) !== false){
12673 this.fireEvent("afterremove", this)
12677 createList : function()
12679 this.list = Roo.get(document.body).createChild({
12680 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12681 cls: 'typeahead typeahead-long dropdown-menu shadow',
12682 style: 'display:none'
12685 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12690 initTrigger : function(){
12695 onDestroy : function(){
12697 this.trigger.removeAllListeners();
12698 // this.trigger.remove();
12701 // this.wrap.remove();
12703 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12707 onFocus : function(){
12708 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12710 if(!this.mimicing){
12711 this.wrap.addClass('x-trigger-wrap-focus');
12712 this.mimicing = true;
12713 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12714 if(this.monitorTab){
12715 this.el.on("keydown", this.checkTab, this);
12722 checkTab : function(e){
12723 if(e.getKey() == e.TAB){
12724 this.triggerBlur();
12729 onBlur : function(){
12734 mimicBlur : function(e, t){
12736 if(!this.wrap.contains(t) && this.validateBlur()){
12737 this.triggerBlur();
12743 triggerBlur : function(){
12744 this.mimicing = false;
12745 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12746 if(this.monitorTab){
12747 this.el.un("keydown", this.checkTab, this);
12749 //this.wrap.removeClass('x-trigger-wrap-focus');
12750 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12754 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12755 validateBlur : function(e, t){
12760 onDisable : function(){
12761 this.inputEl().dom.disabled = true;
12762 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12764 // this.wrap.addClass('x-item-disabled');
12769 onEnable : function(){
12770 this.inputEl().dom.disabled = false;
12771 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12773 // this.el.removeClass('x-item-disabled');
12778 onShow : function(){
12779 var ae = this.getActionEl();
12782 ae.dom.style.display = '';
12783 ae.dom.style.visibility = 'visible';
12789 onHide : function(){
12790 var ae = this.getActionEl();
12791 ae.dom.style.display = 'none';
12795 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12796 * by an implementing function.
12798 * @param {EventObject} e
12800 onTriggerClick : Roo.emptyFn
12808 * @class Roo.bootstrap.CardUploader
12809 * @extends Roo.bootstrap.Button
12810 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12811 * @cfg {Number} errorTimeout default 3000
12812 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12813 * @cfg {Array} html The button text.
12817 * Create a new CardUploader
12818 * @param {Object} config The config object
12821 Roo.bootstrap.CardUploader = function(config){
12825 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12828 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12836 * When a image is clicked on - and needs to display a slideshow or similar..
12837 * @param {Roo.bootstrap.Card} this
12838 * @param {Object} The image information data
12844 * When a the download link is clicked
12845 * @param {Roo.bootstrap.Card} this
12846 * @param {Object} The image information data contains
12853 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12856 errorTimeout : 3000,
12860 fileCollection : false,
12863 getAutoCreate : function()
12867 cls :'form-group' ,
12872 //cls : 'input-group-addon',
12873 html : this.fieldLabel
12881 value : this.value,
12882 cls : 'd-none form-control'
12887 multiple : 'multiple',
12889 cls : 'd-none roo-card-upload-selector'
12893 cls : 'roo-card-uploader-button-container w-100 mb-2'
12896 cls : 'card-columns roo-card-uploader-container'
12906 getChildContainer : function() /// what children are added to.
12908 return this.containerEl;
12911 getButtonContainer : function() /// what children are added to.
12913 return this.el.select(".roo-card-uploader-button-container").first();
12916 initEvents : function()
12919 Roo.bootstrap.Input.prototype.initEvents.call(this);
12923 xns: Roo.bootstrap,
12926 container_method : 'getButtonContainer' ,
12927 html : this.html, // fix changable?
12930 'click' : function(btn, e) {
12939 this.urlAPI = (window.createObjectURL && window) ||
12940 (window.URL && URL.revokeObjectURL && URL) ||
12941 (window.webkitURL && webkitURL);
12946 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12948 this.selectorEl.on('change', this.onFileSelected, this);
12951 this.images.forEach(function(img) {
12954 this.images = false;
12956 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12962 onClick : function(e)
12964 e.preventDefault();
12966 this.selectorEl.dom.click();
12970 onFileSelected : function(e)
12972 e.preventDefault();
12974 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12978 Roo.each(this.selectorEl.dom.files, function(file){
12979 this.addFile(file);
12988 addFile : function(file)
12991 if(typeof(file) === 'string'){
12992 throw "Add file by name?"; // should not happen
12996 if(!file || !this.urlAPI){
13006 var url = _this.urlAPI.createObjectURL( file);
13009 id : Roo.bootstrap.CardUploader.ID--,
13010 is_uploaded : false,
13014 mimetype : file.type,
13022 * addCard - add an Attachment to the uploader
13023 * @param data - the data about the image to upload
13027 title : "Title of file",
13028 is_uploaded : false,
13029 src : "http://.....",
13030 srcfile : { the File upload object },
13031 mimetype : file.type,
13034 .. any other data...
13040 addCard : function (data)
13042 // hidden input element?
13043 // if the file is not an image...
13044 //then we need to use something other that and header_image
13049 xns : Roo.bootstrap,
13050 xtype : 'CardFooter',
13053 xns : Roo.bootstrap,
13059 xns : Roo.bootstrap,
13061 html : String.format("<small>{0}</small>", data.title),
13062 cls : 'col-10 text-left',
13067 click : function() {
13069 t.fireEvent( "download", t, data );
13075 xns : Roo.bootstrap,
13077 style: 'max-height: 28px; ',
13083 click : function() {
13084 t.removeCard(data.id)
13096 var cn = this.addxtype(
13099 xns : Roo.bootstrap,
13102 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13103 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13104 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13109 initEvents : function() {
13110 Roo.bootstrap.Card.prototype.initEvents.call(this);
13112 this.imgEl = this.el.select('.card-img-top').first();
13114 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13115 this.imgEl.set({ 'pointer' : 'cursor' });
13118 this.getCardFooter().addClass('p-1');
13125 // dont' really need ot update items.
13126 // this.items.push(cn);
13127 this.fileCollection.add(cn);
13129 if (!data.srcfile) {
13130 this.updateInput();
13135 var reader = new FileReader();
13136 reader.addEventListener("load", function() {
13137 data.srcdata = reader.result;
13140 reader.readAsDataURL(data.srcfile);
13145 removeCard : function(id)
13148 var card = this.fileCollection.get(id);
13149 card.data.is_deleted = 1;
13150 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13151 //this.fileCollection.remove(card);
13152 //this.items = this.items.filter(function(e) { return e != card });
13153 // dont' really need ot update items.
13154 card.el.dom.parentNode.removeChild(card.el.dom);
13155 this.updateInput();
13161 this.fileCollection.each(function(card) {
13162 if (card.el.dom && card.el.dom.parentNode) {
13163 card.el.dom.parentNode.removeChild(card.el.dom);
13166 this.fileCollection.clear();
13167 this.updateInput();
13170 updateInput : function()
13173 this.fileCollection.each(function(e) {
13177 this.inputEl().dom.value = JSON.stringify(data);
13187 Roo.bootstrap.CardUploader.ID = -1;/*
13189 * Ext JS Library 1.1.1
13190 * Copyright(c) 2006-2007, Ext JS, LLC.
13192 * Originally Released Under LGPL - original licence link has changed is not relivant.
13195 * <script type="text/javascript">
13200 * @class Roo.data.SortTypes
13202 * Defines the default sorting (casting?) comparison functions used when sorting data.
13204 Roo.data.SortTypes = {
13206 * Default sort that does nothing
13207 * @param {Mixed} s The value being converted
13208 * @return {Mixed} The comparison value
13210 none : function(s){
13215 * The regular expression used to strip tags
13219 stripTagsRE : /<\/?[^>]+>/gi,
13222 * Strips all HTML tags to sort on text only
13223 * @param {Mixed} s The value being converted
13224 * @return {String} The comparison value
13226 asText : function(s){
13227 return String(s).replace(this.stripTagsRE, "");
13231 * Strips all HTML tags to sort on text only - Case insensitive
13232 * @param {Mixed} s The value being converted
13233 * @return {String} The comparison value
13235 asUCText : function(s){
13236 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13240 * Case insensitive string
13241 * @param {Mixed} s The value being converted
13242 * @return {String} The comparison value
13244 asUCString : function(s) {
13245 return String(s).toUpperCase();
13250 * @param {Mixed} s The value being converted
13251 * @return {Number} The comparison value
13253 asDate : function(s) {
13257 if(s instanceof Date){
13258 return s.getTime();
13260 return Date.parse(String(s));
13265 * @param {Mixed} s The value being converted
13266 * @return {Float} The comparison value
13268 asFloat : function(s) {
13269 var val = parseFloat(String(s).replace(/,/g, ""));
13278 * @param {Mixed} s The value being converted
13279 * @return {Number} The comparison value
13281 asInt : function(s) {
13282 var val = parseInt(String(s).replace(/,/g, ""));
13290 * Ext JS Library 1.1.1
13291 * Copyright(c) 2006-2007, Ext JS, LLC.
13293 * Originally Released Under LGPL - original licence link has changed is not relivant.
13296 * <script type="text/javascript">
13300 * @class Roo.data.Record
13301 * Instances of this class encapsulate both record <em>definition</em> information, and record
13302 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13303 * to access Records cached in an {@link Roo.data.Store} object.<br>
13305 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13306 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13309 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13311 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13312 * {@link #create}. The parameters are the same.
13313 * @param {Array} data An associative Array of data values keyed by the field name.
13314 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13315 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13316 * not specified an integer id is generated.
13318 Roo.data.Record = function(data, id){
13319 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13324 * Generate a constructor for a specific record layout.
13325 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13326 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13327 * Each field definition object may contain the following properties: <ul>
13328 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
13329 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13330 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13331 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13332 * is being used, then this is a string containing the javascript expression to reference the data relative to
13333 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13334 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13335 * this may be omitted.</p></li>
13336 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13337 * <ul><li>auto (Default, implies no conversion)</li>
13342 * <li>date</li></ul></p></li>
13343 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13344 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13345 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13346 * by the Reader into an object that will be stored in the Record. It is passed the
13347 * following parameters:<ul>
13348 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13350 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13352 * <br>usage:<br><pre><code>
13353 var TopicRecord = Roo.data.Record.create(
13354 {name: 'title', mapping: 'topic_title'},
13355 {name: 'author', mapping: 'username'},
13356 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13357 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13358 {name: 'lastPoster', mapping: 'user2'},
13359 {name: 'excerpt', mapping: 'post_text'}
13362 var myNewRecord = new TopicRecord({
13363 title: 'Do my job please',
13366 lastPost: new Date(),
13367 lastPoster: 'Animal',
13368 excerpt: 'No way dude!'
13370 myStore.add(myNewRecord);
13375 Roo.data.Record.create = function(o){
13376 var f = function(){
13377 f.superclass.constructor.apply(this, arguments);
13379 Roo.extend(f, Roo.data.Record);
13380 var p = f.prototype;
13381 p.fields = new Roo.util.MixedCollection(false, function(field){
13384 for(var i = 0, len = o.length; i < len; i++){
13385 p.fields.add(new Roo.data.Field(o[i]));
13387 f.getField = function(name){
13388 return p.fields.get(name);
13393 Roo.data.Record.AUTO_ID = 1000;
13394 Roo.data.Record.EDIT = 'edit';
13395 Roo.data.Record.REJECT = 'reject';
13396 Roo.data.Record.COMMIT = 'commit';
13398 Roo.data.Record.prototype = {
13400 * Readonly flag - true if this record has been modified.
13409 join : function(store){
13410 this.store = store;
13414 * Set the named field to the specified value.
13415 * @param {String} name The name of the field to set.
13416 * @param {Object} value The value to set the field to.
13418 set : function(name, value){
13419 if(this.data[name] == value){
13423 if(!this.modified){
13424 this.modified = {};
13426 if(typeof this.modified[name] == 'undefined'){
13427 this.modified[name] = this.data[name];
13429 this.data[name] = value;
13430 if(!this.editing && this.store){
13431 this.store.afterEdit(this);
13436 * Get the value of the named field.
13437 * @param {String} name The name of the field to get the value of.
13438 * @return {Object} The value of the field.
13440 get : function(name){
13441 return this.data[name];
13445 beginEdit : function(){
13446 this.editing = true;
13447 this.modified = {};
13451 cancelEdit : function(){
13452 this.editing = false;
13453 delete this.modified;
13457 endEdit : function(){
13458 this.editing = false;
13459 if(this.dirty && this.store){
13460 this.store.afterEdit(this);
13465 * Usually called by the {@link Roo.data.Store} which owns the Record.
13466 * Rejects all changes made to the Record since either creation, or the last commit operation.
13467 * Modified fields are reverted to their original values.
13469 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13470 * of reject operations.
13472 reject : function(){
13473 var m = this.modified;
13475 if(typeof m[n] != "function"){
13476 this.data[n] = m[n];
13479 this.dirty = false;
13480 delete this.modified;
13481 this.editing = false;
13483 this.store.afterReject(this);
13488 * Usually called by the {@link Roo.data.Store} which owns the Record.
13489 * Commits all changes made to the Record since either creation, or the last commit operation.
13491 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13492 * of commit operations.
13494 commit : function(){
13495 this.dirty = false;
13496 delete this.modified;
13497 this.editing = false;
13499 this.store.afterCommit(this);
13504 hasError : function(){
13505 return this.error != null;
13509 clearError : function(){
13514 * Creates a copy of this record.
13515 * @param {String} id (optional) A new record id if you don't want to use this record's id
13518 copy : function(newId) {
13519 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13523 * Ext JS Library 1.1.1
13524 * Copyright(c) 2006-2007, Ext JS, LLC.
13526 * Originally Released Under LGPL - original licence link has changed is not relivant.
13529 * <script type="text/javascript">
13535 * @class Roo.data.Store
13536 * @extends Roo.util.Observable
13537 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13538 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13540 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13541 * has no knowledge of the format of the data returned by the Proxy.<br>
13543 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13544 * instances from the data object. These records are cached and made available through accessor functions.
13546 * Creates a new Store.
13547 * @param {Object} config A config object containing the objects needed for the Store to access data,
13548 * and read the data into Records.
13550 Roo.data.Store = function(config){
13551 this.data = new Roo.util.MixedCollection(false);
13552 this.data.getKey = function(o){
13555 this.baseParams = {};
13557 this.paramNames = {
13562 "multisort" : "_multisort"
13565 if(config && config.data){
13566 this.inlineData = config.data;
13567 delete config.data;
13570 Roo.apply(this, config);
13572 if(this.reader){ // reader passed
13573 this.reader = Roo.factory(this.reader, Roo.data);
13574 this.reader.xmodule = this.xmodule || false;
13575 if(!this.recordType){
13576 this.recordType = this.reader.recordType;
13578 if(this.reader.onMetaChange){
13579 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13583 if(this.recordType){
13584 this.fields = this.recordType.prototype.fields;
13586 this.modified = [];
13590 * @event datachanged
13591 * Fires when the data cache has changed, and a widget which is using this Store
13592 * as a Record cache should refresh its view.
13593 * @param {Store} this
13595 datachanged : true,
13597 * @event metachange
13598 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13599 * @param {Store} this
13600 * @param {Object} meta The JSON metadata
13605 * Fires when Records have been added to the Store
13606 * @param {Store} this
13607 * @param {Roo.data.Record[]} records The array of Records added
13608 * @param {Number} index The index at which the record(s) were added
13613 * Fires when a Record has been removed from the Store
13614 * @param {Store} this
13615 * @param {Roo.data.Record} record The Record that was removed
13616 * @param {Number} index The index at which the record was removed
13621 * Fires when a Record has been updated
13622 * @param {Store} this
13623 * @param {Roo.data.Record} record The Record that was updated
13624 * @param {String} operation The update operation being performed. Value may be one of:
13626 Roo.data.Record.EDIT
13627 Roo.data.Record.REJECT
13628 Roo.data.Record.COMMIT
13634 * Fires when the data cache has been cleared.
13635 * @param {Store} this
13639 * @event beforeload
13640 * Fires before a request is made for a new data object. If the beforeload handler returns false
13641 * the load action will be canceled.
13642 * @param {Store} this
13643 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13647 * @event beforeloadadd
13648 * Fires after a new set of Records has been loaded.
13649 * @param {Store} this
13650 * @param {Roo.data.Record[]} records The Records that were loaded
13651 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13653 beforeloadadd : true,
13656 * Fires after a new set of Records has been loaded, before they are added to the store.
13657 * @param {Store} this
13658 * @param {Roo.data.Record[]} records The Records that were loaded
13659 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13660 * @params {Object} return from reader
13664 * @event loadexception
13665 * Fires if an exception occurs in the Proxy during loading.
13666 * Called with the signature of the Proxy's "loadexception" event.
13667 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13670 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13671 * @param {Object} load options
13672 * @param {Object} jsonData from your request (normally this contains the Exception)
13674 loadexception : true
13678 this.proxy = Roo.factory(this.proxy, Roo.data);
13679 this.proxy.xmodule = this.xmodule || false;
13680 this.relayEvents(this.proxy, ["loadexception"]);
13682 this.sortToggle = {};
13683 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13685 Roo.data.Store.superclass.constructor.call(this);
13687 if(this.inlineData){
13688 this.loadData(this.inlineData);
13689 delete this.inlineData;
13693 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13695 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13696 * without a remote query - used by combo/forms at present.
13700 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13703 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13706 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13707 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13710 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13711 * on any HTTP request
13714 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13717 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13721 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13722 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13724 remoteSort : false,
13727 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13728 * loaded or when a record is removed. (defaults to false).
13730 pruneModifiedRecords : false,
13733 lastOptions : null,
13736 * Add Records to the Store and fires the add event.
13737 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13739 add : function(records){
13740 records = [].concat(records);
13741 for(var i = 0, len = records.length; i < len; i++){
13742 records[i].join(this);
13744 var index = this.data.length;
13745 this.data.addAll(records);
13746 this.fireEvent("add", this, records, index);
13750 * Remove a Record from the Store and fires the remove event.
13751 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13753 remove : function(record){
13754 var index = this.data.indexOf(record);
13755 this.data.removeAt(index);
13757 if(this.pruneModifiedRecords){
13758 this.modified.remove(record);
13760 this.fireEvent("remove", this, record, index);
13764 * Remove all Records from the Store and fires the clear event.
13766 removeAll : function(){
13768 if(this.pruneModifiedRecords){
13769 this.modified = [];
13771 this.fireEvent("clear", this);
13775 * Inserts Records to the Store at the given index and fires the add event.
13776 * @param {Number} index The start index at which to insert the passed Records.
13777 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13779 insert : function(index, records){
13780 records = [].concat(records);
13781 for(var i = 0, len = records.length; i < len; i++){
13782 this.data.insert(index, records[i]);
13783 records[i].join(this);
13785 this.fireEvent("add", this, records, index);
13789 * Get the index within the cache of the passed Record.
13790 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13791 * @return {Number} The index of the passed Record. Returns -1 if not found.
13793 indexOf : function(record){
13794 return this.data.indexOf(record);
13798 * Get the index within the cache of the Record with the passed id.
13799 * @param {String} id The id of the Record to find.
13800 * @return {Number} The index of the Record. Returns -1 if not found.
13802 indexOfId : function(id){
13803 return this.data.indexOfKey(id);
13807 * Get the Record with the specified id.
13808 * @param {String} id The id of the Record to find.
13809 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13811 getById : function(id){
13812 return this.data.key(id);
13816 * Get the Record at the specified index.
13817 * @param {Number} index The index of the Record to find.
13818 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13820 getAt : function(index){
13821 return this.data.itemAt(index);
13825 * Returns a range of Records between specified indices.
13826 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13827 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13828 * @return {Roo.data.Record[]} An array of Records
13830 getRange : function(start, end){
13831 return this.data.getRange(start, end);
13835 storeOptions : function(o){
13836 o = Roo.apply({}, o);
13839 this.lastOptions = o;
13843 * Loads the Record cache from the configured Proxy using the configured Reader.
13845 * If using remote paging, then the first load call must specify the <em>start</em>
13846 * and <em>limit</em> properties in the options.params property to establish the initial
13847 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13849 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13850 * and this call will return before the new data has been loaded. Perform any post-processing
13851 * in a callback function, or in a "load" event handler.</strong>
13853 * @param {Object} options An object containing properties which control loading options:<ul>
13854 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13855 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13856 * passed the following arguments:<ul>
13857 * <li>r : Roo.data.Record[]</li>
13858 * <li>options: Options object from the load call</li>
13859 * <li>success: Boolean success indicator</li></ul></li>
13860 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13861 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13864 load : function(options){
13865 options = options || {};
13866 if(this.fireEvent("beforeload", this, options) !== false){
13867 this.storeOptions(options);
13868 var p = Roo.apply(options.params || {}, this.baseParams);
13869 // if meta was not loaded from remote source.. try requesting it.
13870 if (!this.reader.metaFromRemote) {
13871 p._requestMeta = 1;
13873 if(this.sortInfo && this.remoteSort){
13874 var pn = this.paramNames;
13875 p[pn["sort"]] = this.sortInfo.field;
13876 p[pn["dir"]] = this.sortInfo.direction;
13878 if (this.multiSort) {
13879 var pn = this.paramNames;
13880 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13883 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13888 * Reloads the Record cache from the configured Proxy using the configured Reader and
13889 * the options from the last load operation performed.
13890 * @param {Object} options (optional) An object containing properties which may override the options
13891 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13892 * the most recently used options are reused).
13894 reload : function(options){
13895 this.load(Roo.applyIf(options||{}, this.lastOptions));
13899 // Called as a callback by the Reader during a load operation.
13900 loadRecords : function(o, options, success){
13901 if(!o || success === false){
13902 if(success !== false){
13903 this.fireEvent("load", this, [], options, o);
13905 if(options.callback){
13906 options.callback.call(options.scope || this, [], options, false);
13910 // if data returned failure - throw an exception.
13911 if (o.success === false) {
13912 // show a message if no listener is registered.
13913 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13914 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13916 // loadmask wil be hooked into this..
13917 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13920 var r = o.records, t = o.totalRecords || r.length;
13922 this.fireEvent("beforeloadadd", this, r, options, o);
13924 if(!options || options.add !== true){
13925 if(this.pruneModifiedRecords){
13926 this.modified = [];
13928 for(var i = 0, len = r.length; i < len; i++){
13932 this.data = this.snapshot;
13933 delete this.snapshot;
13936 this.data.addAll(r);
13937 this.totalLength = t;
13939 this.fireEvent("datachanged", this);
13941 this.totalLength = Math.max(t, this.data.length+r.length);
13945 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13947 var e = new Roo.data.Record({});
13949 e.set(this.parent.displayField, this.parent.emptyTitle);
13950 e.set(this.parent.valueField, '');
13955 this.fireEvent("load", this, r, options, o);
13956 if(options.callback){
13957 options.callback.call(options.scope || this, r, options, true);
13963 * Loads data from a passed data block. A Reader which understands the format of the data
13964 * must have been configured in the constructor.
13965 * @param {Object} data The data block from which to read the Records. The format of the data expected
13966 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13967 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13969 loadData : function(o, append){
13970 var r = this.reader.readRecords(o);
13971 this.loadRecords(r, {add: append}, true);
13975 * using 'cn' the nested child reader read the child array into it's child stores.
13976 * @param {Object} rec The record with a 'children array
13978 loadDataFromChildren : function(rec)
13980 this.loadData(this.reader.toLoadData(rec));
13985 * Gets the number of cached records.
13987 * <em>If using paging, this may not be the total size of the dataset. If the data object
13988 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13989 * the data set size</em>
13991 getCount : function(){
13992 return this.data.length || 0;
13996 * Gets the total number of records in the dataset as returned by the server.
13998 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13999 * the dataset size</em>
14001 getTotalCount : function(){
14002 return this.totalLength || 0;
14006 * Returns the sort state of the Store as an object with two properties:
14008 field {String} The name of the field by which the Records are sorted
14009 direction {String} The sort order, "ASC" or "DESC"
14012 getSortState : function(){
14013 return this.sortInfo;
14017 applySort : function(){
14018 if(this.sortInfo && !this.remoteSort){
14019 var s = this.sortInfo, f = s.field;
14020 var st = this.fields.get(f).sortType;
14021 var fn = function(r1, r2){
14022 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14023 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14025 this.data.sort(s.direction, fn);
14026 if(this.snapshot && this.snapshot != this.data){
14027 this.snapshot.sort(s.direction, fn);
14033 * Sets the default sort column and order to be used by the next load operation.
14034 * @param {String} fieldName The name of the field to sort by.
14035 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14037 setDefaultSort : function(field, dir){
14038 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14042 * Sort the Records.
14043 * If remote sorting is used, the sort is performed on the server, and the cache is
14044 * reloaded. If local sorting is used, the cache is sorted internally.
14045 * @param {String} fieldName The name of the field to sort by.
14046 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14048 sort : function(fieldName, dir){
14049 var f = this.fields.get(fieldName);
14051 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14053 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14054 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14059 this.sortToggle[f.name] = dir;
14060 this.sortInfo = {field: f.name, direction: dir};
14061 if(!this.remoteSort){
14063 this.fireEvent("datachanged", this);
14065 this.load(this.lastOptions);
14070 * Calls the specified function for each of the Records in the cache.
14071 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14072 * Returning <em>false</em> aborts and exits the iteration.
14073 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14075 each : function(fn, scope){
14076 this.data.each(fn, scope);
14080 * Gets all records modified since the last commit. Modified records are persisted across load operations
14081 * (e.g., during paging).
14082 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14084 getModifiedRecords : function(){
14085 return this.modified;
14089 createFilterFn : function(property, value, anyMatch){
14090 if(!value.exec){ // not a regex
14091 value = String(value);
14092 if(value.length == 0){
14095 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14097 return function(r){
14098 return value.test(r.data[property]);
14103 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14104 * @param {String} property A field on your records
14105 * @param {Number} start The record index to start at (defaults to 0)
14106 * @param {Number} end The last record index to include (defaults to length - 1)
14107 * @return {Number} The sum
14109 sum : function(property, start, end){
14110 var rs = this.data.items, v = 0;
14111 start = start || 0;
14112 end = (end || end === 0) ? end : rs.length-1;
14114 for(var i = start; i <= end; i++){
14115 v += (rs[i].data[property] || 0);
14121 * Filter the records by a specified property.
14122 * @param {String} field A field on your records
14123 * @param {String/RegExp} value Either a string that the field
14124 * should start with or a RegExp to test against the field
14125 * @param {Boolean} anyMatch True to match any part not just the beginning
14127 filter : function(property, value, anyMatch){
14128 var fn = this.createFilterFn(property, value, anyMatch);
14129 return fn ? this.filterBy(fn) : this.clearFilter();
14133 * Filter by a function. The specified function will be called with each
14134 * record in this data source. If the function returns true the record is included,
14135 * otherwise it is filtered.
14136 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14137 * @param {Object} scope (optional) The scope of the function (defaults to this)
14139 filterBy : function(fn, scope){
14140 this.snapshot = this.snapshot || this.data;
14141 this.data = this.queryBy(fn, scope||this);
14142 this.fireEvent("datachanged", this);
14146 * Query the records by a specified property.
14147 * @param {String} field A field on your records
14148 * @param {String/RegExp} value Either a string that the field
14149 * should start with or a RegExp to test against the field
14150 * @param {Boolean} anyMatch True to match any part not just the beginning
14151 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14153 query : function(property, value, anyMatch){
14154 var fn = this.createFilterFn(property, value, anyMatch);
14155 return fn ? this.queryBy(fn) : this.data.clone();
14159 * Query by a function. The specified function will be called with each
14160 * record in this data source. If the function returns true the record is included
14162 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14163 * @param {Object} scope (optional) The scope of the function (defaults to this)
14164 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14166 queryBy : function(fn, scope){
14167 var data = this.snapshot || this.data;
14168 return data.filterBy(fn, scope||this);
14172 * Collects unique values for a particular dataIndex from this store.
14173 * @param {String} dataIndex The property to collect
14174 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14175 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14176 * @return {Array} An array of the unique values
14178 collect : function(dataIndex, allowNull, bypassFilter){
14179 var d = (bypassFilter === true && this.snapshot) ?
14180 this.snapshot.items : this.data.items;
14181 var v, sv, r = [], l = {};
14182 for(var i = 0, len = d.length; i < len; i++){
14183 v = d[i].data[dataIndex];
14185 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14194 * Revert to a view of the Record cache with no filtering applied.
14195 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14197 clearFilter : function(suppressEvent){
14198 if(this.snapshot && this.snapshot != this.data){
14199 this.data = this.snapshot;
14200 delete this.snapshot;
14201 if(suppressEvent !== true){
14202 this.fireEvent("datachanged", this);
14208 afterEdit : function(record){
14209 if(this.modified.indexOf(record) == -1){
14210 this.modified.push(record);
14212 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14216 afterReject : function(record){
14217 this.modified.remove(record);
14218 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14222 afterCommit : function(record){
14223 this.modified.remove(record);
14224 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14228 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14229 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14231 commitChanges : function(){
14232 var m = this.modified.slice(0);
14233 this.modified = [];
14234 for(var i = 0, len = m.length; i < len; i++){
14240 * Cancel outstanding changes on all changed records.
14242 rejectChanges : function(){
14243 var m = this.modified.slice(0);
14244 this.modified = [];
14245 for(var i = 0, len = m.length; i < len; i++){
14250 onMetaChange : function(meta, rtype, o){
14251 this.recordType = rtype;
14252 this.fields = rtype.prototype.fields;
14253 delete this.snapshot;
14254 this.sortInfo = meta.sortInfo || this.sortInfo;
14255 this.modified = [];
14256 this.fireEvent('metachange', this, this.reader.meta);
14259 moveIndex : function(data, type)
14261 var index = this.indexOf(data);
14263 var newIndex = index + type;
14267 this.insert(newIndex, data);
14272 * Ext JS Library 1.1.1
14273 * Copyright(c) 2006-2007, Ext JS, LLC.
14275 * Originally Released Under LGPL - original licence link has changed is not relivant.
14278 * <script type="text/javascript">
14282 * @class Roo.data.SimpleStore
14283 * @extends Roo.data.Store
14284 * Small helper class to make creating Stores from Array data easier.
14285 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14286 * @cfg {Array} fields An array of field definition objects, or field name strings.
14287 * @cfg {Object} an existing reader (eg. copied from another store)
14288 * @cfg {Array} data The multi-dimensional array of data
14290 * @param {Object} config
14292 Roo.data.SimpleStore = function(config)
14294 Roo.data.SimpleStore.superclass.constructor.call(this, {
14296 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14299 Roo.data.Record.create(config.fields)
14301 proxy : new Roo.data.MemoryProxy(config.data)
14305 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14307 * Ext JS Library 1.1.1
14308 * Copyright(c) 2006-2007, Ext JS, LLC.
14310 * Originally Released Under LGPL - original licence link has changed is not relivant.
14313 * <script type="text/javascript">
14318 * @extends Roo.data.Store
14319 * @class Roo.data.JsonStore
14320 * Small helper class to make creating Stores for JSON data easier. <br/>
14322 var store = new Roo.data.JsonStore({
14323 url: 'get-images.php',
14325 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14328 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14329 * JsonReader and HttpProxy (unless inline data is provided).</b>
14330 * @cfg {Array} fields An array of field definition objects, or field name strings.
14332 * @param {Object} config
14334 Roo.data.JsonStore = function(c){
14335 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14336 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14337 reader: new Roo.data.JsonReader(c, c.fields)
14340 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14342 * Ext JS Library 1.1.1
14343 * Copyright(c) 2006-2007, Ext JS, LLC.
14345 * Originally Released Under LGPL - original licence link has changed is not relivant.
14348 * <script type="text/javascript">
14352 Roo.data.Field = function(config){
14353 if(typeof config == "string"){
14354 config = {name: config};
14356 Roo.apply(this, config);
14359 this.type = "auto";
14362 var st = Roo.data.SortTypes;
14363 // named sortTypes are supported, here we look them up
14364 if(typeof this.sortType == "string"){
14365 this.sortType = st[this.sortType];
14368 // set default sortType for strings and dates
14369 if(!this.sortType){
14372 this.sortType = st.asUCString;
14375 this.sortType = st.asDate;
14378 this.sortType = st.none;
14383 var stripRe = /[\$,%]/g;
14385 // prebuilt conversion function for this field, instead of
14386 // switching every time we're reading a value
14388 var cv, dateFormat = this.dateFormat;
14393 cv = function(v){ return v; };
14396 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14400 return v !== undefined && v !== null && v !== '' ?
14401 parseInt(String(v).replace(stripRe, ""), 10) : '';
14406 return v !== undefined && v !== null && v !== '' ?
14407 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14412 cv = function(v){ return v === true || v === "true" || v == 1; };
14419 if(v instanceof Date){
14423 if(dateFormat == "timestamp"){
14424 return new Date(v*1000);
14426 return Date.parseDate(v, dateFormat);
14428 var parsed = Date.parse(v);
14429 return parsed ? new Date(parsed) : null;
14438 Roo.data.Field.prototype = {
14446 * Ext JS Library 1.1.1
14447 * Copyright(c) 2006-2007, Ext JS, LLC.
14449 * Originally Released Under LGPL - original licence link has changed is not relivant.
14452 * <script type="text/javascript">
14455 // Base class for reading structured data from a data source. This class is intended to be
14456 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14459 * @class Roo.data.DataReader
14460 * Base class for reading structured data from a data source. This class is intended to be
14461 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14464 Roo.data.DataReader = function(meta, recordType){
14468 this.recordType = recordType instanceof Array ?
14469 Roo.data.Record.create(recordType) : recordType;
14472 Roo.data.DataReader.prototype = {
14475 readerType : 'Data',
14477 * Create an empty record
14478 * @param {Object} data (optional) - overlay some values
14479 * @return {Roo.data.Record} record created.
14481 newRow : function(d) {
14483 this.recordType.prototype.fields.each(function(c) {
14485 case 'int' : da[c.name] = 0; break;
14486 case 'date' : da[c.name] = new Date(); break;
14487 case 'float' : da[c.name] = 0.0; break;
14488 case 'boolean' : da[c.name] = false; break;
14489 default : da[c.name] = ""; break;
14493 return new this.recordType(Roo.apply(da, d));
14499 * Ext JS Library 1.1.1
14500 * Copyright(c) 2006-2007, Ext JS, LLC.
14502 * Originally Released Under LGPL - original licence link has changed is not relivant.
14505 * <script type="text/javascript">
14509 * @class Roo.data.DataProxy
14510 * @extends Roo.data.Observable
14511 * This class is an abstract base class for implementations which provide retrieval of
14512 * unformatted data objects.<br>
14514 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14515 * (of the appropriate type which knows how to parse the data object) to provide a block of
14516 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14518 * Custom implementations must implement the load method as described in
14519 * {@link Roo.data.HttpProxy#load}.
14521 Roo.data.DataProxy = function(){
14524 * @event beforeload
14525 * Fires before a network request is made to retrieve a data object.
14526 * @param {Object} This DataProxy object.
14527 * @param {Object} params The params parameter to the load function.
14532 * Fires before the load method's callback is called.
14533 * @param {Object} This DataProxy object.
14534 * @param {Object} o The data object.
14535 * @param {Object} arg The callback argument object passed to the load function.
14539 * @event loadexception
14540 * Fires if an Exception occurs during data retrieval.
14541 * @param {Object} This DataProxy object.
14542 * @param {Object} o The data object.
14543 * @param {Object} arg The callback argument object passed to the load function.
14544 * @param {Object} e The Exception.
14546 loadexception : true
14548 Roo.data.DataProxy.superclass.constructor.call(this);
14551 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14554 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14558 * Ext JS Library 1.1.1
14559 * Copyright(c) 2006-2007, Ext JS, LLC.
14561 * Originally Released Under LGPL - original licence link has changed is not relivant.
14564 * <script type="text/javascript">
14567 * @class Roo.data.MemoryProxy
14568 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14569 * to the Reader when its load method is called.
14571 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14573 Roo.data.MemoryProxy = function(data){
14577 Roo.data.MemoryProxy.superclass.constructor.call(this);
14581 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14584 * Load data from the requested source (in this case an in-memory
14585 * data object passed to the constructor), read the data object into
14586 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14587 * process that block using the passed callback.
14588 * @param {Object} params This parameter is not used by the MemoryProxy class.
14589 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14590 * object into a block of Roo.data.Records.
14591 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14592 * The function must be passed <ul>
14593 * <li>The Record block object</li>
14594 * <li>The "arg" argument from the load function</li>
14595 * <li>A boolean success indicator</li>
14597 * @param {Object} scope The scope in which to call the callback
14598 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14600 load : function(params, reader, callback, scope, arg){
14601 params = params || {};
14604 result = reader.readRecords(params.data ? params.data :this.data);
14606 this.fireEvent("loadexception", this, arg, null, e);
14607 callback.call(scope, null, arg, false);
14610 callback.call(scope, result, arg, true);
14614 update : function(params, records){
14619 * Ext JS Library 1.1.1
14620 * Copyright(c) 2006-2007, Ext JS, LLC.
14622 * Originally Released Under LGPL - original licence link has changed is not relivant.
14625 * <script type="text/javascript">
14628 * @class Roo.data.HttpProxy
14629 * @extends Roo.data.DataProxy
14630 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14631 * configured to reference a certain URL.<br><br>
14633 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14634 * from which the running page was served.<br><br>
14636 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14638 * Be aware that to enable the browser to parse an XML document, the server must set
14639 * the Content-Type header in the HTTP response to "text/xml".
14641 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14642 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14643 * will be used to make the request.
14645 Roo.data.HttpProxy = function(conn){
14646 Roo.data.HttpProxy.superclass.constructor.call(this);
14647 // is conn a conn config or a real conn?
14649 this.useAjax = !conn || !conn.events;
14653 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14654 // thse are take from connection...
14657 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14660 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14661 * extra parameters to each request made by this object. (defaults to undefined)
14664 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14665 * to each request made by this object. (defaults to undefined)
14668 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14671 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14674 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14680 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14684 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14685 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14686 * a finer-grained basis than the DataProxy events.
14688 getConnection : function(){
14689 return this.useAjax ? Roo.Ajax : this.conn;
14693 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14694 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14695 * process that block using the passed callback.
14696 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14697 * for the request to the remote server.
14698 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14699 * object into a block of Roo.data.Records.
14700 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14701 * The function must be passed <ul>
14702 * <li>The Record block object</li>
14703 * <li>The "arg" argument from the load function</li>
14704 * <li>A boolean success indicator</li>
14706 * @param {Object} scope The scope in which to call the callback
14707 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14709 load : function(params, reader, callback, scope, arg){
14710 if(this.fireEvent("beforeload", this, params) !== false){
14712 params : params || {},
14714 callback : callback,
14719 callback : this.loadResponse,
14723 Roo.applyIf(o, this.conn);
14724 if(this.activeRequest){
14725 Roo.Ajax.abort(this.activeRequest);
14727 this.activeRequest = Roo.Ajax.request(o);
14729 this.conn.request(o);
14732 callback.call(scope||this, null, arg, false);
14737 loadResponse : function(o, success, response){
14738 delete this.activeRequest;
14740 this.fireEvent("loadexception", this, o, response);
14741 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14746 result = o.reader.read(response);
14748 this.fireEvent("loadexception", this, o, response, e);
14749 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14753 this.fireEvent("load", this, o, o.request.arg);
14754 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14758 update : function(dataSet){
14763 updateResponse : function(dataSet){
14768 * Ext JS Library 1.1.1
14769 * Copyright(c) 2006-2007, Ext JS, LLC.
14771 * Originally Released Under LGPL - original licence link has changed is not relivant.
14774 * <script type="text/javascript">
14778 * @class Roo.data.ScriptTagProxy
14779 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14780 * other than the originating domain of the running page.<br><br>
14782 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14783 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14785 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14786 * source code that is used as the source inside a <script> tag.<br><br>
14788 * In order for the browser to process the returned data, the server must wrap the data object
14789 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14790 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14791 * depending on whether the callback name was passed:
14794 boolean scriptTag = false;
14795 String cb = request.getParameter("callback");
14798 response.setContentType("text/javascript");
14800 response.setContentType("application/x-json");
14802 Writer out = response.getWriter();
14804 out.write(cb + "(");
14806 out.print(dataBlock.toJsonString());
14813 * @param {Object} config A configuration object.
14815 Roo.data.ScriptTagProxy = function(config){
14816 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14817 Roo.apply(this, config);
14818 this.head = document.getElementsByTagName("head")[0];
14821 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14823 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14825 * @cfg {String} url The URL from which to request the data object.
14828 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14832 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14833 * the server the name of the callback function set up by the load call to process the returned data object.
14834 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14835 * javascript output which calls this named function passing the data object as its only parameter.
14837 callbackParam : "callback",
14839 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14840 * name to the request.
14845 * Load data from the configured URL, read the data object into
14846 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14847 * process that block using the passed callback.
14848 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14849 * for the request to the remote server.
14850 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14851 * object into a block of Roo.data.Records.
14852 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14853 * The function must be passed <ul>
14854 * <li>The Record block object</li>
14855 * <li>The "arg" argument from the load function</li>
14856 * <li>A boolean success indicator</li>
14858 * @param {Object} scope The scope in which to call the callback
14859 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14861 load : function(params, reader, callback, scope, arg){
14862 if(this.fireEvent("beforeload", this, params) !== false){
14864 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14866 var url = this.url;
14867 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14869 url += "&_dc=" + (new Date().getTime());
14871 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14874 cb : "stcCallback"+transId,
14875 scriptId : "stcScript"+transId,
14879 callback : callback,
14885 window[trans.cb] = function(o){
14886 conn.handleResponse(o, trans);
14889 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14891 if(this.autoAbort !== false){
14895 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14897 var script = document.createElement("script");
14898 script.setAttribute("src", url);
14899 script.setAttribute("type", "text/javascript");
14900 script.setAttribute("id", trans.scriptId);
14901 this.head.appendChild(script);
14903 this.trans = trans;
14905 callback.call(scope||this, null, arg, false);
14910 isLoading : function(){
14911 return this.trans ? true : false;
14915 * Abort the current server request.
14917 abort : function(){
14918 if(this.isLoading()){
14919 this.destroyTrans(this.trans);
14924 destroyTrans : function(trans, isLoaded){
14925 this.head.removeChild(document.getElementById(trans.scriptId));
14926 clearTimeout(trans.timeoutId);
14928 window[trans.cb] = undefined;
14930 delete window[trans.cb];
14933 // if hasn't been loaded, wait for load to remove it to prevent script error
14934 window[trans.cb] = function(){
14935 window[trans.cb] = undefined;
14937 delete window[trans.cb];
14944 handleResponse : function(o, trans){
14945 this.trans = false;
14946 this.destroyTrans(trans, true);
14949 result = trans.reader.readRecords(o);
14951 this.fireEvent("loadexception", this, o, trans.arg, e);
14952 trans.callback.call(trans.scope||window, null, trans.arg, false);
14955 this.fireEvent("load", this, o, trans.arg);
14956 trans.callback.call(trans.scope||window, result, trans.arg, true);
14960 handleFailure : function(trans){
14961 this.trans = false;
14962 this.destroyTrans(trans, false);
14963 this.fireEvent("loadexception", this, null, trans.arg);
14964 trans.callback.call(trans.scope||window, null, trans.arg, false);
14968 * Ext JS Library 1.1.1
14969 * Copyright(c) 2006-2007, Ext JS, LLC.
14971 * Originally Released Under LGPL - original licence link has changed is not relivant.
14974 * <script type="text/javascript">
14978 * @class Roo.data.JsonReader
14979 * @extends Roo.data.DataReader
14980 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14981 * based on mappings in a provided Roo.data.Record constructor.
14983 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14984 * in the reply previously.
14989 var RecordDef = Roo.data.Record.create([
14990 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14991 {name: 'occupation'} // This field will use "occupation" as the mapping.
14993 var myReader = new Roo.data.JsonReader({
14994 totalProperty: "results", // The property which contains the total dataset size (optional)
14995 root: "rows", // The property which contains an Array of row objects
14996 id: "id" // The property within each row object that provides an ID for the record (optional)
15000 * This would consume a JSON file like this:
15002 { 'results': 2, 'rows': [
15003 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15004 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15007 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15008 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15009 * paged from the remote server.
15010 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15011 * @cfg {String} root name of the property which contains the Array of row objects.
15012 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15013 * @cfg {Array} fields Array of field definition objects
15015 * Create a new JsonReader
15016 * @param {Object} meta Metadata configuration options
15017 * @param {Object} recordType Either an Array of field definition objects,
15018 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15020 Roo.data.JsonReader = function(meta, recordType){
15023 // set some defaults:
15024 Roo.applyIf(meta, {
15025 totalProperty: 'total',
15026 successProperty : 'success',
15031 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15033 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15035 readerType : 'Json',
15038 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15039 * Used by Store query builder to append _requestMeta to params.
15042 metaFromRemote : false,
15044 * This method is only used by a DataProxy which has retrieved data from a remote server.
15045 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15046 * @return {Object} data A data block which is used by an Roo.data.Store object as
15047 * a cache of Roo.data.Records.
15049 read : function(response){
15050 var json = response.responseText;
15052 var o = /* eval:var:o */ eval("("+json+")");
15054 throw {message: "JsonReader.read: Json object not found"};
15060 this.metaFromRemote = true;
15061 this.meta = o.metaData;
15062 this.recordType = Roo.data.Record.create(o.metaData.fields);
15063 this.onMetaChange(this.meta, this.recordType, o);
15065 return this.readRecords(o);
15068 // private function a store will implement
15069 onMetaChange : function(meta, recordType, o){
15076 simpleAccess: function(obj, subsc) {
15083 getJsonAccessor: function(){
15085 return function(expr) {
15087 return(re.test(expr))
15088 ? new Function("obj", "return obj." + expr)
15093 return Roo.emptyFn;
15098 * Create a data block containing Roo.data.Records from an XML document.
15099 * @param {Object} o An object which contains an Array of row objects in the property specified
15100 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15101 * which contains the total size of the dataset.
15102 * @return {Object} data A data block which is used by an Roo.data.Store object as
15103 * a cache of Roo.data.Records.
15105 readRecords : function(o){
15107 * After any data loads, the raw JSON data is available for further custom processing.
15111 var s = this.meta, Record = this.recordType,
15112 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15114 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15116 if(s.totalProperty) {
15117 this.getTotal = this.getJsonAccessor(s.totalProperty);
15119 if(s.successProperty) {
15120 this.getSuccess = this.getJsonAccessor(s.successProperty);
15122 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15124 var g = this.getJsonAccessor(s.id);
15125 this.getId = function(rec) {
15127 return (r === undefined || r === "") ? null : r;
15130 this.getId = function(){return null;};
15133 for(var jj = 0; jj < fl; jj++){
15135 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15136 this.ef[jj] = this.getJsonAccessor(map);
15140 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15141 if(s.totalProperty){
15142 var vt = parseInt(this.getTotal(o), 10);
15147 if(s.successProperty){
15148 var vs = this.getSuccess(o);
15149 if(vs === false || vs === 'false'){
15154 for(var i = 0; i < c; i++){
15157 var id = this.getId(n);
15158 for(var j = 0; j < fl; j++){
15160 var v = this.ef[j](n);
15162 Roo.log('missing convert for ' + f.name);
15166 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15168 var record = new Record(values, id);
15170 records[i] = record;
15176 totalRecords : totalRecords
15179 // used when loading children.. @see loadDataFromChildren
15180 toLoadData: function(rec)
15182 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15183 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15184 return { data : data, total : data.length };
15189 * Ext JS Library 1.1.1
15190 * Copyright(c) 2006-2007, Ext JS, LLC.
15192 * Originally Released Under LGPL - original licence link has changed is not relivant.
15195 * <script type="text/javascript">
15199 * @class Roo.data.ArrayReader
15200 * @extends Roo.data.DataReader
15201 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15202 * Each element of that Array represents a row of data fields. The
15203 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15204 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15208 var RecordDef = Roo.data.Record.create([
15209 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15210 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15212 var myReader = new Roo.data.ArrayReader({
15213 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15217 * This would consume an Array like this:
15219 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15223 * Create a new JsonReader
15224 * @param {Object} meta Metadata configuration options.
15225 * @param {Object|Array} recordType Either an Array of field definition objects
15227 * @cfg {Array} fields Array of field definition objects
15228 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15229 * as specified to {@link Roo.data.Record#create},
15230 * or an {@link Roo.data.Record} object
15233 * created using {@link Roo.data.Record#create}.
15235 Roo.data.ArrayReader = function(meta, recordType)
15237 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15240 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15243 * Create a data block containing Roo.data.Records from an XML document.
15244 * @param {Object} o An Array of row objects which represents the dataset.
15245 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15246 * a cache of Roo.data.Records.
15248 readRecords : function(o)
15250 var sid = this.meta ? this.meta.id : null;
15251 var recordType = this.recordType, fields = recordType.prototype.fields;
15254 for(var i = 0; i < root.length; i++){
15257 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15258 for(var j = 0, jlen = fields.length; j < jlen; j++){
15259 var f = fields.items[j];
15260 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15261 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15263 values[f.name] = v;
15265 var record = new recordType(values, id);
15267 records[records.length] = record;
15271 totalRecords : records.length
15274 // used when loading children.. @see loadDataFromChildren
15275 toLoadData: function(rec)
15277 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15278 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15289 * @class Roo.bootstrap.ComboBox
15290 * @extends Roo.bootstrap.TriggerField
15291 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15292 * @cfg {Boolean} append (true|false) default false
15293 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15294 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15295 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15296 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15297 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15298 * @cfg {Boolean} animate default true
15299 * @cfg {Boolean} emptyResultText only for touch device
15300 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15301 * @cfg {String} emptyTitle default ''
15302 * @cfg {Number} width fixed with? experimental
15304 * Create a new ComboBox.
15305 * @param {Object} config Configuration options
15307 Roo.bootstrap.ComboBox = function(config){
15308 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15312 * Fires when the dropdown list is expanded
15313 * @param {Roo.bootstrap.ComboBox} combo This combo box
15318 * Fires when the dropdown list is collapsed
15319 * @param {Roo.bootstrap.ComboBox} combo This combo box
15323 * @event beforeselect
15324 * Fires before a list item is selected. Return false to cancel the selection.
15325 * @param {Roo.bootstrap.ComboBox} combo This combo box
15326 * @param {Roo.data.Record} record The data record returned from the underlying store
15327 * @param {Number} index The index of the selected item in the dropdown list
15329 'beforeselect' : true,
15332 * Fires when a list item is selected
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15334 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15335 * @param {Number} index The index of the selected item in the dropdown list
15339 * @event beforequery
15340 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15341 * The event object passed has these properties:
15342 * @param {Roo.bootstrap.ComboBox} combo This combo box
15343 * @param {String} query The query
15344 * @param {Boolean} forceAll true to force "all" query
15345 * @param {Boolean} cancel true to cancel the query
15346 * @param {Object} e The query event object
15348 'beforequery': true,
15351 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15357 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15358 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15364 * Fires when the remove value from the combobox array
15365 * @param {Roo.bootstrap.ComboBox} combo This combo box
15369 * @event afterremove
15370 * Fires when the remove value from the combobox array
15371 * @param {Roo.bootstrap.ComboBox} combo This combo box
15373 'afterremove' : true,
15375 * @event specialfilter
15376 * Fires when specialfilter
15377 * @param {Roo.bootstrap.ComboBox} combo This combo box
15379 'specialfilter' : true,
15382 * Fires when tick the element
15383 * @param {Roo.bootstrap.ComboBox} combo This combo box
15387 * @event touchviewdisplay
15388 * Fires when touch view require special display (default is using displayField)
15389 * @param {Roo.bootstrap.ComboBox} combo This combo box
15390 * @param {Object} cfg set html .
15392 'touchviewdisplay' : true
15397 this.tickItems = [];
15399 this.selectedIndex = -1;
15400 if(this.mode == 'local'){
15401 if(config.queryDelay === undefined){
15402 this.queryDelay = 10;
15404 if(config.minChars === undefined){
15410 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15413 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15414 * rendering into an Roo.Editor, defaults to false)
15417 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15418 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15421 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15424 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15425 * the dropdown list (defaults to undefined, with no header element)
15429 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15433 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15435 listWidth: undefined,
15437 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15438 * mode = 'remote' or 'text' if mode = 'local')
15440 displayField: undefined,
15443 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15444 * mode = 'remote' or 'value' if mode = 'local').
15445 * Note: use of a valueField requires the user make a selection
15446 * in order for a value to be mapped.
15448 valueField: undefined,
15450 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15455 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15456 * field's data value (defaults to the underlying DOM element's name)
15458 hiddenName: undefined,
15460 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15464 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15466 selectedClass: 'active',
15469 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15473 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15474 * anchor positions (defaults to 'tl-bl')
15476 listAlign: 'tl-bl?',
15478 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15482 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15483 * query specified by the allQuery config option (defaults to 'query')
15485 triggerAction: 'query',
15487 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15488 * (defaults to 4, does not apply if editable = false)
15492 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15493 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15497 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15498 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15502 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15503 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15507 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15508 * when editable = true (defaults to false)
15510 selectOnFocus:false,
15512 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15514 queryParam: 'query',
15516 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15517 * when mode = 'remote' (defaults to 'Loading...')
15519 loadingText: 'Loading...',
15521 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15525 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15529 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15530 * traditional select (defaults to true)
15534 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15538 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15542 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15543 * listWidth has a higher value)
15547 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15548 * allow the user to set arbitrary text into the field (defaults to false)
15550 forceSelection:false,
15552 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15553 * if typeAhead = true (defaults to 250)
15555 typeAheadDelay : 250,
15557 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15558 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15560 valueNotFoundText : undefined,
15562 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15564 blockFocus : false,
15567 * @cfg {Boolean} disableClear Disable showing of clear button.
15569 disableClear : false,
15571 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15573 alwaysQuery : false,
15576 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15581 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15583 invalidClass : "has-warning",
15586 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15588 validClass : "has-success",
15591 * @cfg {Boolean} specialFilter (true|false) special filter default false
15593 specialFilter : false,
15596 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15598 mobileTouchView : true,
15601 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15603 useNativeIOS : false,
15606 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15608 mobile_restrict_height : false,
15610 ios_options : false,
15622 btnPosition : 'right',
15623 triggerList : true,
15624 showToggleBtn : true,
15626 emptyResultText: 'Empty',
15627 triggerText : 'Select',
15631 // element that contains real text value.. (when hidden is used..)
15633 getAutoCreate : function()
15638 * Render classic select for iso
15641 if(Roo.isIOS && this.useNativeIOS){
15642 cfg = this.getAutoCreateNativeIOS();
15650 if(Roo.isTouch && this.mobileTouchView){
15651 cfg = this.getAutoCreateTouchView();
15658 if(!this.tickable){
15659 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15664 * ComboBox with tickable selections
15667 var align = this.labelAlign || this.parentLabelAlign();
15670 cls : 'form-group roo-combobox-tickable' //input-group
15673 var btn_text_select = '';
15674 var btn_text_done = '';
15675 var btn_text_cancel = '';
15677 if (this.btn_text_show) {
15678 btn_text_select = 'Select';
15679 btn_text_done = 'Done';
15680 btn_text_cancel = 'Cancel';
15685 cls : 'tickable-buttons',
15690 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15691 //html : this.triggerText
15692 html: btn_text_select
15698 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15700 html: btn_text_done
15706 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15708 html: btn_text_cancel
15714 buttons.cn.unshift({
15716 cls: 'roo-select2-search-field-input'
15722 Roo.each(buttons.cn, function(c){
15724 c.cls += ' btn-' + _this.size;
15727 if (_this.disabled) {
15734 style : 'display: contents',
15739 cls: 'form-hidden-field'
15743 cls: 'roo-select2-choices',
15747 cls: 'roo-select2-search-field',
15758 cls: 'roo-select2-container input-group roo-select2-container-multi',
15764 // cls: 'typeahead typeahead-long dropdown-menu',
15765 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15770 if(this.hasFeedback && !this.allowBlank){
15774 cls: 'glyphicon form-control-feedback'
15777 combobox.cn.push(feedback);
15784 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15785 tooltip : 'This field is required'
15787 if (Roo.bootstrap.version == 4) {
15790 style : 'display:none'
15793 if (align ==='left' && this.fieldLabel.length) {
15795 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15802 cls : 'control-label col-form-label',
15803 html : this.fieldLabel
15815 var labelCfg = cfg.cn[1];
15816 var contentCfg = cfg.cn[2];
15819 if(this.indicatorpos == 'right'){
15825 cls : 'control-label col-form-label',
15829 html : this.fieldLabel
15845 labelCfg = cfg.cn[0];
15846 contentCfg = cfg.cn[1];
15850 if(this.labelWidth > 12){
15851 labelCfg.style = "width: " + this.labelWidth + 'px';
15853 if(this.width * 1 > 0){
15854 contentCfg.style = "width: " + this.width + 'px';
15856 if(this.labelWidth < 13 && this.labelmd == 0){
15857 this.labelmd = this.labelWidth;
15860 if(this.labellg > 0){
15861 labelCfg.cls += ' col-lg-' + this.labellg;
15862 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15865 if(this.labelmd > 0){
15866 labelCfg.cls += ' col-md-' + this.labelmd;
15867 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15870 if(this.labelsm > 0){
15871 labelCfg.cls += ' col-sm-' + this.labelsm;
15872 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15875 if(this.labelxs > 0){
15876 labelCfg.cls += ' col-xs-' + this.labelxs;
15877 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15881 } else if ( this.fieldLabel.length) {
15882 // Roo.log(" label");
15887 //cls : 'input-group-addon',
15888 html : this.fieldLabel
15893 if(this.indicatorpos == 'right'){
15897 //cls : 'input-group-addon',
15898 html : this.fieldLabel
15908 // Roo.log(" no label && no align");
15915 ['xs','sm','md','lg'].map(function(size){
15916 if (settings[size]) {
15917 cfg.cls += ' col-' + size + '-' + settings[size];
15925 _initEventsCalled : false,
15928 initEvents: function()
15930 if (this._initEventsCalled) { // as we call render... prevent looping...
15933 this._initEventsCalled = true;
15936 throw "can not find store for combo";
15939 this.indicator = this.indicatorEl();
15941 this.store = Roo.factory(this.store, Roo.data);
15942 this.store.parent = this;
15944 // if we are building from html. then this element is so complex, that we can not really
15945 // use the rendered HTML.
15946 // so we have to trash and replace the previous code.
15947 if (Roo.XComponent.build_from_html) {
15948 // remove this element....
15949 var e = this.el.dom, k=0;
15950 while (e ) { e = e.previousSibling; ++k;}
15955 this.rendered = false;
15957 this.render(this.parent().getChildContainer(true), k);
15960 if(Roo.isIOS && this.useNativeIOS){
15961 this.initIOSView();
15969 if(Roo.isTouch && this.mobileTouchView){
15970 this.initTouchView();
15975 this.initTickableEvents();
15979 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15981 if(this.hiddenName){
15983 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15985 this.hiddenField.dom.value =
15986 this.hiddenValue !== undefined ? this.hiddenValue :
15987 this.value !== undefined ? this.value : '';
15989 // prevent input submission
15990 this.el.dom.removeAttribute('name');
15991 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15996 // this.el.dom.setAttribute('autocomplete', 'off');
15999 var cls = 'x-combo-list';
16001 //this.list = new Roo.Layer({
16002 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16008 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16009 _this.list.setWidth(lw);
16012 this.list.on('mouseover', this.onViewOver, this);
16013 this.list.on('mousemove', this.onViewMove, this);
16014 this.list.on('scroll', this.onViewScroll, this);
16017 this.list.swallowEvent('mousewheel');
16018 this.assetHeight = 0;
16021 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16022 this.assetHeight += this.header.getHeight();
16025 this.innerList = this.list.createChild({cls:cls+'-inner'});
16026 this.innerList.on('mouseover', this.onViewOver, this);
16027 this.innerList.on('mousemove', this.onViewMove, this);
16028 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16030 if(this.allowBlank && !this.pageSize && !this.disableClear){
16031 this.footer = this.list.createChild({cls:cls+'-ft'});
16032 this.pageTb = new Roo.Toolbar(this.footer);
16036 this.footer = this.list.createChild({cls:cls+'-ft'});
16037 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16038 {pageSize: this.pageSize});
16042 if (this.pageTb && this.allowBlank && !this.disableClear) {
16044 this.pageTb.add(new Roo.Toolbar.Fill(), {
16045 cls: 'x-btn-icon x-btn-clear',
16047 handler: function()
16050 _this.clearValue();
16051 _this.onSelect(false, -1);
16056 this.assetHeight += this.footer.getHeight();
16061 this.tpl = Roo.bootstrap.version == 4 ?
16062 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16063 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16066 this.view = new Roo.View(this.list, this.tpl, {
16067 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16069 //this.view.wrapEl.setDisplayed(false);
16070 this.view.on('click', this.onViewClick, this);
16073 this.store.on('beforeload', this.onBeforeLoad, this);
16074 this.store.on('load', this.onLoad, this);
16075 this.store.on('loadexception', this.onLoadException, this);
16077 if(this.resizable){
16078 this.resizer = new Roo.Resizable(this.list, {
16079 pinned:true, handles:'se'
16081 this.resizer.on('resize', function(r, w, h){
16082 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16083 this.listWidth = w;
16084 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16085 this.restrictHeight();
16087 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16090 if(!this.editable){
16091 this.editable = true;
16092 this.setEditable(false);
16097 if (typeof(this.events.add.listeners) != 'undefined') {
16099 this.addicon = this.wrap.createChild(
16100 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16102 this.addicon.on('click', function(e) {
16103 this.fireEvent('add', this);
16106 if (typeof(this.events.edit.listeners) != 'undefined') {
16108 this.editicon = this.wrap.createChild(
16109 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16110 if (this.addicon) {
16111 this.editicon.setStyle('margin-left', '40px');
16113 this.editicon.on('click', function(e) {
16115 // we fire even if inothing is selected..
16116 this.fireEvent('edit', this, this.lastData );
16122 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16123 "up" : function(e){
16124 this.inKeyMode = true;
16128 "down" : function(e){
16129 if(!this.isExpanded()){
16130 this.onTriggerClick();
16132 this.inKeyMode = true;
16137 "enter" : function(e){
16138 // this.onViewClick();
16142 if(this.fireEvent("specialkey", this, e)){
16143 this.onViewClick(false);
16149 "esc" : function(e){
16153 "tab" : function(e){
16156 if(this.fireEvent("specialkey", this, e)){
16157 this.onViewClick(false);
16165 doRelay : function(foo, bar, hname){
16166 if(hname == 'down' || this.scope.isExpanded()){
16167 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16176 this.queryDelay = Math.max(this.queryDelay || 10,
16177 this.mode == 'local' ? 10 : 250);
16180 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16182 if(this.typeAhead){
16183 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16185 if(this.editable !== false){
16186 this.inputEl().on("keyup", this.onKeyUp, this);
16188 if(this.forceSelection){
16189 this.inputEl().on('blur', this.doForce, this);
16193 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16194 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16198 initTickableEvents: function()
16202 if(this.hiddenName){
16204 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16206 this.hiddenField.dom.value =
16207 this.hiddenValue !== undefined ? this.hiddenValue :
16208 this.value !== undefined ? this.value : '';
16210 // prevent input submission
16211 this.el.dom.removeAttribute('name');
16212 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16217 // this.list = this.el.select('ul.dropdown-menu',true).first();
16219 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16220 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16221 if(this.triggerList){
16222 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16225 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16226 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16228 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16229 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16231 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16232 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16234 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16235 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16236 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16239 this.cancelBtn.hide();
16244 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16245 _this.list.setWidth(lw);
16248 this.list.on('mouseover', this.onViewOver, this);
16249 this.list.on('mousemove', this.onViewMove, this);
16251 this.list.on('scroll', this.onViewScroll, this);
16254 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16255 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16258 this.view = new Roo.View(this.list, this.tpl, {
16263 selectedClass: this.selectedClass
16266 //this.view.wrapEl.setDisplayed(false);
16267 this.view.on('click', this.onViewClick, this);
16271 this.store.on('beforeload', this.onBeforeLoad, this);
16272 this.store.on('load', this.onLoad, this);
16273 this.store.on('loadexception', this.onLoadException, this);
16276 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16277 "up" : function(e){
16278 this.inKeyMode = true;
16282 "down" : function(e){
16283 this.inKeyMode = true;
16287 "enter" : function(e){
16288 if(this.fireEvent("specialkey", this, e)){
16289 this.onViewClick(false);
16295 "esc" : function(e){
16296 this.onTickableFooterButtonClick(e, false, false);
16299 "tab" : function(e){
16300 this.fireEvent("specialkey", this, e);
16302 this.onTickableFooterButtonClick(e, false, false);
16309 doRelay : function(e, fn, key){
16310 if(this.scope.isExpanded()){
16311 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16320 this.queryDelay = Math.max(this.queryDelay || 10,
16321 this.mode == 'local' ? 10 : 250);
16324 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16326 if(this.typeAhead){
16327 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16330 if(this.editable !== false){
16331 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16334 this.indicator = this.indicatorEl();
16336 if(this.indicator){
16337 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16338 this.indicator.hide();
16343 onDestroy : function(){
16345 this.view.setStore(null);
16346 this.view.el.removeAllListeners();
16347 this.view.el.remove();
16348 this.view.purgeListeners();
16351 this.list.dom.innerHTML = '';
16355 this.store.un('beforeload', this.onBeforeLoad, this);
16356 this.store.un('load', this.onLoad, this);
16357 this.store.un('loadexception', this.onLoadException, this);
16359 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16363 fireKey : function(e){
16364 if(e.isNavKeyPress() && !this.list.isVisible()){
16365 this.fireEvent("specialkey", this, e);
16370 onResize: function(w, h)
16374 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16376 // if(typeof w != 'number'){
16377 // // we do not handle it!?!?
16380 // var tw = this.trigger.getWidth();
16381 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16382 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16384 // this.inputEl().setWidth( this.adjustWidth('input', x));
16386 // //this.trigger.setStyle('left', x+'px');
16388 // if(this.list && this.listWidth === undefined){
16389 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16390 // this.list.setWidth(lw);
16391 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16399 * Allow or prevent the user from directly editing the field text. If false is passed,
16400 * the user will only be able to select from the items defined in the dropdown list. This method
16401 * is the runtime equivalent of setting the 'editable' config option at config time.
16402 * @param {Boolean} value True to allow the user to directly edit the field text
16404 setEditable : function(value){
16405 if(value == this.editable){
16408 this.editable = value;
16410 this.inputEl().dom.setAttribute('readOnly', true);
16411 this.inputEl().on('mousedown', this.onTriggerClick, this);
16412 this.inputEl().addClass('x-combo-noedit');
16414 this.inputEl().dom.removeAttribute('readOnly');
16415 this.inputEl().un('mousedown', this.onTriggerClick, this);
16416 this.inputEl().removeClass('x-combo-noedit');
16422 onBeforeLoad : function(combo,opts){
16423 if(!this.hasFocus){
16427 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16429 this.restrictHeight();
16430 this.selectedIndex = -1;
16434 onLoad : function(){
16436 this.hasQuery = false;
16438 if(!this.hasFocus){
16442 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16443 this.loading.hide();
16446 if(this.store.getCount() > 0){
16449 this.restrictHeight();
16450 if(this.lastQuery == this.allQuery){
16451 if(this.editable && !this.tickable){
16452 this.inputEl().dom.select();
16456 !this.selectByValue(this.value, true) &&
16459 !this.store.lastOptions ||
16460 typeof(this.store.lastOptions.add) == 'undefined' ||
16461 this.store.lastOptions.add != true
16464 this.select(0, true);
16467 if(this.autoFocus){
16470 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16471 this.taTask.delay(this.typeAheadDelay);
16475 this.onEmptyResults();
16481 onLoadException : function()
16483 this.hasQuery = false;
16485 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16486 this.loading.hide();
16489 if(this.tickable && this.editable){
16494 // only causes errors at present
16495 //Roo.log(this.store.reader.jsonData);
16496 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16498 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16504 onTypeAhead : function(){
16505 if(this.store.getCount() > 0){
16506 var r = this.store.getAt(0);
16507 var newValue = r.data[this.displayField];
16508 var len = newValue.length;
16509 var selStart = this.getRawValue().length;
16511 if(selStart != len){
16512 this.setRawValue(newValue);
16513 this.selectText(selStart, newValue.length);
16519 onSelect : function(record, index){
16521 if(this.fireEvent('beforeselect', this, record, index) !== false){
16523 this.setFromData(index > -1 ? record.data : false);
16526 this.fireEvent('select', this, record, index);
16531 * Returns the currently selected field value or empty string if no value is set.
16532 * @return {String} value The selected value
16534 getValue : function()
16536 if(Roo.isIOS && this.useNativeIOS){
16537 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16541 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16544 if(this.valueField){
16545 return typeof this.value != 'undefined' ? this.value : '';
16547 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16551 getRawValue : function()
16553 if(Roo.isIOS && this.useNativeIOS){
16554 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16557 var v = this.inputEl().getValue();
16563 * Clears any text/value currently set in the field
16565 clearValue : function(){
16567 if(this.hiddenField){
16568 this.hiddenField.dom.value = '';
16571 this.setRawValue('');
16572 this.lastSelectionText = '';
16573 this.lastData = false;
16575 var close = this.closeTriggerEl();
16586 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16587 * will be displayed in the field. If the value does not match the data value of an existing item,
16588 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16589 * Otherwise the field will be blank (although the value will still be set).
16590 * @param {String} value The value to match
16592 setValue : function(v)
16594 if(Roo.isIOS && this.useNativeIOS){
16595 this.setIOSValue(v);
16605 if(this.valueField){
16606 var r = this.findRecord(this.valueField, v);
16608 text = r.data[this.displayField];
16609 }else if(this.valueNotFoundText !== undefined){
16610 text = this.valueNotFoundText;
16613 this.lastSelectionText = text;
16614 if(this.hiddenField){
16615 this.hiddenField.dom.value = v;
16617 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16620 var close = this.closeTriggerEl();
16623 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16629 * @property {Object} the last set data for the element
16634 * Sets the value of the field based on a object which is related to the record format for the store.
16635 * @param {Object} value the value to set as. or false on reset?
16637 setFromData : function(o){
16644 var dv = ''; // display value
16645 var vv = ''; // value value..
16647 if (this.displayField) {
16648 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16650 // this is an error condition!!!
16651 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16654 if(this.valueField){
16655 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16658 var close = this.closeTriggerEl();
16661 if(dv.length || vv * 1 > 0){
16663 this.blockFocus=true;
16669 if(this.hiddenField){
16670 this.hiddenField.dom.value = vv;
16672 this.lastSelectionText = dv;
16673 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16677 // no hidden field.. - we store the value in 'value', but still display
16678 // display field!!!!
16679 this.lastSelectionText = dv;
16680 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16687 reset : function(){
16688 // overridden so that last data is reset..
16695 this.setValue(this.originalValue);
16696 //this.clearInvalid();
16697 this.lastData = false;
16699 this.view.clearSelections();
16705 findRecord : function(prop, value){
16707 if(this.store.getCount() > 0){
16708 this.store.each(function(r){
16709 if(r.data[prop] == value){
16719 getName: function()
16721 // returns hidden if it's set..
16722 if (!this.rendered) {return ''};
16723 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16727 onViewMove : function(e, t){
16728 this.inKeyMode = false;
16732 onViewOver : function(e, t){
16733 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16736 var item = this.view.findItemFromChild(t);
16739 var index = this.view.indexOf(item);
16740 this.select(index, false);
16745 onViewClick : function(view, doFocus, el, e)
16747 var index = this.view.getSelectedIndexes()[0];
16749 var r = this.store.getAt(index);
16753 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16760 Roo.each(this.tickItems, function(v,k){
16762 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16764 _this.tickItems.splice(k, 1);
16766 if(typeof(e) == 'undefined' && view == false){
16767 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16779 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16780 this.tickItems.push(r.data);
16783 if(typeof(e) == 'undefined' && view == false){
16784 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16791 this.onSelect(r, index);
16793 if(doFocus !== false && !this.blockFocus){
16794 this.inputEl().focus();
16799 restrictHeight : function(){
16800 //this.innerList.dom.style.height = '';
16801 //var inner = this.innerList.dom;
16802 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16803 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16804 //this.list.beginUpdate();
16805 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16806 this.list.alignTo(this.inputEl(), this.listAlign);
16807 this.list.alignTo(this.inputEl(), this.listAlign);
16808 //this.list.endUpdate();
16812 onEmptyResults : function(){
16814 if(this.tickable && this.editable){
16815 this.hasFocus = false;
16816 this.restrictHeight();
16824 * Returns true if the dropdown list is expanded, else false.
16826 isExpanded : function(){
16827 return this.list.isVisible();
16831 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16832 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16833 * @param {String} value The data value of the item to select
16834 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16835 * selected item if it is not currently in view (defaults to true)
16836 * @return {Boolean} True if the value matched an item in the list, else false
16838 selectByValue : function(v, scrollIntoView){
16839 if(v !== undefined && v !== null){
16840 var r = this.findRecord(this.valueField || this.displayField, v);
16842 this.select(this.store.indexOf(r), scrollIntoView);
16850 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16851 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16852 * @param {Number} index The zero-based index of the list item to select
16853 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16854 * selected item if it is not currently in view (defaults to true)
16856 select : function(index, scrollIntoView){
16857 this.selectedIndex = index;
16858 this.view.select(index);
16859 if(scrollIntoView !== false){
16860 var el = this.view.getNode(index);
16862 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16865 this.list.scrollChildIntoView(el, false);
16871 selectNext : function(){
16872 var ct = this.store.getCount();
16874 if(this.selectedIndex == -1){
16876 }else if(this.selectedIndex < ct-1){
16877 this.select(this.selectedIndex+1);
16883 selectPrev : function(){
16884 var ct = this.store.getCount();
16886 if(this.selectedIndex == -1){
16888 }else if(this.selectedIndex != 0){
16889 this.select(this.selectedIndex-1);
16895 onKeyUp : function(e){
16896 if(this.editable !== false && !e.isSpecialKey()){
16897 this.lastKey = e.getKey();
16898 this.dqTask.delay(this.queryDelay);
16903 validateBlur : function(){
16904 return !this.list || !this.list.isVisible();
16908 initQuery : function(){
16910 var v = this.getRawValue();
16912 if(this.tickable && this.editable){
16913 v = this.tickableInputEl().getValue();
16920 doForce : function(){
16921 if(this.inputEl().dom.value.length > 0){
16922 this.inputEl().dom.value =
16923 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16929 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16930 * query allowing the query action to be canceled if needed.
16931 * @param {String} query The SQL query to execute
16932 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16933 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16934 * saved in the current store (defaults to false)
16936 doQuery : function(q, forceAll){
16938 if(q === undefined || q === null){
16943 forceAll: forceAll,
16947 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16952 forceAll = qe.forceAll;
16953 if(forceAll === true || (q.length >= this.minChars)){
16955 this.hasQuery = true;
16957 if(this.lastQuery != q || this.alwaysQuery){
16958 this.lastQuery = q;
16959 if(this.mode == 'local'){
16960 this.selectedIndex = -1;
16962 this.store.clearFilter();
16965 if(this.specialFilter){
16966 this.fireEvent('specialfilter', this);
16971 this.store.filter(this.displayField, q);
16974 this.store.fireEvent("datachanged", this.store);
16981 this.store.baseParams[this.queryParam] = q;
16983 var options = {params : this.getParams(q)};
16986 options.add = true;
16987 options.params.start = this.page * this.pageSize;
16990 this.store.load(options);
16993 * this code will make the page width larger, at the beginning, the list not align correctly,
16994 * we should expand the list on onLoad
16995 * so command out it
17000 this.selectedIndex = -1;
17005 this.loadNext = false;
17009 getParams : function(q){
17011 //p[this.queryParam] = q;
17015 p.limit = this.pageSize;
17021 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17023 collapse : function(){
17024 if(!this.isExpanded()){
17030 this.hasFocus = false;
17034 this.cancelBtn.hide();
17035 this.trigger.show();
17038 this.tickableInputEl().dom.value = '';
17039 this.tickableInputEl().blur();
17044 Roo.get(document).un('mousedown', this.collapseIf, this);
17045 Roo.get(document).un('mousewheel', this.collapseIf, this);
17046 if (!this.editable) {
17047 Roo.get(document).un('keydown', this.listKeyPress, this);
17049 this.fireEvent('collapse', this);
17055 collapseIf : function(e){
17056 var in_combo = e.within(this.el);
17057 var in_list = e.within(this.list);
17058 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17060 if (in_combo || in_list || is_list) {
17061 //e.stopPropagation();
17066 this.onTickableFooterButtonClick(e, false, false);
17074 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17076 expand : function(){
17078 if(this.isExpanded() || !this.hasFocus){
17082 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17083 this.list.setWidth(lw);
17089 this.restrictHeight();
17093 this.tickItems = Roo.apply([], this.item);
17096 this.cancelBtn.show();
17097 this.trigger.hide();
17100 this.tickableInputEl().focus();
17105 Roo.get(document).on('mousedown', this.collapseIf, this);
17106 Roo.get(document).on('mousewheel', this.collapseIf, this);
17107 if (!this.editable) {
17108 Roo.get(document).on('keydown', this.listKeyPress, this);
17111 this.fireEvent('expand', this);
17115 // Implements the default empty TriggerField.onTriggerClick function
17116 onTriggerClick : function(e)
17118 Roo.log('trigger click');
17120 if(this.disabled || !this.triggerList){
17125 this.loadNext = false;
17127 if(this.isExpanded()){
17129 if (!this.blockFocus) {
17130 this.inputEl().focus();
17134 this.hasFocus = true;
17135 if(this.triggerAction == 'all') {
17136 this.doQuery(this.allQuery, true);
17138 this.doQuery(this.getRawValue());
17140 if (!this.blockFocus) {
17141 this.inputEl().focus();
17146 onTickableTriggerClick : function(e)
17153 this.loadNext = false;
17154 this.hasFocus = true;
17156 if(this.triggerAction == 'all') {
17157 this.doQuery(this.allQuery, true);
17159 this.doQuery(this.getRawValue());
17163 onSearchFieldClick : function(e)
17165 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17166 this.onTickableFooterButtonClick(e, false, false);
17170 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17175 this.loadNext = false;
17176 this.hasFocus = true;
17178 if(this.triggerAction == 'all') {
17179 this.doQuery(this.allQuery, true);
17181 this.doQuery(this.getRawValue());
17185 listKeyPress : function(e)
17187 //Roo.log('listkeypress');
17188 // scroll to first matching element based on key pres..
17189 if (e.isSpecialKey()) {
17192 var k = String.fromCharCode(e.getKey()).toUpperCase();
17195 var csel = this.view.getSelectedNodes();
17196 var cselitem = false;
17198 var ix = this.view.indexOf(csel[0]);
17199 cselitem = this.store.getAt(ix);
17200 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17206 this.store.each(function(v) {
17208 // start at existing selection.
17209 if (cselitem.id == v.id) {
17215 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17216 match = this.store.indexOf(v);
17222 if (match === false) {
17223 return true; // no more action?
17226 this.view.select(match);
17227 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17228 sn.scrollIntoView(sn.dom.parentNode, false);
17231 onViewScroll : function(e, t){
17233 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
17237 this.hasQuery = true;
17239 this.loading = this.list.select('.loading', true).first();
17241 if(this.loading === null){
17242 this.list.createChild({
17244 cls: 'loading roo-select2-more-results roo-select2-active',
17245 html: 'Loading more results...'
17248 this.loading = this.list.select('.loading', true).first();
17250 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17252 this.loading.hide();
17255 this.loading.show();
17260 this.loadNext = true;
17262 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17267 addItem : function(o)
17269 var dv = ''; // display value
17271 if (this.displayField) {
17272 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17274 // this is an error condition!!!
17275 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17282 var choice = this.choices.createChild({
17284 cls: 'roo-select2-search-choice',
17293 cls: 'roo-select2-search-choice-close fa fa-times',
17298 }, this.searchField);
17300 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17302 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17310 this.inputEl().dom.value = '';
17315 onRemoveItem : function(e, _self, o)
17317 e.preventDefault();
17319 this.lastItem = Roo.apply([], this.item);
17321 var index = this.item.indexOf(o.data) * 1;
17324 Roo.log('not this item?!');
17328 this.item.splice(index, 1);
17333 this.fireEvent('remove', this, e);
17339 syncValue : function()
17341 if(!this.item.length){
17348 Roo.each(this.item, function(i){
17349 if(_this.valueField){
17350 value.push(i[_this.valueField]);
17357 this.value = value.join(',');
17359 if(this.hiddenField){
17360 this.hiddenField.dom.value = this.value;
17363 this.store.fireEvent("datachanged", this.store);
17368 clearItem : function()
17370 if(!this.multiple){
17376 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17384 if(this.tickable && !Roo.isTouch){
17385 this.view.refresh();
17389 inputEl: function ()
17391 if(Roo.isIOS && this.useNativeIOS){
17392 return this.el.select('select.roo-ios-select', true).first();
17395 if(Roo.isTouch && this.mobileTouchView){
17396 return this.el.select('input.form-control',true).first();
17400 return this.searchField;
17403 return this.el.select('input.form-control',true).first();
17406 onTickableFooterButtonClick : function(e, btn, el)
17408 e.preventDefault();
17410 this.lastItem = Roo.apply([], this.item);
17412 if(btn && btn.name == 'cancel'){
17413 this.tickItems = Roo.apply([], this.item);
17422 Roo.each(this.tickItems, function(o){
17430 validate : function()
17432 if(this.getVisibilityEl().hasClass('hidden')){
17436 var v = this.getRawValue();
17439 v = this.getValue();
17442 if(this.disabled || this.allowBlank || v.length){
17447 this.markInvalid();
17451 tickableInputEl : function()
17453 if(!this.tickable || !this.editable){
17454 return this.inputEl();
17457 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17461 getAutoCreateTouchView : function()
17466 cls: 'form-group' //input-group
17472 type : this.inputType,
17473 cls : 'form-control x-combo-noedit',
17474 autocomplete: 'new-password',
17475 placeholder : this.placeholder || '',
17480 input.name = this.name;
17484 input.cls += ' input-' + this.size;
17487 if (this.disabled) {
17488 input.disabled = true;
17492 cls : 'roo-combobox-wrap',
17499 inputblock.cls += ' input-group';
17501 inputblock.cn.unshift({
17503 cls : 'input-group-addon input-group-prepend input-group-text',
17508 if(this.removable && !this.multiple){
17509 inputblock.cls += ' roo-removable';
17511 inputblock.cn.push({
17514 cls : 'roo-combo-removable-btn close'
17518 if(this.hasFeedback && !this.allowBlank){
17520 inputblock.cls += ' has-feedback';
17522 inputblock.cn.push({
17524 cls: 'glyphicon form-control-feedback'
17531 inputblock.cls += (this.before) ? '' : ' input-group';
17533 inputblock.cn.push({
17535 cls : 'input-group-addon input-group-append input-group-text',
17541 var ibwrap = inputblock;
17546 cls: 'roo-select2-choices',
17550 cls: 'roo-select2-search-field',
17563 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17568 cls: 'form-hidden-field'
17574 if(!this.multiple && this.showToggleBtn){
17580 if (this.caret != false) {
17583 cls: 'fa fa-' + this.caret
17590 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17592 Roo.bootstrap.version == 3 ? caret : '',
17595 cls: 'combobox-clear',
17609 combobox.cls += ' roo-select2-container-multi';
17612 var required = this.allowBlank ? {
17614 style: 'display: none'
17617 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17618 tooltip : 'This field is required'
17621 var align = this.labelAlign || this.parentLabelAlign();
17623 if (align ==='left' && this.fieldLabel.length) {
17629 cls : 'control-label col-form-label',
17630 html : this.fieldLabel
17634 cls : 'roo-combobox-wrap ',
17641 var labelCfg = cfg.cn[1];
17642 var contentCfg = cfg.cn[2];
17645 if(this.indicatorpos == 'right'){
17650 cls : 'control-label col-form-label',
17654 html : this.fieldLabel
17660 cls : "roo-combobox-wrap ",
17668 labelCfg = cfg.cn[0];
17669 contentCfg = cfg.cn[1];
17674 if(this.labelWidth > 12){
17675 labelCfg.style = "width: " + this.labelWidth + 'px';
17678 if(this.labelWidth < 13 && this.labelmd == 0){
17679 this.labelmd = this.labelWidth;
17682 if(this.labellg > 0){
17683 labelCfg.cls += ' col-lg-' + this.labellg;
17684 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17687 if(this.labelmd > 0){
17688 labelCfg.cls += ' col-md-' + this.labelmd;
17689 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17692 if(this.labelsm > 0){
17693 labelCfg.cls += ' col-sm-' + this.labelsm;
17694 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17697 if(this.labelxs > 0){
17698 labelCfg.cls += ' col-xs-' + this.labelxs;
17699 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17703 } else if ( this.fieldLabel.length) {
17708 cls : 'control-label',
17709 html : this.fieldLabel
17720 if(this.indicatorpos == 'right'){
17724 cls : 'control-label',
17725 html : this.fieldLabel,
17743 var settings = this;
17745 ['xs','sm','md','lg'].map(function(size){
17746 if (settings[size]) {
17747 cfg.cls += ' col-' + size + '-' + settings[size];
17754 initTouchView : function()
17756 this.renderTouchView();
17758 this.touchViewEl.on('scroll', function(){
17759 this.el.dom.scrollTop = 0;
17762 this.originalValue = this.getValue();
17764 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17766 this.inputEl().on("click", this.showTouchView, this);
17767 if (this.triggerEl) {
17768 this.triggerEl.on("click", this.showTouchView, this);
17772 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17773 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17775 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17777 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17778 this.store.on('load', this.onTouchViewLoad, this);
17779 this.store.on('loadexception', this.onTouchViewLoadException, this);
17781 if(this.hiddenName){
17783 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17785 this.hiddenField.dom.value =
17786 this.hiddenValue !== undefined ? this.hiddenValue :
17787 this.value !== undefined ? this.value : '';
17789 this.el.dom.removeAttribute('name');
17790 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17794 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17795 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17798 if(this.removable && !this.multiple){
17799 var close = this.closeTriggerEl();
17801 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17802 close.on('click', this.removeBtnClick, this, close);
17806 * fix the bug in Safari iOS8
17808 this.inputEl().on("focus", function(e){
17809 document.activeElement.blur();
17812 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17819 renderTouchView : function()
17821 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17822 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17824 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17825 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17827 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17828 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829 this.touchViewBodyEl.setStyle('overflow', 'auto');
17831 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17832 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17834 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17835 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17839 showTouchView : function()
17845 this.touchViewHeaderEl.hide();
17847 if(this.modalTitle.length){
17848 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17849 this.touchViewHeaderEl.show();
17852 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17853 this.touchViewEl.show();
17855 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17857 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17858 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17860 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17862 if(this.modalTitle.length){
17863 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17866 this.touchViewBodyEl.setHeight(bodyHeight);
17870 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17872 this.touchViewEl.addClass(['in','show']);
17875 if(this._touchViewMask){
17876 Roo.get(document.body).addClass("x-body-masked");
17877 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17878 this._touchViewMask.setStyle('z-index', 10000);
17879 this._touchViewMask.addClass('show');
17882 this.doTouchViewQuery();
17886 hideTouchView : function()
17888 this.touchViewEl.removeClass(['in','show']);
17892 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17894 this.touchViewEl.setStyle('display', 'none');
17897 if(this._touchViewMask){
17898 this._touchViewMask.removeClass('show');
17899 Roo.get(document.body).removeClass("x-body-masked");
17903 setTouchViewValue : function()
17910 Roo.each(this.tickItems, function(o){
17915 this.hideTouchView();
17918 doTouchViewQuery : function()
17927 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17931 if(!this.alwaysQuery || this.mode == 'local'){
17932 this.onTouchViewLoad();
17939 onTouchViewBeforeLoad : function(combo,opts)
17945 onTouchViewLoad : function()
17947 if(this.store.getCount() < 1){
17948 this.onTouchViewEmptyResults();
17952 this.clearTouchView();
17954 var rawValue = this.getRawValue();
17956 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17958 this.tickItems = [];
17960 this.store.data.each(function(d, rowIndex){
17961 var row = this.touchViewListGroup.createChild(template);
17963 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17964 row.addClass(d.data.cls);
17967 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17970 html : d.data[this.displayField]
17973 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17974 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17977 row.removeClass('selected');
17978 if(!this.multiple && this.valueField &&
17979 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17982 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983 row.addClass('selected');
17986 if(this.multiple && this.valueField &&
17987 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17991 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17992 this.tickItems.push(d.data);
17995 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17999 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18001 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18003 if(this.modalTitle.length){
18004 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18007 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18009 if(this.mobile_restrict_height && listHeight < bodyHeight){
18010 this.touchViewBodyEl.setHeight(listHeight);
18015 if(firstChecked && listHeight > bodyHeight){
18016 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18021 onTouchViewLoadException : function()
18023 this.hideTouchView();
18026 onTouchViewEmptyResults : function()
18028 this.clearTouchView();
18030 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18032 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18036 clearTouchView : function()
18038 this.touchViewListGroup.dom.innerHTML = '';
18041 onTouchViewClick : function(e, el, o)
18043 e.preventDefault();
18046 var rowIndex = o.rowIndex;
18048 var r = this.store.getAt(rowIndex);
18050 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18052 if(!this.multiple){
18053 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18054 c.dom.removeAttribute('checked');
18057 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18059 this.setFromData(r.data);
18061 var close = this.closeTriggerEl();
18067 this.hideTouchView();
18069 this.fireEvent('select', this, r, rowIndex);
18074 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18075 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18076 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18080 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18081 this.addItem(r.data);
18082 this.tickItems.push(r.data);
18086 getAutoCreateNativeIOS : function()
18089 cls: 'form-group' //input-group,
18094 cls : 'roo-ios-select'
18098 combobox.name = this.name;
18101 if (this.disabled) {
18102 combobox.disabled = true;
18105 var settings = this;
18107 ['xs','sm','md','lg'].map(function(size){
18108 if (settings[size]) {
18109 cfg.cls += ' col-' + size + '-' + settings[size];
18119 initIOSView : function()
18121 this.store.on('load', this.onIOSViewLoad, this);
18126 onIOSViewLoad : function()
18128 if(this.store.getCount() < 1){
18132 this.clearIOSView();
18134 if(this.allowBlank) {
18136 var default_text = '-- SELECT --';
18138 if(this.placeholder.length){
18139 default_text = this.placeholder;
18142 if(this.emptyTitle.length){
18143 default_text += ' - ' + this.emptyTitle + ' -';
18146 var opt = this.inputEl().createChild({
18149 html : default_text
18153 o[this.valueField] = 0;
18154 o[this.displayField] = default_text;
18156 this.ios_options.push({
18163 this.store.data.each(function(d, rowIndex){
18167 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18168 html = d.data[this.displayField];
18173 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18174 value = d.data[this.valueField];
18183 if(this.value == d.data[this.valueField]){
18184 option['selected'] = true;
18187 var opt = this.inputEl().createChild(option);
18189 this.ios_options.push({
18196 this.inputEl().on('change', function(){
18197 this.fireEvent('select', this);
18202 clearIOSView: function()
18204 this.inputEl().dom.innerHTML = '';
18206 this.ios_options = [];
18209 setIOSValue: function(v)
18213 if(!this.ios_options){
18217 Roo.each(this.ios_options, function(opts){
18219 opts.el.dom.removeAttribute('selected');
18221 if(opts.data[this.valueField] != v){
18225 opts.el.dom.setAttribute('selected', true);
18231 * @cfg {Boolean} grow
18235 * @cfg {Number} growMin
18239 * @cfg {Number} growMax
18248 Roo.apply(Roo.bootstrap.ComboBox, {
18252 cls: 'modal-header',
18274 cls: 'list-group-item',
18278 cls: 'roo-combobox-list-group-item-value'
18282 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18296 listItemCheckbox : {
18298 cls: 'list-group-item',
18302 cls: 'roo-combobox-list-group-item-value'
18306 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18322 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18327 cls: 'modal-footer',
18335 cls: 'col-xs-6 text-left',
18338 cls: 'btn btn-danger roo-touch-view-cancel',
18344 cls: 'col-xs-6 text-right',
18347 cls: 'btn btn-success roo-touch-view-ok',
18358 Roo.apply(Roo.bootstrap.ComboBox, {
18360 touchViewTemplate : {
18362 cls: 'modal fade roo-combobox-touch-view',
18366 cls: 'modal-dialog',
18367 style : 'position:fixed', // we have to fix position....
18371 cls: 'modal-content',
18373 Roo.bootstrap.ComboBox.header,
18374 Roo.bootstrap.ComboBox.body,
18375 Roo.bootstrap.ComboBox.footer
18384 * Ext JS Library 1.1.1
18385 * Copyright(c) 2006-2007, Ext JS, LLC.
18387 * Originally Released Under LGPL - original licence link has changed is not relivant.
18390 * <script type="text/javascript">
18395 * @extends Roo.util.Observable
18396 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18397 * This class also supports single and multi selection modes. <br>
18398 * Create a data model bound view:
18400 var store = new Roo.data.Store(...);
18402 var view = new Roo.View({
18404 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18406 singleSelect: true,
18407 selectedClass: "ydataview-selected",
18411 // listen for node click?
18412 view.on("click", function(vw, index, node, e){
18413 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18417 dataModel.load("foobar.xml");
18419 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18421 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18422 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18424 * Note: old style constructor is still suported (container, template, config)
18427 * Create a new View
18428 * @param {Object} config The config object
18431 Roo.View = function(config, depreciated_tpl, depreciated_config){
18433 this.parent = false;
18435 if (typeof(depreciated_tpl) == 'undefined') {
18436 // new way.. - universal constructor.
18437 Roo.apply(this, config);
18438 this.el = Roo.get(this.el);
18441 this.el = Roo.get(config);
18442 this.tpl = depreciated_tpl;
18443 Roo.apply(this, depreciated_config);
18445 this.wrapEl = this.el.wrap().wrap();
18446 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18449 if(typeof(this.tpl) == "string"){
18450 this.tpl = new Roo.Template(this.tpl);
18452 // support xtype ctors..
18453 this.tpl = new Roo.factory(this.tpl, Roo);
18457 this.tpl.compile();
18462 * @event beforeclick
18463 * Fires before a click is processed. Returns false to cancel the default action.
18464 * @param {Roo.View} this
18465 * @param {Number} index The index of the target node
18466 * @param {HTMLElement} node The target node
18467 * @param {Roo.EventObject} e The raw event object
18469 "beforeclick" : true,
18472 * Fires when a template node is clicked.
18473 * @param {Roo.View} this
18474 * @param {Number} index The index of the target node
18475 * @param {HTMLElement} node The target node
18476 * @param {Roo.EventObject} e The raw event object
18481 * Fires when a template node is double clicked.
18482 * @param {Roo.View} this
18483 * @param {Number} index The index of the target node
18484 * @param {HTMLElement} node The target node
18485 * @param {Roo.EventObject} e The raw event object
18489 * @event contextmenu
18490 * Fires when a template node is right clicked.
18491 * @param {Roo.View} this
18492 * @param {Number} index The index of the target node
18493 * @param {HTMLElement} node The target node
18494 * @param {Roo.EventObject} e The raw event object
18496 "contextmenu" : true,
18498 * @event selectionchange
18499 * Fires when the selected nodes change.
18500 * @param {Roo.View} this
18501 * @param {Array} selections Array of the selected nodes
18503 "selectionchange" : true,
18506 * @event beforeselect
18507 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18508 * @param {Roo.View} this
18509 * @param {HTMLElement} node The node to be selected
18510 * @param {Array} selections Array of currently selected nodes
18512 "beforeselect" : true,
18514 * @event preparedata
18515 * Fires on every row to render, to allow you to change the data.
18516 * @param {Roo.View} this
18517 * @param {Object} data to be rendered (change this)
18519 "preparedata" : true
18527 "click": this.onClick,
18528 "dblclick": this.onDblClick,
18529 "contextmenu": this.onContextMenu,
18533 this.selections = [];
18535 this.cmp = new Roo.CompositeElementLite([]);
18537 this.store = Roo.factory(this.store, Roo.data);
18538 this.setStore(this.store, true);
18541 if ( this.footer && this.footer.xtype) {
18543 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18545 this.footer.dataSource = this.store;
18546 this.footer.container = fctr;
18547 this.footer = Roo.factory(this.footer, Roo);
18548 fctr.insertFirst(this.el);
18550 // this is a bit insane - as the paging toolbar seems to detach the el..
18551 // dom.parentNode.parentNode.parentNode
18552 // they get detached?
18556 Roo.View.superclass.constructor.call(this);
18561 Roo.extend(Roo.View, Roo.util.Observable, {
18564 * @cfg {Roo.data.Store} store Data store to load data from.
18569 * @cfg {String|Roo.Element} el The container element.
18574 * @cfg {String|Roo.Template} tpl The template used by this View
18578 * @cfg {String} dataName the named area of the template to use as the data area
18579 * Works with domtemplates roo-name="name"
18583 * @cfg {String} selectedClass The css class to add to selected nodes
18585 selectedClass : "x-view-selected",
18587 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18592 * @cfg {String} text to display on mask (default Loading)
18596 * @cfg {Boolean} multiSelect Allow multiple selection
18598 multiSelect : false,
18600 * @cfg {Boolean} singleSelect Allow single selection
18602 singleSelect: false,
18605 * @cfg {Boolean} toggleSelect - selecting
18607 toggleSelect : false,
18610 * @cfg {Boolean} tickable - selecting
18615 * Returns the element this view is bound to.
18616 * @return {Roo.Element}
18618 getEl : function(){
18619 return this.wrapEl;
18625 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18627 refresh : function(){
18628 //Roo.log('refresh');
18631 // if we are using something like 'domtemplate', then
18632 // the what gets used is:
18633 // t.applySubtemplate(NAME, data, wrapping data..)
18634 // the outer template then get' applied with
18635 // the store 'extra data'
18636 // and the body get's added to the
18637 // roo-name="data" node?
18638 // <span class='roo-tpl-{name}'></span> ?????
18642 this.clearSelections();
18643 this.el.update("");
18645 var records = this.store.getRange();
18646 if(records.length < 1) {
18648 // is this valid?? = should it render a template??
18650 this.el.update(this.emptyText);
18654 if (this.dataName) {
18655 this.el.update(t.apply(this.store.meta)); //????
18656 el = this.el.child('.roo-tpl-' + this.dataName);
18659 for(var i = 0, len = records.length; i < len; i++){
18660 var data = this.prepareData(records[i].data, i, records[i]);
18661 this.fireEvent("preparedata", this, data, i, records[i]);
18663 var d = Roo.apply({}, data);
18666 Roo.apply(d, {'roo-id' : Roo.id()});
18670 Roo.each(this.parent.item, function(item){
18671 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18674 Roo.apply(d, {'roo-data-checked' : 'checked'});
18678 html[html.length] = Roo.util.Format.trim(
18680 t.applySubtemplate(this.dataName, d, this.store.meta) :
18687 el.update(html.join(""));
18688 this.nodes = el.dom.childNodes;
18689 this.updateIndexes(0);
18694 * Function to override to reformat the data that is sent to
18695 * the template for each node.
18696 * DEPRICATED - use the preparedata event handler.
18697 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18698 * a JSON object for an UpdateManager bound view).
18700 prepareData : function(data, index, record)
18702 this.fireEvent("preparedata", this, data, index, record);
18706 onUpdate : function(ds, record){
18707 // Roo.log('on update');
18708 this.clearSelections();
18709 var index = this.store.indexOf(record);
18710 var n = this.nodes[index];
18711 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18712 n.parentNode.removeChild(n);
18713 this.updateIndexes(index, index);
18719 onAdd : function(ds, records, index)
18721 //Roo.log(['on Add', ds, records, index] );
18722 this.clearSelections();
18723 if(this.nodes.length == 0){
18727 var n = this.nodes[index];
18728 for(var i = 0, len = records.length; i < len; i++){
18729 var d = this.prepareData(records[i].data, i, records[i]);
18731 this.tpl.insertBefore(n, d);
18734 this.tpl.append(this.el, d);
18737 this.updateIndexes(index);
18740 onRemove : function(ds, record, index){
18741 // Roo.log('onRemove');
18742 this.clearSelections();
18743 var el = this.dataName ?
18744 this.el.child('.roo-tpl-' + this.dataName) :
18747 el.dom.removeChild(this.nodes[index]);
18748 this.updateIndexes(index);
18752 * Refresh an individual node.
18753 * @param {Number} index
18755 refreshNode : function(index){
18756 this.onUpdate(this.store, this.store.getAt(index));
18759 updateIndexes : function(startIndex, endIndex){
18760 var ns = this.nodes;
18761 startIndex = startIndex || 0;
18762 endIndex = endIndex || ns.length - 1;
18763 for(var i = startIndex; i <= endIndex; i++){
18764 ns[i].nodeIndex = i;
18769 * Changes the data store this view uses and refresh the view.
18770 * @param {Store} store
18772 setStore : function(store, initial){
18773 if(!initial && this.store){
18774 this.store.un("datachanged", this.refresh);
18775 this.store.un("add", this.onAdd);
18776 this.store.un("remove", this.onRemove);
18777 this.store.un("update", this.onUpdate);
18778 this.store.un("clear", this.refresh);
18779 this.store.un("beforeload", this.onBeforeLoad);
18780 this.store.un("load", this.onLoad);
18781 this.store.un("loadexception", this.onLoad);
18785 store.on("datachanged", this.refresh, this);
18786 store.on("add", this.onAdd, this);
18787 store.on("remove", this.onRemove, this);
18788 store.on("update", this.onUpdate, this);
18789 store.on("clear", this.refresh, this);
18790 store.on("beforeload", this.onBeforeLoad, this);
18791 store.on("load", this.onLoad, this);
18792 store.on("loadexception", this.onLoad, this);
18800 * onbeforeLoad - masks the loading area.
18803 onBeforeLoad : function(store,opts)
18805 //Roo.log('onBeforeLoad');
18807 this.el.update("");
18809 this.el.mask(this.mask ? this.mask : "Loading" );
18811 onLoad : function ()
18818 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18819 * @param {HTMLElement} node
18820 * @return {HTMLElement} The template node
18822 findItemFromChild : function(node){
18823 var el = this.dataName ?
18824 this.el.child('.roo-tpl-' + this.dataName,true) :
18827 if(!node || node.parentNode == el){
18830 var p = node.parentNode;
18831 while(p && p != el){
18832 if(p.parentNode == el){
18841 onClick : function(e){
18842 var item = this.findItemFromChild(e.getTarget());
18844 var index = this.indexOf(item);
18845 if(this.onItemClick(item, index, e) !== false){
18846 this.fireEvent("click", this, index, item, e);
18849 this.clearSelections();
18854 onContextMenu : function(e){
18855 var item = this.findItemFromChild(e.getTarget());
18857 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18862 onDblClick : function(e){
18863 var item = this.findItemFromChild(e.getTarget());
18865 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18869 onItemClick : function(item, index, e)
18871 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18874 if (this.toggleSelect) {
18875 var m = this.isSelected(item) ? 'unselect' : 'select';
18878 _t[m](item, true, false);
18881 if(this.multiSelect || this.singleSelect){
18882 if(this.multiSelect && e.shiftKey && this.lastSelection){
18883 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18885 this.select(item, this.multiSelect && e.ctrlKey);
18886 this.lastSelection = item;
18889 if(!this.tickable){
18890 e.preventDefault();
18898 * Get the number of selected nodes.
18901 getSelectionCount : function(){
18902 return this.selections.length;
18906 * Get the currently selected nodes.
18907 * @return {Array} An array of HTMLElements
18909 getSelectedNodes : function(){
18910 return this.selections;
18914 * Get the indexes of the selected nodes.
18917 getSelectedIndexes : function(){
18918 var indexes = [], s = this.selections;
18919 for(var i = 0, len = s.length; i < len; i++){
18920 indexes.push(s[i].nodeIndex);
18926 * Clear all selections
18927 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18929 clearSelections : function(suppressEvent){
18930 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18931 this.cmp.elements = this.selections;
18932 this.cmp.removeClass(this.selectedClass);
18933 this.selections = [];
18934 if(!suppressEvent){
18935 this.fireEvent("selectionchange", this, this.selections);
18941 * Returns true if the passed node is selected
18942 * @param {HTMLElement/Number} node The node or node index
18943 * @return {Boolean}
18945 isSelected : function(node){
18946 var s = this.selections;
18950 node = this.getNode(node);
18951 return s.indexOf(node) !== -1;
18956 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18957 * @param {Boolean} keepExisting (optional) true to keep existing selections
18958 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18960 select : function(nodeInfo, keepExisting, suppressEvent){
18961 if(nodeInfo instanceof Array){
18963 this.clearSelections(true);
18965 for(var i = 0, len = nodeInfo.length; i < len; i++){
18966 this.select(nodeInfo[i], true, true);
18970 var node = this.getNode(nodeInfo);
18971 if(!node || this.isSelected(node)){
18972 return; // already selected.
18975 this.clearSelections(true);
18978 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18979 Roo.fly(node).addClass(this.selectedClass);
18980 this.selections.push(node);
18981 if(!suppressEvent){
18982 this.fireEvent("selectionchange", this, this.selections);
18990 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18991 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18992 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18994 unselect : function(nodeInfo, keepExisting, suppressEvent)
18996 if(nodeInfo instanceof Array){
18997 Roo.each(this.selections, function(s) {
18998 this.unselect(s, nodeInfo);
19002 var node = this.getNode(nodeInfo);
19003 if(!node || !this.isSelected(node)){
19004 //Roo.log("not selected");
19005 return; // not selected.
19009 Roo.each(this.selections, function(s) {
19011 Roo.fly(node).removeClass(this.selectedClass);
19018 this.selections= ns;
19019 this.fireEvent("selectionchange", this, this.selections);
19023 * Gets a template node.
19024 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025 * @return {HTMLElement} The node or null if it wasn't found
19027 getNode : function(nodeInfo){
19028 if(typeof nodeInfo == "string"){
19029 return document.getElementById(nodeInfo);
19030 }else if(typeof nodeInfo == "number"){
19031 return this.nodes[nodeInfo];
19037 * Gets a range template nodes.
19038 * @param {Number} startIndex
19039 * @param {Number} endIndex
19040 * @return {Array} An array of nodes
19042 getNodes : function(start, end){
19043 var ns = this.nodes;
19044 start = start || 0;
19045 end = typeof end == "undefined" ? ns.length - 1 : end;
19048 for(var i = start; i <= end; i++){
19052 for(var i = start; i >= end; i--){
19060 * Finds the index of the passed node
19061 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19062 * @return {Number} The index of the node or -1
19064 indexOf : function(node){
19065 node = this.getNode(node);
19066 if(typeof node.nodeIndex == "number"){
19067 return node.nodeIndex;
19069 var ns = this.nodes;
19070 for(var i = 0, len = ns.length; i < len; i++){
19081 * based on jquery fullcalendar
19085 Roo.bootstrap = Roo.bootstrap || {};
19087 * @class Roo.bootstrap.Calendar
19088 * @extends Roo.bootstrap.Component
19089 * Bootstrap Calendar class
19090 * @cfg {Boolean} loadMask (true|false) default false
19091 * @cfg {Object} header generate the user specific header of the calendar, default false
19094 * Create a new Container
19095 * @param {Object} config The config object
19100 Roo.bootstrap.Calendar = function(config){
19101 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19105 * Fires when a date is selected
19106 * @param {DatePicker} this
19107 * @param {Date} date The selected date
19111 * @event monthchange
19112 * Fires when the displayed month changes
19113 * @param {DatePicker} this
19114 * @param {Date} date The selected month
19116 'monthchange': true,
19118 * @event evententer
19119 * Fires when mouse over an event
19120 * @param {Calendar} this
19121 * @param {event} Event
19123 'evententer': true,
19125 * @event eventleave
19126 * Fires when the mouse leaves an
19127 * @param {Calendar} this
19130 'eventleave': true,
19132 * @event eventclick
19133 * Fires when the mouse click an
19134 * @param {Calendar} this
19143 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19146 * @cfg {Number} startDay
19147 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19155 getAutoCreate : function(){
19158 var fc_button = function(name, corner, style, content ) {
19159 return Roo.apply({},{
19161 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19163 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19166 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19177 style : 'width:100%',
19184 cls : 'fc-header-left',
19186 fc_button('prev', 'left', 'arrow', '‹' ),
19187 fc_button('next', 'right', 'arrow', '›' ),
19188 { tag: 'span', cls: 'fc-header-space' },
19189 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19197 cls : 'fc-header-center',
19201 cls: 'fc-header-title',
19204 html : 'month / year'
19212 cls : 'fc-header-right',
19214 /* fc_button('month', 'left', '', 'month' ),
19215 fc_button('week', '', '', 'week' ),
19216 fc_button('day', 'right', '', 'day' )
19228 header = this.header;
19231 var cal_heads = function() {
19233 // fixme - handle this.
19235 for (var i =0; i < Date.dayNames.length; i++) {
19236 var d = Date.dayNames[i];
19239 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19240 html : d.substring(0,3)
19244 ret[0].cls += ' fc-first';
19245 ret[6].cls += ' fc-last';
19248 var cal_cell = function(n) {
19251 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19256 cls: 'fc-day-number',
19260 cls: 'fc-day-content',
19264 style: 'position: relative;' // height: 17px;
19276 var cal_rows = function() {
19279 for (var r = 0; r < 6; r++) {
19286 for (var i =0; i < Date.dayNames.length; i++) {
19287 var d = Date.dayNames[i];
19288 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19291 row.cn[0].cls+=' fc-first';
19292 row.cn[0].cn[0].style = 'min-height:90px';
19293 row.cn[6].cls+=' fc-last';
19297 ret[0].cls += ' fc-first';
19298 ret[4].cls += ' fc-prev-last';
19299 ret[5].cls += ' fc-last';
19306 cls: 'fc-border-separate',
19307 style : 'width:100%',
19315 cls : 'fc-first fc-last',
19333 cls : 'fc-content',
19334 style : "position: relative;",
19337 cls : 'fc-view fc-view-month fc-grid',
19338 style : 'position: relative',
19339 unselectable : 'on',
19342 cls : 'fc-event-container',
19343 style : 'position:absolute;z-index:8;top:0;left:0;'
19361 initEvents : function()
19364 throw "can not find store for calendar";
19370 style: "text-align:center",
19374 style: "background-color:white;width:50%;margin:250 auto",
19378 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19389 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19391 var size = this.el.select('.fc-content', true).first().getSize();
19392 this.maskEl.setSize(size.width, size.height);
19393 this.maskEl.enableDisplayMode("block");
19394 if(!this.loadMask){
19395 this.maskEl.hide();
19398 this.store = Roo.factory(this.store, Roo.data);
19399 this.store.on('load', this.onLoad, this);
19400 this.store.on('beforeload', this.onBeforeLoad, this);
19404 this.cells = this.el.select('.fc-day',true);
19405 //Roo.log(this.cells);
19406 this.textNodes = this.el.query('.fc-day-number');
19407 this.cells.addClassOnOver('fc-state-hover');
19409 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19410 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19411 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19412 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19414 this.on('monthchange', this.onMonthChange, this);
19416 this.update(new Date().clearTime());
19419 resize : function() {
19420 var sz = this.el.getSize();
19422 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19423 this.el.select('.fc-day-content div',true).setHeight(34);
19428 showPrevMonth : function(e){
19429 this.update(this.activeDate.add("mo", -1));
19431 showToday : function(e){
19432 this.update(new Date().clearTime());
19435 showNextMonth : function(e){
19436 this.update(this.activeDate.add("mo", 1));
19440 showPrevYear : function(){
19441 this.update(this.activeDate.add("y", -1));
19445 showNextYear : function(){
19446 this.update(this.activeDate.add("y", 1));
19451 update : function(date)
19453 var vd = this.activeDate;
19454 this.activeDate = date;
19455 // if(vd && this.el){
19456 // var t = date.getTime();
19457 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19458 // Roo.log('using add remove');
19460 // this.fireEvent('monthchange', this, date);
19462 // this.cells.removeClass("fc-state-highlight");
19463 // this.cells.each(function(c){
19464 // if(c.dateValue == t){
19465 // c.addClass("fc-state-highlight");
19466 // setTimeout(function(){
19467 // try{c.dom.firstChild.focus();}catch(e){}
19477 var days = date.getDaysInMonth();
19479 var firstOfMonth = date.getFirstDateOfMonth();
19480 var startingPos = firstOfMonth.getDay()-this.startDay;
19482 if(startingPos < this.startDay){
19486 var pm = date.add(Date.MONTH, -1);
19487 var prevStart = pm.getDaysInMonth()-startingPos;
19489 this.cells = this.el.select('.fc-day',true);
19490 this.textNodes = this.el.query('.fc-day-number');
19491 this.cells.addClassOnOver('fc-state-hover');
19493 var cells = this.cells.elements;
19494 var textEls = this.textNodes;
19496 Roo.each(cells, function(cell){
19497 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19500 days += startingPos;
19502 // convert everything to numbers so it's fast
19503 var day = 86400000;
19504 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19507 //Roo.log(prevStart);
19509 var today = new Date().clearTime().getTime();
19510 var sel = date.clearTime().getTime();
19511 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19512 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19513 var ddMatch = this.disabledDatesRE;
19514 var ddText = this.disabledDatesText;
19515 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19516 var ddaysText = this.disabledDaysText;
19517 var format = this.format;
19519 var setCellClass = function(cal, cell){
19523 //Roo.log('set Cell Class');
19525 var t = d.getTime();
19529 cell.dateValue = t;
19531 cell.className += " fc-today";
19532 cell.className += " fc-state-highlight";
19533 cell.title = cal.todayText;
19536 // disable highlight in other month..
19537 //cell.className += " fc-state-highlight";
19542 cell.className = " fc-state-disabled";
19543 cell.title = cal.minText;
19547 cell.className = " fc-state-disabled";
19548 cell.title = cal.maxText;
19552 if(ddays.indexOf(d.getDay()) != -1){
19553 cell.title = ddaysText;
19554 cell.className = " fc-state-disabled";
19557 if(ddMatch && format){
19558 var fvalue = d.dateFormat(format);
19559 if(ddMatch.test(fvalue)){
19560 cell.title = ddText.replace("%0", fvalue);
19561 cell.className = " fc-state-disabled";
19565 if (!cell.initialClassName) {
19566 cell.initialClassName = cell.dom.className;
19569 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19574 for(; i < startingPos; i++) {
19575 textEls[i].innerHTML = (++prevStart);
19576 d.setDate(d.getDate()+1);
19578 cells[i].className = "fc-past fc-other-month";
19579 setCellClass(this, cells[i]);
19584 for(; i < days; i++){
19585 intDay = i - startingPos + 1;
19586 textEls[i].innerHTML = (intDay);
19587 d.setDate(d.getDate()+1);
19589 cells[i].className = ''; // "x-date-active";
19590 setCellClass(this, cells[i]);
19594 for(; i < 42; i++) {
19595 textEls[i].innerHTML = (++extraDays);
19596 d.setDate(d.getDate()+1);
19598 cells[i].className = "fc-future fc-other-month";
19599 setCellClass(this, cells[i]);
19602 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19604 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19606 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19607 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19609 if(totalRows != 6){
19610 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19611 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19614 this.fireEvent('monthchange', this, date);
19618 if(!this.internalRender){
19619 var main = this.el.dom.firstChild;
19620 var w = main.offsetWidth;
19621 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19622 Roo.fly(main).setWidth(w);
19623 this.internalRender = true;
19624 // opera does not respect the auto grow header center column
19625 // then, after it gets a width opera refuses to recalculate
19626 // without a second pass
19627 if(Roo.isOpera && !this.secondPass){
19628 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19629 this.secondPass = true;
19630 this.update.defer(10, this, [date]);
19637 findCell : function(dt) {
19638 dt = dt.clearTime().getTime();
19640 this.cells.each(function(c){
19641 //Roo.log("check " +c.dateValue + '?=' + dt);
19642 if(c.dateValue == dt){
19652 findCells : function(ev) {
19653 var s = ev.start.clone().clearTime().getTime();
19655 var e= ev.end.clone().clearTime().getTime();
19658 this.cells.each(function(c){
19659 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19661 if(c.dateValue > e){
19664 if(c.dateValue < s){
19673 // findBestRow: function(cells)
19677 // for (var i =0 ; i < cells.length;i++) {
19678 // ret = Math.max(cells[i].rows || 0,ret);
19685 addItem : function(ev)
19687 // look for vertical location slot in
19688 var cells = this.findCells(ev);
19690 // ev.row = this.findBestRow(cells);
19692 // work out the location.
19696 for(var i =0; i < cells.length; i++) {
19698 cells[i].row = cells[0].row;
19701 cells[i].row = cells[i].row + 1;
19711 if (crow.start.getY() == cells[i].getY()) {
19713 crow.end = cells[i];
19730 cells[0].events.push(ev);
19732 this.calevents.push(ev);
19735 clearEvents: function() {
19737 if(!this.calevents){
19741 Roo.each(this.cells.elements, function(c){
19747 Roo.each(this.calevents, function(e) {
19748 Roo.each(e.els, function(el) {
19749 el.un('mouseenter' ,this.onEventEnter, this);
19750 el.un('mouseleave' ,this.onEventLeave, this);
19755 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19761 renderEvents: function()
19765 this.cells.each(function(c) {
19774 if(c.row != c.events.length){
19775 r = 4 - (4 - (c.row - c.events.length));
19778 c.events = ev.slice(0, r);
19779 c.more = ev.slice(r);
19781 if(c.more.length && c.more.length == 1){
19782 c.events.push(c.more.pop());
19785 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19789 this.cells.each(function(c) {
19791 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19794 for (var e = 0; e < c.events.length; e++){
19795 var ev = c.events[e];
19796 var rows = ev.rows;
19798 for(var i = 0; i < rows.length; i++) {
19800 // how many rows should it span..
19803 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19804 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19806 unselectable : "on",
19809 cls: 'fc-event-inner',
19813 // cls: 'fc-event-time',
19814 // html : cells.length > 1 ? '' : ev.time
19818 cls: 'fc-event-title',
19819 html : String.format('{0}', ev.title)
19826 cls: 'ui-resizable-handle ui-resizable-e',
19827 html : '  '
19834 cfg.cls += ' fc-event-start';
19836 if ((i+1) == rows.length) {
19837 cfg.cls += ' fc-event-end';
19840 var ctr = _this.el.select('.fc-event-container',true).first();
19841 var cg = ctr.createChild(cfg);
19843 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19844 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19846 var r = (c.more.length) ? 1 : 0;
19847 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19848 cg.setWidth(ebox.right - sbox.x -2);
19850 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19851 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19852 cg.on('click', _this.onEventClick, _this, ev);
19863 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19864 style : 'position: absolute',
19865 unselectable : "on",
19868 cls: 'fc-event-inner',
19872 cls: 'fc-event-title',
19880 cls: 'ui-resizable-handle ui-resizable-e',
19881 html : '  '
19887 var ctr = _this.el.select('.fc-event-container',true).first();
19888 var cg = ctr.createChild(cfg);
19890 var sbox = c.select('.fc-day-content',true).first().getBox();
19891 var ebox = c.select('.fc-day-content',true).first().getBox();
19893 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19894 cg.setWidth(ebox.right - sbox.x -2);
19896 cg.on('click', _this.onMoreEventClick, _this, c.more);
19906 onEventEnter: function (e, el,event,d) {
19907 this.fireEvent('evententer', this, el, event);
19910 onEventLeave: function (e, el,event,d) {
19911 this.fireEvent('eventleave', this, el, event);
19914 onEventClick: function (e, el,event,d) {
19915 this.fireEvent('eventclick', this, el, event);
19918 onMonthChange: function () {
19922 onMoreEventClick: function(e, el, more)
19926 this.calpopover.placement = 'right';
19927 this.calpopover.setTitle('More');
19929 this.calpopover.setContent('');
19931 var ctr = this.calpopover.el.select('.popover-content', true).first();
19933 Roo.each(more, function(m){
19935 cls : 'fc-event-hori fc-event-draggable',
19938 var cg = ctr.createChild(cfg);
19940 cg.on('click', _this.onEventClick, _this, m);
19943 this.calpopover.show(el);
19948 onLoad: function ()
19950 this.calevents = [];
19953 if(this.store.getCount() > 0){
19954 this.store.data.each(function(d){
19957 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19958 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19959 time : d.data.start_time,
19960 title : d.data.title,
19961 description : d.data.description,
19962 venue : d.data.venue
19967 this.renderEvents();
19969 if(this.calevents.length && this.loadMask){
19970 this.maskEl.hide();
19974 onBeforeLoad: function()
19976 this.clearEvents();
19978 this.maskEl.show();
19992 * @class Roo.bootstrap.Popover
19993 * @extends Roo.bootstrap.Component
19994 * Bootstrap Popover class
19995 * @cfg {String} html contents of the popover (or false to use children..)
19996 * @cfg {String} title of popover (or false to hide)
19997 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19998 * @cfg {String} trigger click || hover (or false to trigger manually)
19999 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20000 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20001 * - if false and it has a 'parent' then it will be automatically added to that element
20002 * - if string - Roo.get will be called
20003 * @cfg {Number} delay - delay before showing
20006 * Create a new Popover
20007 * @param {Object} config The config object
20010 Roo.bootstrap.Popover = function(config){
20011 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20017 * After the popover show
20019 * @param {Roo.bootstrap.Popover} this
20024 * After the popover hide
20026 * @param {Roo.bootstrap.Popover} this
20032 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20037 placement : 'right',
20038 trigger : 'hover', // hover
20044 can_build_overlaid : false,
20046 maskEl : false, // the mask element
20049 alignEl : false, // when show is called with an element - this get's stored.
20051 getChildContainer : function()
20053 return this.contentEl;
20056 getPopoverHeader : function()
20058 this.title = true; // flag not to hide it..
20059 this.headerEl.addClass('p-0');
20060 return this.headerEl
20064 getAutoCreate : function(){
20067 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20068 style: 'display:block',
20074 cls : 'popover-inner ',
20078 cls: 'popover-title popover-header',
20079 html : this.title === false ? '' : this.title
20082 cls : 'popover-content popover-body ' + (this.cls || ''),
20083 html : this.html || ''
20094 * @param {string} the title
20096 setTitle: function(str)
20100 this.headerEl.dom.innerHTML = str;
20105 * @param {string} the body content
20107 setContent: function(str)
20110 if (this.contentEl) {
20111 this.contentEl.dom.innerHTML = str;
20115 // as it get's added to the bottom of the page.
20116 onRender : function(ct, position)
20118 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20123 var cfg = Roo.apply({}, this.getAutoCreate());
20127 cfg.cls += ' ' + this.cls;
20130 cfg.style = this.style;
20132 //Roo.log("adding to ");
20133 this.el = Roo.get(document.body).createChild(cfg, position);
20134 // Roo.log(this.el);
20137 this.contentEl = this.el.select('.popover-content',true).first();
20138 this.headerEl = this.el.select('.popover-title',true).first();
20141 if(typeof(this.items) != 'undefined'){
20142 var items = this.items;
20145 for(var i =0;i < items.length;i++) {
20146 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20150 this.items = nitems;
20152 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20153 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20160 resizeMask : function()
20162 this.maskEl.setSize(
20163 Roo.lib.Dom.getViewWidth(true),
20164 Roo.lib.Dom.getViewHeight(true)
20168 initEvents : function()
20172 Roo.bootstrap.Popover.register(this);
20175 this.arrowEl = this.el.select('.arrow',true).first();
20176 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20177 this.el.enableDisplayMode('block');
20181 if (this.over === false && !this.parent()) {
20184 if (this.triggers === false) {
20189 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20190 var triggers = this.trigger ? this.trigger.split(' ') : [];
20191 Roo.each(triggers, function(trigger) {
20193 if (trigger == 'click') {
20194 on_el.on('click', this.toggle, this);
20195 } else if (trigger != 'manual') {
20196 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20197 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20199 on_el.on(eventIn ,this.enter, this);
20200 on_el.on(eventOut, this.leave, this);
20210 toggle : function () {
20211 this.hoverState == 'in' ? this.leave() : this.enter();
20214 enter : function () {
20216 clearTimeout(this.timeout);
20218 this.hoverState = 'in';
20220 if (!this.delay || !this.delay.show) {
20225 this.timeout = setTimeout(function () {
20226 if (_t.hoverState == 'in') {
20229 }, this.delay.show)
20232 leave : function() {
20233 clearTimeout(this.timeout);
20235 this.hoverState = 'out';
20237 if (!this.delay || !this.delay.hide) {
20242 this.timeout = setTimeout(function () {
20243 if (_t.hoverState == 'out') {
20246 }, this.delay.hide)
20250 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20251 * @param {string} (left|right|top|bottom) position
20253 show : function (on_el, placement)
20255 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20256 on_el = on_el || false; // default to false
20259 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20260 on_el = this.parent().el;
20261 } else if (this.over) {
20262 on_el = Roo.get(this.over);
20267 this.alignEl = Roo.get( on_el );
20270 this.render(document.body);
20276 if (this.title === false) {
20277 this.headerEl.hide();
20282 this.el.dom.style.display = 'block';
20285 if (this.alignEl) {
20286 this.updatePosition(this.placement, true);
20289 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20290 var es = this.el.getSize();
20291 var x = Roo.lib.Dom.getViewWidth()/2;
20292 var y = Roo.lib.Dom.getViewHeight()/2;
20293 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20298 //var arrow = this.el.select('.arrow',true).first();
20299 //arrow.set(align[2],
20301 this.el.addClass('in');
20305 this.hoverState = 'in';
20308 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20309 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20310 this.maskEl.dom.style.display = 'block';
20311 this.maskEl.addClass('show');
20313 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20315 this.fireEvent('show', this);
20319 * fire this manually after loading a grid in the table for example
20320 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20321 * @param {Boolean} try and move it if we cant get right position.
20323 updatePosition : function(placement, try_move)
20325 // allow for calling with no parameters
20326 placement = placement ? placement : this.placement;
20327 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20329 this.el.removeClass([
20330 'fade','top','bottom', 'left', 'right','in',
20331 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20333 this.el.addClass(placement + ' bs-popover-' + placement);
20335 if (!this.alignEl ) {
20339 switch (placement) {
20341 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20342 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20343 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344 //normal display... or moved up/down.
20345 this.el.setXY(offset);
20346 var xy = this.alignEl.getAnchorXY('tr', false);
20348 this.arrowEl.setXY(xy);
20351 // continue through...
20352 return this.updatePosition('left', false);
20356 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20357 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20358 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20359 //normal display... or moved up/down.
20360 this.el.setXY(offset);
20361 var xy = this.alignEl.getAnchorXY('tl', false);
20362 xy[0]-=10;xy[1]+=5; // << fix me
20363 this.arrowEl.setXY(xy);
20367 return this.updatePosition('right', false);
20370 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20371 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20372 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20373 //normal display... or moved up/down.
20374 this.el.setXY(offset);
20375 var xy = this.alignEl.getAnchorXY('t', false);
20376 xy[1]-=10; // << fix me
20377 this.arrowEl.setXY(xy);
20381 return this.updatePosition('bottom', false);
20384 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20385 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20386 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20387 //normal display... or moved up/down.
20388 this.el.setXY(offset);
20389 var xy = this.alignEl.getAnchorXY('b', false);
20390 xy[1]+=2; // << fix me
20391 this.arrowEl.setXY(xy);
20395 return this.updatePosition('top', false);
20406 this.el.setXY([0,0]);
20407 this.el.removeClass('in');
20409 this.hoverState = null;
20410 this.maskEl.hide(); // always..
20411 this.fireEvent('hide', this);
20417 Roo.apply(Roo.bootstrap.Popover, {
20420 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20421 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20422 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20423 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20428 clickHander : false,
20432 onMouseDown : function(e)
20434 if (this.popups.length && !e.getTarget(".roo-popover")) {
20435 /// what is nothing is showing..
20444 register : function(popup)
20446 if (!Roo.bootstrap.Popover.clickHandler) {
20447 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20449 // hide other popups.
20450 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20451 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20452 this.hideAll(); //<< why?
20453 //this.popups.push(popup);
20455 hideAll : function()
20457 this.popups.forEach(function(p) {
20461 onShow : function() {
20462 Roo.bootstrap.Popover.popups.push(this);
20464 onHide : function() {
20465 Roo.bootstrap.Popover.popups.remove(this);
20471 * Card header - holder for the card header elements.
20476 * @class Roo.bootstrap.PopoverNav
20477 * @extends Roo.bootstrap.NavGroup
20478 * Bootstrap Popover header navigation class
20480 * Create a new Popover Header Navigation
20481 * @param {Object} config The config object
20484 Roo.bootstrap.PopoverNav = function(config){
20485 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20488 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20491 container_method : 'getPopoverHeader'
20509 * @class Roo.bootstrap.Progress
20510 * @extends Roo.bootstrap.Component
20511 * Bootstrap Progress class
20512 * @cfg {Boolean} striped striped of the progress bar
20513 * @cfg {Boolean} active animated of the progress bar
20517 * Create a new Progress
20518 * @param {Object} config The config object
20521 Roo.bootstrap.Progress = function(config){
20522 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20525 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20530 getAutoCreate : function(){
20538 cfg.cls += ' progress-striped';
20542 cfg.cls += ' active';
20561 * @class Roo.bootstrap.ProgressBar
20562 * @extends Roo.bootstrap.Component
20563 * Bootstrap ProgressBar class
20564 * @cfg {Number} aria_valuenow aria-value now
20565 * @cfg {Number} aria_valuemin aria-value min
20566 * @cfg {Number} aria_valuemax aria-value max
20567 * @cfg {String} label label for the progress bar
20568 * @cfg {String} panel (success | info | warning | danger )
20569 * @cfg {String} role role of the progress bar
20570 * @cfg {String} sr_only text
20574 * Create a new ProgressBar
20575 * @param {Object} config The config object
20578 Roo.bootstrap.ProgressBar = function(config){
20579 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20582 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20586 aria_valuemax : 100,
20592 getAutoCreate : function()
20597 cls: 'progress-bar',
20598 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20610 cfg.role = this.role;
20613 if(this.aria_valuenow){
20614 cfg['aria-valuenow'] = this.aria_valuenow;
20617 if(this.aria_valuemin){
20618 cfg['aria-valuemin'] = this.aria_valuemin;
20621 if(this.aria_valuemax){
20622 cfg['aria-valuemax'] = this.aria_valuemax;
20625 if(this.label && !this.sr_only){
20626 cfg.html = this.label;
20630 cfg.cls += ' progress-bar-' + this.panel;
20636 update : function(aria_valuenow)
20638 this.aria_valuenow = aria_valuenow;
20640 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20655 * @class Roo.bootstrap.TabGroup
20656 * @extends Roo.bootstrap.Column
20657 * Bootstrap Column class
20658 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20659 * @cfg {Boolean} carousel true to make the group behave like a carousel
20660 * @cfg {Boolean} bullets show bullets for the panels
20661 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20662 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20663 * @cfg {Boolean} showarrow (true|false) show arrow default true
20666 * Create a new TabGroup
20667 * @param {Object} config The config object
20670 Roo.bootstrap.TabGroup = function(config){
20671 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20673 this.navId = Roo.id();
20676 Roo.bootstrap.TabGroup.register(this);
20680 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20683 transition : false,
20688 slideOnTouch : false,
20691 getAutoCreate : function()
20693 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20695 cfg.cls += ' tab-content';
20697 if (this.carousel) {
20698 cfg.cls += ' carousel slide';
20701 cls : 'carousel-inner',
20705 if(this.bullets && !Roo.isTouch){
20708 cls : 'carousel-bullets',
20712 if(this.bullets_cls){
20713 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20720 cfg.cn[0].cn.push(bullets);
20723 if(this.showarrow){
20724 cfg.cn[0].cn.push({
20726 class : 'carousel-arrow',
20730 class : 'carousel-prev',
20734 class : 'fa fa-chevron-left'
20740 class : 'carousel-next',
20744 class : 'fa fa-chevron-right'
20757 initEvents: function()
20759 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20760 // this.el.on("touchstart", this.onTouchStart, this);
20763 if(this.autoslide){
20766 this.slideFn = window.setInterval(function() {
20767 _this.showPanelNext();
20771 if(this.showarrow){
20772 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20773 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20779 // onTouchStart : function(e, el, o)
20781 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20785 // this.showPanelNext();
20789 getChildContainer : function()
20791 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20795 * register a Navigation item
20796 * @param {Roo.bootstrap.NavItem} the navitem to add
20798 register : function(item)
20800 this.tabs.push( item);
20801 item.navId = this.navId; // not really needed..
20806 getActivePanel : function()
20809 Roo.each(this.tabs, function(t) {
20819 getPanelByName : function(n)
20822 Roo.each(this.tabs, function(t) {
20823 if (t.tabId == n) {
20831 indexOfPanel : function(p)
20834 Roo.each(this.tabs, function(t,i) {
20835 if (t.tabId == p.tabId) {
20844 * show a specific panel
20845 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20846 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20848 showPanel : function (pan)
20850 if(this.transition || typeof(pan) == 'undefined'){
20851 Roo.log("waiting for the transitionend");
20855 if (typeof(pan) == 'number') {
20856 pan = this.tabs[pan];
20859 if (typeof(pan) == 'string') {
20860 pan = this.getPanelByName(pan);
20863 var cur = this.getActivePanel();
20866 Roo.log('pan or acitve pan is undefined');
20870 if (pan.tabId == this.getActivePanel().tabId) {
20874 if (false === cur.fireEvent('beforedeactivate')) {
20878 if(this.bullets > 0 && !Roo.isTouch){
20879 this.setActiveBullet(this.indexOfPanel(pan));
20882 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20884 //class="carousel-item carousel-item-next carousel-item-left"
20886 this.transition = true;
20887 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20888 var lr = dir == 'next' ? 'left' : 'right';
20889 pan.el.addClass(dir); // or prev
20890 pan.el.addClass('carousel-item-' + dir); // or prev
20891 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20892 cur.el.addClass(lr); // or right
20893 pan.el.addClass(lr);
20894 cur.el.addClass('carousel-item-' +lr); // or right
20895 pan.el.addClass('carousel-item-' +lr);
20899 cur.el.on('transitionend', function() {
20900 Roo.log("trans end?");
20902 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20903 pan.setActive(true);
20905 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20906 cur.setActive(false);
20908 _this.transition = false;
20910 }, this, { single: true } );
20915 cur.setActive(false);
20916 pan.setActive(true);
20921 showPanelNext : function()
20923 var i = this.indexOfPanel(this.getActivePanel());
20925 if (i >= this.tabs.length - 1 && !this.autoslide) {
20929 if (i >= this.tabs.length - 1 && this.autoslide) {
20933 this.showPanel(this.tabs[i+1]);
20936 showPanelPrev : function()
20938 var i = this.indexOfPanel(this.getActivePanel());
20940 if (i < 1 && !this.autoslide) {
20944 if (i < 1 && this.autoslide) {
20945 i = this.tabs.length;
20948 this.showPanel(this.tabs[i-1]);
20952 addBullet: function()
20954 if(!this.bullets || Roo.isTouch){
20957 var ctr = this.el.select('.carousel-bullets',true).first();
20958 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20959 var bullet = ctr.createChild({
20960 cls : 'bullet bullet-' + i
20961 },ctr.dom.lastChild);
20966 bullet.on('click', (function(e, el, o, ii, t){
20968 e.preventDefault();
20970 this.showPanel(ii);
20972 if(this.autoslide && this.slideFn){
20973 clearInterval(this.slideFn);
20974 this.slideFn = window.setInterval(function() {
20975 _this.showPanelNext();
20979 }).createDelegate(this, [i, bullet], true));
20984 setActiveBullet : function(i)
20990 Roo.each(this.el.select('.bullet', true).elements, function(el){
20991 el.removeClass('selected');
20994 var bullet = this.el.select('.bullet-' + i, true).first();
21000 bullet.addClass('selected');
21011 Roo.apply(Roo.bootstrap.TabGroup, {
21015 * register a Navigation Group
21016 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21018 register : function(navgrp)
21020 this.groups[navgrp.navId] = navgrp;
21024 * fetch a Navigation Group based on the navigation ID
21025 * if one does not exist , it will get created.
21026 * @param {string} the navgroup to add
21027 * @returns {Roo.bootstrap.NavGroup} the navgroup
21029 get: function(navId) {
21030 if (typeof(this.groups[navId]) == 'undefined') {
21031 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21033 return this.groups[navId] ;
21048 * @class Roo.bootstrap.TabPanel
21049 * @extends Roo.bootstrap.Component
21050 * Bootstrap TabPanel class
21051 * @cfg {Boolean} active panel active
21052 * @cfg {String} html panel content
21053 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21054 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21055 * @cfg {String} href click to link..
21056 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21060 * Create a new TabPanel
21061 * @param {Object} config The config object
21064 Roo.bootstrap.TabPanel = function(config){
21065 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21069 * Fires when the active status changes
21070 * @param {Roo.bootstrap.TabPanel} this
21071 * @param {Boolean} state the new state
21076 * @event beforedeactivate
21077 * Fires before a tab is de-activated - can be used to do validation on a form.
21078 * @param {Roo.bootstrap.TabPanel} this
21079 * @return {Boolean} false if there is an error
21082 'beforedeactivate': true
21085 this.tabId = this.tabId || Roo.id();
21089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21096 touchSlide : false,
21097 getAutoCreate : function(){
21102 // item is needed for carousel - not sure if it has any effect otherwise
21103 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21104 html: this.html || ''
21108 cfg.cls += ' active';
21112 cfg.tabId = this.tabId;
21120 initEvents: function()
21122 var p = this.parent();
21124 this.navId = this.navId || p.navId;
21126 if (typeof(this.navId) != 'undefined') {
21127 // not really needed.. but just in case.. parent should be a NavGroup.
21128 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21132 var i = tg.tabs.length - 1;
21134 if(this.active && tg.bullets > 0 && i < tg.bullets){
21135 tg.setActiveBullet(i);
21139 this.el.on('click', this.onClick, this);
21141 if(Roo.isTouch && this.touchSlide){
21142 this.el.on("touchstart", this.onTouchStart, this);
21143 this.el.on("touchmove", this.onTouchMove, this);
21144 this.el.on("touchend", this.onTouchEnd, this);
21149 onRender : function(ct, position)
21151 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21154 setActive : function(state)
21156 Roo.log("panel - set active " + this.tabId + "=" + state);
21158 this.active = state;
21160 this.el.removeClass('active');
21162 } else if (!this.el.hasClass('active')) {
21163 this.el.addClass('active');
21166 this.fireEvent('changed', this, state);
21169 onClick : function(e)
21171 e.preventDefault();
21173 if(!this.href.length){
21177 window.location.href = this.href;
21186 onTouchStart : function(e)
21188 this.swiping = false;
21190 this.startX = e.browserEvent.touches[0].clientX;
21191 this.startY = e.browserEvent.touches[0].clientY;
21194 onTouchMove : function(e)
21196 this.swiping = true;
21198 this.endX = e.browserEvent.touches[0].clientX;
21199 this.endY = e.browserEvent.touches[0].clientY;
21202 onTouchEnd : function(e)
21209 var tabGroup = this.parent();
21211 if(this.endX > this.startX){ // swiping right
21212 tabGroup.showPanelPrev();
21216 if(this.startX > this.endX){ // swiping left
21217 tabGroup.showPanelNext();
21236 * @class Roo.bootstrap.DateField
21237 * @extends Roo.bootstrap.Input
21238 * Bootstrap DateField class
21239 * @cfg {Number} weekStart default 0
21240 * @cfg {String} viewMode default empty, (months|years)
21241 * @cfg {String} minViewMode default empty, (months|years)
21242 * @cfg {Number} startDate default -Infinity
21243 * @cfg {Number} endDate default Infinity
21244 * @cfg {Boolean} todayHighlight default false
21245 * @cfg {Boolean} todayBtn default false
21246 * @cfg {Boolean} calendarWeeks default false
21247 * @cfg {Object} daysOfWeekDisabled default empty
21248 * @cfg {Boolean} singleMode default false (true | false)
21250 * @cfg {Boolean} keyboardNavigation default true
21251 * @cfg {String} language default en
21254 * Create a new DateField
21255 * @param {Object} config The config object
21258 Roo.bootstrap.DateField = function(config){
21259 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21263 * Fires when this field show.
21264 * @param {Roo.bootstrap.DateField} this
21265 * @param {Mixed} date The date value
21270 * Fires when this field hide.
21271 * @param {Roo.bootstrap.DateField} this
21272 * @param {Mixed} date The date value
21277 * Fires when select a date.
21278 * @param {Roo.bootstrap.DateField} this
21279 * @param {Mixed} date The date value
21283 * @event beforeselect
21284 * Fires when before select a date.
21285 * @param {Roo.bootstrap.DateField} this
21286 * @param {Mixed} date The date value
21288 beforeselect : true
21292 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21295 * @cfg {String} format
21296 * The default date format string which can be overriden for localization support. The format must be
21297 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21301 * @cfg {String} altFormats
21302 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21303 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21305 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21313 todayHighlight : false,
21319 keyboardNavigation: true,
21321 calendarWeeks: false,
21323 startDate: -Infinity,
21327 daysOfWeekDisabled: [],
21331 singleMode : false,
21333 UTCDate: function()
21335 return new Date(Date.UTC.apply(Date, arguments));
21338 UTCToday: function()
21340 var today = new Date();
21341 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21344 getDate: function() {
21345 var d = this.getUTCDate();
21346 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21349 getUTCDate: function() {
21353 setDate: function(d) {
21354 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21357 setUTCDate: function(d) {
21359 this.setValue(this.formatDate(this.date));
21362 onRender: function(ct, position)
21365 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21367 this.language = this.language || 'en';
21368 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21369 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21371 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21372 this.format = this.format || 'm/d/y';
21373 this.isInline = false;
21374 this.isInput = true;
21375 this.component = this.el.select('.add-on', true).first() || false;
21376 this.component = (this.component && this.component.length === 0) ? false : this.component;
21377 this.hasInput = this.component && this.inputEl().length;
21379 if (typeof(this.minViewMode === 'string')) {
21380 switch (this.minViewMode) {
21382 this.minViewMode = 1;
21385 this.minViewMode = 2;
21388 this.minViewMode = 0;
21393 if (typeof(this.viewMode === 'string')) {
21394 switch (this.viewMode) {
21407 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21409 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21411 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21413 this.picker().on('mousedown', this.onMousedown, this);
21414 this.picker().on('click', this.onClick, this);
21416 this.picker().addClass('datepicker-dropdown');
21418 this.startViewMode = this.viewMode;
21420 if(this.singleMode){
21421 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21422 v.setVisibilityMode(Roo.Element.DISPLAY);
21426 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21427 v.setStyle('width', '189px');
21431 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21432 if(!this.calendarWeeks){
21437 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21438 v.attr('colspan', function(i, val){
21439 return parseInt(val) + 1;
21444 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21446 this.setStartDate(this.startDate);
21447 this.setEndDate(this.endDate);
21449 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21456 if(this.isInline) {
21461 picker : function()
21463 return this.pickerEl;
21464 // return this.el.select('.datepicker', true).first();
21467 fillDow: function()
21469 var dowCnt = this.weekStart;
21478 if(this.calendarWeeks){
21486 while (dowCnt < this.weekStart + 7) {
21490 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21494 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21497 fillMonths: function()
21500 var months = this.picker().select('>.datepicker-months td', true).first();
21502 months.dom.innerHTML = '';
21508 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21511 months.createChild(month);
21518 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
21520 if (this.date < this.startDate) {
21521 this.viewDate = new Date(this.startDate);
21522 } else if (this.date > this.endDate) {
21523 this.viewDate = new Date(this.endDate);
21525 this.viewDate = new Date(this.date);
21533 var d = new Date(this.viewDate),
21534 year = d.getUTCFullYear(),
21535 month = d.getUTCMonth(),
21536 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21537 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21538 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21539 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21540 currentDate = this.date && this.date.valueOf(),
21541 today = this.UTCToday();
21543 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21545 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21547 // this.picker.select('>tfoot th.today').
21548 // .text(dates[this.language].today)
21549 // .toggle(this.todayBtn !== false);
21551 this.updateNavArrows();
21554 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21556 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21558 prevMonth.setUTCDate(day);
21560 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21562 var nextMonth = new Date(prevMonth);
21564 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21566 nextMonth = nextMonth.valueOf();
21568 var fillMonths = false;
21570 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21572 while(prevMonth.valueOf() <= nextMonth) {
21575 if (prevMonth.getUTCDay() === this.weekStart) {
21577 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21585 if(this.calendarWeeks){
21586 // ISO 8601: First week contains first thursday.
21587 // ISO also states week starts on Monday, but we can be more abstract here.
21589 // Start of current week: based on weekstart/current date
21590 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21591 // Thursday of this week
21592 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21593 // First Thursday of year, year from thursday
21594 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21595 // Calendar week: ms between thursdays, div ms per day, div 7 days
21596 calWeek = (th - yth) / 864e5 / 7 + 1;
21598 fillMonths.cn.push({
21606 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21608 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21611 if (this.todayHighlight &&
21612 prevMonth.getUTCFullYear() == today.getFullYear() &&
21613 prevMonth.getUTCMonth() == today.getMonth() &&
21614 prevMonth.getUTCDate() == today.getDate()) {
21615 clsName += ' today';
21618 if (currentDate && prevMonth.valueOf() === currentDate) {
21619 clsName += ' active';
21622 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21623 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21624 clsName += ' disabled';
21627 fillMonths.cn.push({
21629 cls: 'day ' + clsName,
21630 html: prevMonth.getDate()
21633 prevMonth.setDate(prevMonth.getDate()+1);
21636 var currentYear = this.date && this.date.getUTCFullYear();
21637 var currentMonth = this.date && this.date.getUTCMonth();
21639 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21641 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21642 v.removeClass('active');
21644 if(currentYear === year && k === currentMonth){
21645 v.addClass('active');
21648 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21649 v.addClass('disabled');
21655 year = parseInt(year/10, 10) * 10;
21657 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21659 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21662 for (var i = -1; i < 11; i++) {
21663 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21665 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21673 showMode: function(dir)
21676 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21679 Roo.each(this.picker().select('>div',true).elements, function(v){
21680 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21683 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21688 if(this.isInline) {
21692 this.picker().removeClass(['bottom', 'top']);
21694 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21696 * place to the top of element!
21700 this.picker().addClass('top');
21701 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21706 this.picker().addClass('bottom');
21708 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21711 parseDate : function(value)
21713 if(!value || value instanceof Date){
21716 var v = Date.parseDate(value, this.format);
21717 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21718 v = Date.parseDate(value, 'Y-m-d');
21720 if(!v && this.altFormats){
21721 if(!this.altFormatsArray){
21722 this.altFormatsArray = this.altFormats.split("|");
21724 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21725 v = Date.parseDate(value, this.altFormatsArray[i]);
21731 formatDate : function(date, fmt)
21733 return (!date || !(date instanceof Date)) ?
21734 date : date.dateFormat(fmt || this.format);
21737 onFocus : function()
21739 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21743 onBlur : function()
21745 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21747 var d = this.inputEl().getValue();
21754 showPopup : function()
21756 this.picker().show();
21760 this.fireEvent('showpopup', this, this.date);
21763 hidePopup : function()
21765 if(this.isInline) {
21768 this.picker().hide();
21769 this.viewMode = this.startViewMode;
21772 this.fireEvent('hidepopup', this, this.date);
21776 onMousedown: function(e)
21778 e.stopPropagation();
21779 e.preventDefault();
21784 Roo.bootstrap.DateField.superclass.keyup.call(this);
21788 setValue: function(v)
21790 if(this.fireEvent('beforeselect', this, v) !== false){
21791 var d = new Date(this.parseDate(v) ).clearTime();
21793 if(isNaN(d.getTime())){
21794 this.date = this.viewDate = '';
21795 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21799 v = this.formatDate(d);
21801 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21803 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21807 this.fireEvent('select', this, this.date);
21811 getValue: function()
21813 return this.formatDate(this.date);
21816 fireKey: function(e)
21818 if (!this.picker().isVisible()){
21819 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21825 var dateChanged = false,
21827 newDate, newViewDate;
21832 e.preventDefault();
21836 if (!this.keyboardNavigation) {
21839 dir = e.keyCode == 37 ? -1 : 1;
21842 newDate = this.moveYear(this.date, dir);
21843 newViewDate = this.moveYear(this.viewDate, dir);
21844 } else if (e.shiftKey){
21845 newDate = this.moveMonth(this.date, dir);
21846 newViewDate = this.moveMonth(this.viewDate, dir);
21848 newDate = new Date(this.date);
21849 newDate.setUTCDate(this.date.getUTCDate() + dir);
21850 newViewDate = new Date(this.viewDate);
21851 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21853 if (this.dateWithinRange(newDate)){
21854 this.date = newDate;
21855 this.viewDate = newViewDate;
21856 this.setValue(this.formatDate(this.date));
21858 e.preventDefault();
21859 dateChanged = true;
21864 if (!this.keyboardNavigation) {
21867 dir = e.keyCode == 38 ? -1 : 1;
21869 newDate = this.moveYear(this.date, dir);
21870 newViewDate = this.moveYear(this.viewDate, dir);
21871 } else if (e.shiftKey){
21872 newDate = this.moveMonth(this.date, dir);
21873 newViewDate = this.moveMonth(this.viewDate, dir);
21875 newDate = new Date(this.date);
21876 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21877 newViewDate = new Date(this.viewDate);
21878 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21880 if (this.dateWithinRange(newDate)){
21881 this.date = newDate;
21882 this.viewDate = newViewDate;
21883 this.setValue(this.formatDate(this.date));
21885 e.preventDefault();
21886 dateChanged = true;
21890 this.setValue(this.formatDate(this.date));
21892 e.preventDefault();
21895 this.setValue(this.formatDate(this.date));
21909 onClick: function(e)
21911 e.stopPropagation();
21912 e.preventDefault();
21914 var target = e.getTarget();
21916 if(target.nodeName.toLowerCase() === 'i'){
21917 target = Roo.get(target).dom.parentNode;
21920 var nodeName = target.nodeName;
21921 var className = target.className;
21922 var html = target.innerHTML;
21923 //Roo.log(nodeName);
21925 switch(nodeName.toLowerCase()) {
21927 switch(className) {
21933 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21934 switch(this.viewMode){
21936 this.viewDate = this.moveMonth(this.viewDate, dir);
21940 this.viewDate = this.moveYear(this.viewDate, dir);
21946 var date = new Date();
21947 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21949 this.setValue(this.formatDate(this.date));
21956 if (className.indexOf('disabled') < 0) {
21957 this.viewDate.setUTCDate(1);
21958 if (className.indexOf('month') > -1) {
21959 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21961 var year = parseInt(html, 10) || 0;
21962 this.viewDate.setUTCFullYear(year);
21966 if(this.singleMode){
21967 this.setValue(this.formatDate(this.viewDate));
21978 //Roo.log(className);
21979 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21980 var day = parseInt(html, 10) || 1;
21981 var year = (this.viewDate || new Date()).getUTCFullYear(),
21982 month = (this.viewDate || new Date()).getUTCMonth();
21984 if (className.indexOf('old') > -1) {
21991 } else if (className.indexOf('new') > -1) {
21999 //Roo.log([year,month,day]);
22000 this.date = this.UTCDate(year, month, day,0,0,0,0);
22001 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22003 //Roo.log(this.formatDate(this.date));
22004 this.setValue(this.formatDate(this.date));
22011 setStartDate: function(startDate)
22013 this.startDate = startDate || -Infinity;
22014 if (this.startDate !== -Infinity) {
22015 this.startDate = this.parseDate(this.startDate);
22018 this.updateNavArrows();
22021 setEndDate: function(endDate)
22023 this.endDate = endDate || Infinity;
22024 if (this.endDate !== Infinity) {
22025 this.endDate = this.parseDate(this.endDate);
22028 this.updateNavArrows();
22031 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22033 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22034 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22035 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22037 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22038 return parseInt(d, 10);
22041 this.updateNavArrows();
22044 updateNavArrows: function()
22046 if(this.singleMode){
22050 var d = new Date(this.viewDate),
22051 year = d.getUTCFullYear(),
22052 month = d.getUTCMonth();
22054 Roo.each(this.picker().select('.prev', true).elements, function(v){
22056 switch (this.viewMode) {
22059 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22065 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22072 Roo.each(this.picker().select('.next', true).elements, function(v){
22074 switch (this.viewMode) {
22077 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22083 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22091 moveMonth: function(date, dir)
22096 var new_date = new Date(date.valueOf()),
22097 day = new_date.getUTCDate(),
22098 month = new_date.getUTCMonth(),
22099 mag = Math.abs(dir),
22101 dir = dir > 0 ? 1 : -1;
22104 // If going back one month, make sure month is not current month
22105 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22107 return new_date.getUTCMonth() == month;
22109 // If going forward one month, make sure month is as expected
22110 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22112 return new_date.getUTCMonth() != new_month;
22114 new_month = month + dir;
22115 new_date.setUTCMonth(new_month);
22116 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22117 if (new_month < 0 || new_month > 11) {
22118 new_month = (new_month + 12) % 12;
22121 // For magnitudes >1, move one month at a time...
22122 for (var i=0; i<mag; i++) {
22123 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22124 new_date = this.moveMonth(new_date, dir);
22126 // ...then reset the day, keeping it in the new month
22127 new_month = new_date.getUTCMonth();
22128 new_date.setUTCDate(day);
22130 return new_month != new_date.getUTCMonth();
22133 // Common date-resetting loop -- if date is beyond end of month, make it
22136 new_date.setUTCDate(--day);
22137 new_date.setUTCMonth(new_month);
22142 moveYear: function(date, dir)
22144 return this.moveMonth(date, dir*12);
22147 dateWithinRange: function(date)
22149 return date >= this.startDate && date <= this.endDate;
22155 this.picker().remove();
22158 validateValue : function(value)
22160 if(this.getVisibilityEl().hasClass('hidden')){
22164 if(value.length < 1) {
22165 if(this.allowBlank){
22171 if(value.length < this.minLength){
22174 if(value.length > this.maxLength){
22178 var vt = Roo.form.VTypes;
22179 if(!vt[this.vtype](value, this)){
22183 if(typeof this.validator == "function"){
22184 var msg = this.validator(value);
22190 if(this.regex && !this.regex.test(value)){
22194 if(typeof(this.parseDate(value)) == 'undefined'){
22198 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22202 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22212 this.date = this.viewDate = '';
22214 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22219 Roo.apply(Roo.bootstrap.DateField, {
22230 html: '<i class="fa fa-arrow-left"/>'
22240 html: '<i class="fa fa-arrow-right"/>'
22282 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22283 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22284 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22285 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22286 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22299 navFnc: 'FullYear',
22304 navFnc: 'FullYear',
22309 Roo.apply(Roo.bootstrap.DateField, {
22313 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22317 cls: 'datepicker-days',
22321 cls: 'table-condensed',
22323 Roo.bootstrap.DateField.head,
22327 Roo.bootstrap.DateField.footer
22334 cls: 'datepicker-months',
22338 cls: 'table-condensed',
22340 Roo.bootstrap.DateField.head,
22341 Roo.bootstrap.DateField.content,
22342 Roo.bootstrap.DateField.footer
22349 cls: 'datepicker-years',
22353 cls: 'table-condensed',
22355 Roo.bootstrap.DateField.head,
22356 Roo.bootstrap.DateField.content,
22357 Roo.bootstrap.DateField.footer
22376 * @class Roo.bootstrap.TimeField
22377 * @extends Roo.bootstrap.Input
22378 * Bootstrap DateField class
22382 * Create a new TimeField
22383 * @param {Object} config The config object
22386 Roo.bootstrap.TimeField = function(config){
22387 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22391 * Fires when this field show.
22392 * @param {Roo.bootstrap.DateField} thisthis
22393 * @param {Mixed} date The date value
22398 * Fires when this field hide.
22399 * @param {Roo.bootstrap.DateField} this
22400 * @param {Mixed} date The date value
22405 * Fires when select a date.
22406 * @param {Roo.bootstrap.DateField} this
22407 * @param {Mixed} date The date value
22413 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22416 * @cfg {String} format
22417 * The default time format string which can be overriden for localization support. The format must be
22418 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22422 getAutoCreate : function()
22424 this.after = '<i class="fa far fa-clock"></i>';
22425 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22429 onRender: function(ct, position)
22432 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22434 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22436 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22438 this.pop = this.picker().select('>.datepicker-time',true).first();
22439 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22441 this.picker().on('mousedown', this.onMousedown, this);
22442 this.picker().on('click', this.onClick, this);
22444 this.picker().addClass('datepicker-dropdown');
22449 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22450 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22451 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22452 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22453 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22454 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22458 fireKey: function(e){
22459 if (!this.picker().isVisible()){
22460 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22466 e.preventDefault();
22474 this.onTogglePeriod();
22477 this.onIncrementMinutes();
22480 this.onDecrementMinutes();
22489 onClick: function(e) {
22490 e.stopPropagation();
22491 e.preventDefault();
22494 picker : function()
22496 return this.pickerEl;
22499 fillTime: function()
22501 var time = this.pop.select('tbody', true).first();
22503 time.dom.innerHTML = '';
22518 cls: 'hours-up fa fas fa-chevron-up'
22538 cls: 'minutes-up fa fas fa-chevron-up'
22559 cls: 'timepicker-hour',
22574 cls: 'timepicker-minute',
22589 cls: 'btn btn-primary period',
22611 cls: 'hours-down fa fas fa-chevron-down'
22631 cls: 'minutes-down fa fas fa-chevron-down'
22649 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22656 var hours = this.time.getHours();
22657 var minutes = this.time.getMinutes();
22670 hours = hours - 12;
22674 hours = '0' + hours;
22678 minutes = '0' + minutes;
22681 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22682 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22683 this.pop.select('button', true).first().dom.innerHTML = period;
22689 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22691 var cls = ['bottom'];
22693 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22700 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22704 //this.picker().setXY(20000,20000);
22705 this.picker().addClass(cls.join('-'));
22709 Roo.each(cls, function(c){
22714 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22715 //_this.picker().setTop(_this.inputEl().getHeight());
22719 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22721 //_this.picker().setTop(0 - _this.picker().getHeight());
22726 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22730 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22738 onFocus : function()
22740 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22744 onBlur : function()
22746 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22752 this.picker().show();
22757 this.fireEvent('show', this, this.date);
22762 this.picker().hide();
22765 this.fireEvent('hide', this, this.date);
22768 setTime : function()
22771 this.setValue(this.time.format(this.format));
22773 this.fireEvent('select', this, this.date);
22778 onMousedown: function(e){
22779 e.stopPropagation();
22780 e.preventDefault();
22783 onIncrementHours: function()
22785 Roo.log('onIncrementHours');
22786 this.time = this.time.add(Date.HOUR, 1);
22791 onDecrementHours: function()
22793 Roo.log('onDecrementHours');
22794 this.time = this.time.add(Date.HOUR, -1);
22798 onIncrementMinutes: function()
22800 Roo.log('onIncrementMinutes');
22801 this.time = this.time.add(Date.MINUTE, 1);
22805 onDecrementMinutes: function()
22807 Roo.log('onDecrementMinutes');
22808 this.time = this.time.add(Date.MINUTE, -1);
22812 onTogglePeriod: function()
22814 Roo.log('onTogglePeriod');
22815 this.time = this.time.add(Date.HOUR, 12);
22823 Roo.apply(Roo.bootstrap.TimeField, {
22827 cls: 'datepicker dropdown-menu',
22831 cls: 'datepicker-time',
22835 cls: 'table-condensed',
22864 cls: 'btn btn-info ok',
22892 * @class Roo.bootstrap.MonthField
22893 * @extends Roo.bootstrap.Input
22894 * Bootstrap MonthField class
22896 * @cfg {String} language default en
22899 * Create a new MonthField
22900 * @param {Object} config The config object
22903 Roo.bootstrap.MonthField = function(config){
22904 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22909 * Fires when this field show.
22910 * @param {Roo.bootstrap.MonthField} this
22911 * @param {Mixed} date The date value
22916 * Fires when this field hide.
22917 * @param {Roo.bootstrap.MonthField} this
22918 * @param {Mixed} date The date value
22923 * Fires when select a date.
22924 * @param {Roo.bootstrap.MonthField} this
22925 * @param {String} oldvalue The old value
22926 * @param {String} newvalue The new value
22932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22934 onRender: function(ct, position)
22937 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22939 this.language = this.language || 'en';
22940 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22941 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22943 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22944 this.isInline = false;
22945 this.isInput = true;
22946 this.component = this.el.select('.add-on', true).first() || false;
22947 this.component = (this.component && this.component.length === 0) ? false : this.component;
22948 this.hasInput = this.component && this.inputEL().length;
22950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22952 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22954 this.picker().on('mousedown', this.onMousedown, this);
22955 this.picker().on('click', this.onClick, this);
22957 this.picker().addClass('datepicker-dropdown');
22959 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22960 v.setStyle('width', '189px');
22967 if(this.isInline) {
22973 setValue: function(v, suppressEvent)
22975 var o = this.getValue();
22977 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22981 if(suppressEvent !== true){
22982 this.fireEvent('select', this, o, v);
22987 getValue: function()
22992 onClick: function(e)
22994 e.stopPropagation();
22995 e.preventDefault();
22997 var target = e.getTarget();
22999 if(target.nodeName.toLowerCase() === 'i'){
23000 target = Roo.get(target).dom.parentNode;
23003 var nodeName = target.nodeName;
23004 var className = target.className;
23005 var html = target.innerHTML;
23007 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23011 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23013 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23019 picker : function()
23021 return this.pickerEl;
23024 fillMonths: function()
23027 var months = this.picker().select('>.datepicker-months td', true).first();
23029 months.dom.innerHTML = '';
23035 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23038 months.createChild(month);
23047 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23048 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23051 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23052 e.removeClass('active');
23054 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23055 e.addClass('active');
23062 if(this.isInline) {
23066 this.picker().removeClass(['bottom', 'top']);
23068 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23070 * place to the top of element!
23074 this.picker().addClass('top');
23075 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23080 this.picker().addClass('bottom');
23082 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23085 onFocus : function()
23087 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23091 onBlur : function()
23093 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23095 var d = this.inputEl().getValue();
23104 this.picker().show();
23105 this.picker().select('>.datepicker-months', true).first().show();
23109 this.fireEvent('show', this, this.date);
23114 if(this.isInline) {
23117 this.picker().hide();
23118 this.fireEvent('hide', this, this.date);
23122 onMousedown: function(e)
23124 e.stopPropagation();
23125 e.preventDefault();
23130 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23134 fireKey: function(e)
23136 if (!this.picker().isVisible()){
23137 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23148 e.preventDefault();
23152 dir = e.keyCode == 37 ? -1 : 1;
23154 this.vIndex = this.vIndex + dir;
23156 if(this.vIndex < 0){
23160 if(this.vIndex > 11){
23164 if(isNaN(this.vIndex)){
23168 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23174 dir = e.keyCode == 38 ? -1 : 1;
23176 this.vIndex = this.vIndex + dir * 4;
23178 if(this.vIndex < 0){
23182 if(this.vIndex > 11){
23186 if(isNaN(this.vIndex)){
23190 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23195 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23196 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23200 e.preventDefault();
23203 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23204 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23220 this.picker().remove();
23225 Roo.apply(Roo.bootstrap.MonthField, {
23244 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23245 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23250 Roo.apply(Roo.bootstrap.MonthField, {
23254 cls: 'datepicker dropdown-menu roo-dynamic',
23258 cls: 'datepicker-months',
23262 cls: 'table-condensed',
23264 Roo.bootstrap.DateField.content
23284 * @class Roo.bootstrap.CheckBox
23285 * @extends Roo.bootstrap.Input
23286 * Bootstrap CheckBox class
23288 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23289 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23290 * @cfg {String} boxLabel The text that appears beside the checkbox
23291 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23292 * @cfg {Boolean} checked initnal the element
23293 * @cfg {Boolean} inline inline the element (default false)
23294 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23295 * @cfg {String} tooltip label tooltip
23298 * Create a new CheckBox
23299 * @param {Object} config The config object
23302 Roo.bootstrap.CheckBox = function(config){
23303 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23308 * Fires when the element is checked or unchecked.
23309 * @param {Roo.bootstrap.CheckBox} this This input
23310 * @param {Boolean} checked The new checked value
23315 * Fires when the element is click.
23316 * @param {Roo.bootstrap.CheckBox} this This input
23323 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23325 inputType: 'checkbox',
23334 // checkbox success does not make any sense really..
23339 getAutoCreate : function()
23341 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23347 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23350 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23356 type : this.inputType,
23357 value : this.inputValue,
23358 cls : 'roo-' + this.inputType, //'form-box',
23359 placeholder : this.placeholder || ''
23363 if(this.inputType != 'radio'){
23367 cls : 'roo-hidden-value',
23368 value : this.checked ? this.inputValue : this.valueOff
23373 if (this.weight) { // Validity check?
23374 cfg.cls += " " + this.inputType + "-" + this.weight;
23377 if (this.disabled) {
23378 input.disabled=true;
23382 input.checked = this.checked;
23387 input.name = this.name;
23389 if(this.inputType != 'radio'){
23390 hidden.name = this.name;
23391 input.name = '_hidden_' + this.name;
23396 input.cls += ' input-' + this.size;
23401 ['xs','sm','md','lg'].map(function(size){
23402 if (settings[size]) {
23403 cfg.cls += ' col-' + size + '-' + settings[size];
23407 var inputblock = input;
23409 if (this.before || this.after) {
23412 cls : 'input-group',
23417 inputblock.cn.push({
23419 cls : 'input-group-addon',
23424 inputblock.cn.push(input);
23426 if(this.inputType != 'radio'){
23427 inputblock.cn.push(hidden);
23431 inputblock.cn.push({
23433 cls : 'input-group-addon',
23439 var boxLabelCfg = false;
23445 //'for': id, // box label is handled by onclick - so no for...
23447 html: this.boxLabel
23450 boxLabelCfg.tooltip = this.tooltip;
23456 if (align ==='left' && this.fieldLabel.length) {
23457 // Roo.log("left and has label");
23462 cls : 'control-label',
23463 html : this.fieldLabel
23474 cfg.cn[1].cn.push(boxLabelCfg);
23477 if(this.labelWidth > 12){
23478 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23481 if(this.labelWidth < 13 && this.labelmd == 0){
23482 this.labelmd = this.labelWidth;
23485 if(this.labellg > 0){
23486 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23487 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23490 if(this.labelmd > 0){
23491 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23492 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23495 if(this.labelsm > 0){
23496 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23497 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23500 if(this.labelxs > 0){
23501 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23502 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23505 } else if ( this.fieldLabel.length) {
23506 // Roo.log(" label");
23510 tag: this.boxLabel ? 'span' : 'label',
23512 cls: 'control-label box-input-label',
23513 //cls : 'input-group-addon',
23514 html : this.fieldLabel
23521 cfg.cn.push(boxLabelCfg);
23526 // Roo.log(" no label && no align");
23527 cfg.cn = [ inputblock ] ;
23529 cfg.cn.push(boxLabelCfg);
23537 if(this.inputType != 'radio'){
23538 cfg.cn.push(hidden);
23546 * return the real input element.
23548 inputEl: function ()
23550 return this.el.select('input.roo-' + this.inputType,true).first();
23552 hiddenEl: function ()
23554 return this.el.select('input.roo-hidden-value',true).first();
23557 labelEl: function()
23559 return this.el.select('label.control-label',true).first();
23561 /* depricated... */
23565 return this.labelEl();
23568 boxLabelEl: function()
23570 return this.el.select('label.box-label',true).first();
23573 initEvents : function()
23575 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23577 this.inputEl().on('click', this.onClick, this);
23579 if (this.boxLabel) {
23580 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23583 this.startValue = this.getValue();
23586 Roo.bootstrap.CheckBox.register(this);
23590 onClick : function(e)
23592 if(this.fireEvent('click', this, e) !== false){
23593 this.setChecked(!this.checked);
23598 setChecked : function(state,suppressEvent)
23600 this.startValue = this.getValue();
23602 if(this.inputType == 'radio'){
23604 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23605 e.dom.checked = false;
23608 this.inputEl().dom.checked = true;
23610 this.inputEl().dom.value = this.inputValue;
23612 if(suppressEvent !== true){
23613 this.fireEvent('check', this, true);
23621 this.checked = state;
23623 this.inputEl().dom.checked = state;
23626 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23628 if(suppressEvent !== true){
23629 this.fireEvent('check', this, state);
23635 getValue : function()
23637 if(this.inputType == 'radio'){
23638 return this.getGroupValue();
23641 return this.hiddenEl().dom.value;
23645 getGroupValue : function()
23647 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23651 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23654 setValue : function(v,suppressEvent)
23656 if(this.inputType == 'radio'){
23657 this.setGroupValue(v, suppressEvent);
23661 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23666 setGroupValue : function(v, suppressEvent)
23668 this.startValue = this.getValue();
23670 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671 e.dom.checked = false;
23673 if(e.dom.value == v){
23674 e.dom.checked = true;
23678 if(suppressEvent !== true){
23679 this.fireEvent('check', this, true);
23687 validate : function()
23689 if(this.getVisibilityEl().hasClass('hidden')){
23695 (this.inputType == 'radio' && this.validateRadio()) ||
23696 (this.inputType == 'checkbox' && this.validateCheckbox())
23702 this.markInvalid();
23706 validateRadio : function()
23708 if(this.getVisibilityEl().hasClass('hidden')){
23712 if(this.allowBlank){
23718 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23719 if(!e.dom.checked){
23731 validateCheckbox : function()
23734 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23735 //return (this.getValue() == this.inputValue) ? true : false;
23738 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23746 for(var i in group){
23747 if(group[i].el.isVisible(true)){
23755 for(var i in group){
23760 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23767 * Mark this field as valid
23769 markValid : function()
23773 this.fireEvent('valid', this);
23775 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23778 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23785 if(this.inputType == 'radio'){
23786 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23787 var fg = e.findParent('.form-group', false, true);
23788 if (Roo.bootstrap.version == 3) {
23789 fg.removeClass([_this.invalidClass, _this.validClass]);
23790 fg.addClass(_this.validClass);
23792 fg.removeClass(['is-valid', 'is-invalid']);
23793 fg.addClass('is-valid');
23801 var fg = this.el.findParent('.form-group', false, true);
23802 if (Roo.bootstrap.version == 3) {
23803 fg.removeClass([this.invalidClass, this.validClass]);
23804 fg.addClass(this.validClass);
23806 fg.removeClass(['is-valid', 'is-invalid']);
23807 fg.addClass('is-valid');
23812 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23818 for(var i in group){
23819 var fg = group[i].el.findParent('.form-group', false, true);
23820 if (Roo.bootstrap.version == 3) {
23821 fg.removeClass([this.invalidClass, this.validClass]);
23822 fg.addClass(this.validClass);
23824 fg.removeClass(['is-valid', 'is-invalid']);
23825 fg.addClass('is-valid');
23831 * Mark this field as invalid
23832 * @param {String} msg The validation message
23834 markInvalid : function(msg)
23836 if(this.allowBlank){
23842 this.fireEvent('invalid', this, msg);
23844 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23847 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23851 label.markInvalid();
23854 if(this.inputType == 'radio'){
23856 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23857 var fg = e.findParent('.form-group', false, true);
23858 if (Roo.bootstrap.version == 3) {
23859 fg.removeClass([_this.invalidClass, _this.validClass]);
23860 fg.addClass(_this.invalidClass);
23862 fg.removeClass(['is-invalid', 'is-valid']);
23863 fg.addClass('is-invalid');
23871 var fg = this.el.findParent('.form-group', false, true);
23872 if (Roo.bootstrap.version == 3) {
23873 fg.removeClass([_this.invalidClass, _this.validClass]);
23874 fg.addClass(_this.invalidClass);
23876 fg.removeClass(['is-invalid', 'is-valid']);
23877 fg.addClass('is-invalid');
23882 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23888 for(var i in group){
23889 var fg = group[i].el.findParent('.form-group', false, true);
23890 if (Roo.bootstrap.version == 3) {
23891 fg.removeClass([_this.invalidClass, _this.validClass]);
23892 fg.addClass(_this.invalidClass);
23894 fg.removeClass(['is-invalid', 'is-valid']);
23895 fg.addClass('is-invalid');
23901 clearInvalid : function()
23903 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23905 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23907 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23909 if (label && label.iconEl) {
23910 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23911 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23915 disable : function()
23917 if(this.inputType != 'radio'){
23918 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23925 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23926 _this.getActionEl().addClass(this.disabledClass);
23927 e.dom.disabled = true;
23931 this.disabled = true;
23932 this.fireEvent("disable", this);
23936 enable : function()
23938 if(this.inputType != 'radio'){
23939 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23946 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23947 _this.getActionEl().removeClass(this.disabledClass);
23948 e.dom.disabled = false;
23952 this.disabled = false;
23953 this.fireEvent("enable", this);
23957 setBoxLabel : function(v)
23962 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23968 Roo.apply(Roo.bootstrap.CheckBox, {
23973 * register a CheckBox Group
23974 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23976 register : function(checkbox)
23978 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23979 this.groups[checkbox.groupId] = {};
23982 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23986 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23990 * fetch a CheckBox Group based on the group ID
23991 * @param {string} the group ID
23992 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23994 get: function(groupId) {
23995 if (typeof(this.groups[groupId]) == 'undefined') {
23999 return this.groups[groupId] ;
24012 * @class Roo.bootstrap.Radio
24013 * @extends Roo.bootstrap.Component
24014 * Bootstrap Radio class
24015 * @cfg {String} boxLabel - the label associated
24016 * @cfg {String} value - the value of radio
24019 * Create a new Radio
24020 * @param {Object} config The config object
24022 Roo.bootstrap.Radio = function(config){
24023 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24027 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24033 getAutoCreate : function()
24037 cls : 'form-group radio',
24042 html : this.boxLabel
24050 initEvents : function()
24052 this.parent().register(this);
24054 this.el.on('click', this.onClick, this);
24058 onClick : function(e)
24060 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24061 this.setChecked(true);
24065 setChecked : function(state, suppressEvent)
24067 this.parent().setValue(this.value, suppressEvent);
24071 setBoxLabel : function(v)
24076 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24091 * @class Roo.bootstrap.SecurePass
24092 * @extends Roo.bootstrap.Input
24093 * Bootstrap SecurePass class
24097 * Create a new SecurePass
24098 * @param {Object} config The config object
24101 Roo.bootstrap.SecurePass = function (config) {
24102 // these go here, so the translation tool can replace them..
24104 PwdEmpty: "Please type a password, and then retype it to confirm.",
24105 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24106 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24107 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24108 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24109 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24110 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24111 TooWeak: "Your password is Too Weak."
24113 this.meterLabel = "Password strength:";
24114 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24115 this.meterClass = [
24116 "roo-password-meter-tooweak",
24117 "roo-password-meter-weak",
24118 "roo-password-meter-medium",
24119 "roo-password-meter-strong",
24120 "roo-password-meter-grey"
24125 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24128 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24130 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24132 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24133 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24134 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24135 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24136 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24137 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24138 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24148 * @cfg {String/Object} Label for the strength meter (defaults to
24149 * 'Password strength:')
24154 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24155 * ['Weak', 'Medium', 'Strong'])
24158 pwdStrengths: false,
24171 initEvents: function ()
24173 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24175 if (this.el.is('input[type=password]') && Roo.isSafari) {
24176 this.el.on('keydown', this.SafariOnKeyDown, this);
24179 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24182 onRender: function (ct, position)
24184 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24185 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24186 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24188 this.trigger.createChild({
24193 cls: 'roo-password-meter-grey col-xs-12',
24196 //width: this.meterWidth + 'px'
24200 cls: 'roo-password-meter-text'
24206 if (this.hideTrigger) {
24207 this.trigger.setDisplayed(false);
24209 this.setSize(this.width || '', this.height || '');
24212 onDestroy: function ()
24214 if (this.trigger) {
24215 this.trigger.removeAllListeners();
24216 this.trigger.remove();
24219 this.wrap.remove();
24221 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24224 checkStrength: function ()
24226 var pwd = this.inputEl().getValue();
24227 if (pwd == this._lastPwd) {
24232 if (this.ClientSideStrongPassword(pwd)) {
24234 } else if (this.ClientSideMediumPassword(pwd)) {
24236 } else if (this.ClientSideWeakPassword(pwd)) {
24242 Roo.log('strength1: ' + strength);
24244 //var pm = this.trigger.child('div/div/div').dom;
24245 var pm = this.trigger.child('div/div');
24246 pm.removeClass(this.meterClass);
24247 pm.addClass(this.meterClass[strength]);
24250 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24252 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24254 this._lastPwd = pwd;
24258 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24260 this._lastPwd = '';
24262 var pm = this.trigger.child('div/div');
24263 pm.removeClass(this.meterClass);
24264 pm.addClass('roo-password-meter-grey');
24267 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24270 this.inputEl().dom.type='password';
24273 validateValue: function (value)
24275 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24278 if (value.length == 0) {
24279 if (this.allowBlank) {
24280 this.clearInvalid();
24284 this.markInvalid(this.errors.PwdEmpty);
24285 this.errorMsg = this.errors.PwdEmpty;
24293 if (!value.match(/[\x21-\x7e]+/)) {
24294 this.markInvalid(this.errors.PwdBadChar);
24295 this.errorMsg = this.errors.PwdBadChar;
24298 if (value.length < 6) {
24299 this.markInvalid(this.errors.PwdShort);
24300 this.errorMsg = this.errors.PwdShort;
24303 if (value.length > 16) {
24304 this.markInvalid(this.errors.PwdLong);
24305 this.errorMsg = this.errors.PwdLong;
24309 if (this.ClientSideStrongPassword(value)) {
24311 } else if (this.ClientSideMediumPassword(value)) {
24313 } else if (this.ClientSideWeakPassword(value)) {
24320 if (strength < 2) {
24321 //this.markInvalid(this.errors.TooWeak);
24322 this.errorMsg = this.errors.TooWeak;
24327 console.log('strength2: ' + strength);
24329 //var pm = this.trigger.child('div/div/div').dom;
24331 var pm = this.trigger.child('div/div');
24332 pm.removeClass(this.meterClass);
24333 pm.addClass(this.meterClass[strength]);
24335 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24337 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24339 this.errorMsg = '';
24343 CharacterSetChecks: function (type)
24346 this.fResult = false;
24349 isctype: function (character, type)
24352 case this.kCapitalLetter:
24353 if (character >= 'A' && character <= 'Z') {
24358 case this.kSmallLetter:
24359 if (character >= 'a' && character <= 'z') {
24365 if (character >= '0' && character <= '9') {
24370 case this.kPunctuation:
24371 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24382 IsLongEnough: function (pwd, size)
24384 return !(pwd == null || isNaN(size) || pwd.length < size);
24387 SpansEnoughCharacterSets: function (word, nb)
24389 if (!this.IsLongEnough(word, nb))
24394 var characterSetChecks = new Array(
24395 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24396 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24399 for (var index = 0; index < word.length; ++index) {
24400 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24401 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24402 characterSetChecks[nCharSet].fResult = true;
24409 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24410 if (characterSetChecks[nCharSet].fResult) {
24415 if (nCharSets < nb) {
24421 ClientSideStrongPassword: function (pwd)
24423 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24426 ClientSideMediumPassword: function (pwd)
24428 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24431 ClientSideWeakPassword: function (pwd)
24433 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24436 })//<script type="text/javascript">
24439 * Based Ext JS Library 1.1.1
24440 * Copyright(c) 2006-2007, Ext JS, LLC.
24446 * @class Roo.HtmlEditorCore
24447 * @extends Roo.Component
24448 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24450 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24453 Roo.HtmlEditorCore = function(config){
24456 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24461 * @event initialize
24462 * Fires when the editor is fully initialized (including the iframe)
24463 * @param {Roo.HtmlEditorCore} this
24468 * Fires when the editor is first receives the focus. Any insertion must wait
24469 * until after this event.
24470 * @param {Roo.HtmlEditorCore} this
24474 * @event beforesync
24475 * Fires before the textarea is updated with content from the editor iframe. Return false
24476 * to cancel the sync.
24477 * @param {Roo.HtmlEditorCore} this
24478 * @param {String} html
24482 * @event beforepush
24483 * Fires before the iframe editor is updated with content from the textarea. Return false
24484 * to cancel the push.
24485 * @param {Roo.HtmlEditorCore} this
24486 * @param {String} html
24491 * Fires when the textarea is updated with content from the editor iframe.
24492 * @param {Roo.HtmlEditorCore} this
24493 * @param {String} html
24498 * Fires when the iframe editor is updated with content from the textarea.
24499 * @param {Roo.HtmlEditorCore} this
24500 * @param {String} html
24505 * @event editorevent
24506 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24507 * @param {Roo.HtmlEditorCore} this
24513 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24515 // defaults : white / black...
24516 this.applyBlacklists();
24523 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24527 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24533 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24538 * @cfg {Number} height (in pixels)
24542 * @cfg {Number} width (in pixels)
24547 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24550 stylesheets: false,
24555 // private properties
24556 validationEvent : false,
24558 initialized : false,
24560 sourceEditMode : false,
24561 onFocus : Roo.emptyFn,
24563 hideMode:'offsets',
24567 // blacklist + whitelisted elements..
24574 * Protected method that will not generally be called directly. It
24575 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24576 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24578 getDocMarkup : function(){
24582 // inherit styels from page...??
24583 if (this.stylesheets === false) {
24585 Roo.get(document.head).select('style').each(function(node) {
24586 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24589 Roo.get(document.head).select('link').each(function(node) {
24590 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24593 } else if (!this.stylesheets.length) {
24595 st = '<style type="text/css">' +
24596 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24599 for (var i in this.stylesheets) {
24600 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24605 st += '<style type="text/css">' +
24606 'IMG { cursor: pointer } ' +
24609 var cls = 'roo-htmleditor-body';
24611 if(this.bodyCls.length){
24612 cls += ' ' + this.bodyCls;
24615 return '<html><head>' + st +
24616 //<style type="text/css">' +
24617 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24619 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24623 onRender : function(ct, position)
24626 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24627 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24630 this.el.dom.style.border = '0 none';
24631 this.el.dom.setAttribute('tabIndex', -1);
24632 this.el.addClass('x-hidden hide');
24636 if(Roo.isIE){ // fix IE 1px bogus margin
24637 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24641 this.frameId = Roo.id();
24645 var iframe = this.owner.wrap.createChild({
24647 cls: 'form-control', // bootstrap..
24649 name: this.frameId,
24650 frameBorder : 'no',
24651 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24656 this.iframe = iframe.dom;
24658 this.assignDocWin();
24660 this.doc.designMode = 'on';
24663 this.doc.write(this.getDocMarkup());
24667 var task = { // must defer to wait for browser to be ready
24669 //console.log("run task?" + this.doc.readyState);
24670 this.assignDocWin();
24671 if(this.doc.body || this.doc.readyState == 'complete'){
24673 this.doc.designMode="on";
24677 Roo.TaskMgr.stop(task);
24678 this.initEditor.defer(10, this);
24685 Roo.TaskMgr.start(task);
24690 onResize : function(w, h)
24692 Roo.log('resize: ' +w + ',' + h );
24693 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24697 if(typeof w == 'number'){
24699 this.iframe.style.width = w + 'px';
24701 if(typeof h == 'number'){
24703 this.iframe.style.height = h + 'px';
24705 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24712 * Toggles the editor between standard and source edit mode.
24713 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24715 toggleSourceEdit : function(sourceEditMode){
24717 this.sourceEditMode = sourceEditMode === true;
24719 if(this.sourceEditMode){
24721 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24724 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24725 //this.iframe.className = '';
24728 //this.setSize(this.owner.wrap.getSize());
24729 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24736 * Protected method that will not generally be called directly. If you need/want
24737 * custom HTML cleanup, this is the method you should override.
24738 * @param {String} html The HTML to be cleaned
24739 * return {String} The cleaned HTML
24741 cleanHtml : function(html){
24742 html = String(html);
24743 if(html.length > 5){
24744 if(Roo.isSafari){ // strip safari nonsense
24745 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24748 if(html == ' '){
24755 * HTML Editor -> Textarea
24756 * Protected method that will not generally be called directly. Syncs the contents
24757 * of the editor iframe with the textarea.
24759 syncValue : function(){
24760 if(this.initialized){
24761 var bd = (this.doc.body || this.doc.documentElement);
24762 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24763 var html = bd.innerHTML;
24765 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24766 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24768 html = '<div style="'+m[0]+'">' + html + '</div>';
24771 html = this.cleanHtml(html);
24772 // fix up the special chars.. normaly like back quotes in word...
24773 // however we do not want to do this with chinese..
24774 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24776 var cc = match.charCodeAt();
24778 // Get the character value, handling surrogate pairs
24779 if (match.length == 2) {
24780 // It's a surrogate pair, calculate the Unicode code point
24781 var high = match.charCodeAt(0) - 0xD800;
24782 var low = match.charCodeAt(1) - 0xDC00;
24783 cc = (high * 0x400) + low + 0x10000;
24785 (cc >= 0x4E00 && cc < 0xA000 ) ||
24786 (cc >= 0x3400 && cc < 0x4E00 ) ||
24787 (cc >= 0xf900 && cc < 0xfb00 )
24792 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24793 return "&#" + cc + ";";
24800 if(this.owner.fireEvent('beforesync', this, html) !== false){
24801 this.el.dom.value = html;
24802 this.owner.fireEvent('sync', this, html);
24808 * Protected method that will not generally be called directly. Pushes the value of the textarea
24809 * into the iframe editor.
24811 pushValue : function(){
24812 if(this.initialized){
24813 var v = this.el.dom.value.trim();
24815 // if(v.length < 1){
24819 if(this.owner.fireEvent('beforepush', this, v) !== false){
24820 var d = (this.doc.body || this.doc.documentElement);
24822 this.cleanUpPaste();
24823 this.el.dom.value = d.innerHTML;
24824 this.owner.fireEvent('push', this, v);
24830 deferFocus : function(){
24831 this.focus.defer(10, this);
24835 focus : function(){
24836 if(this.win && !this.sourceEditMode){
24843 assignDocWin: function()
24845 var iframe = this.iframe;
24848 this.doc = iframe.contentWindow.document;
24849 this.win = iframe.contentWindow;
24851 // if (!Roo.get(this.frameId)) {
24854 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24855 // this.win = Roo.get(this.frameId).dom.contentWindow;
24857 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24861 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24862 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24867 initEditor : function(){
24868 //console.log("INIT EDITOR");
24869 this.assignDocWin();
24873 this.doc.designMode="on";
24875 this.doc.write(this.getDocMarkup());
24878 var dbody = (this.doc.body || this.doc.documentElement);
24879 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24880 // this copies styles from the containing element into thsi one..
24881 // not sure why we need all of this..
24882 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24884 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24885 //ss['background-attachment'] = 'fixed'; // w3c
24886 dbody.bgProperties = 'fixed'; // ie
24887 //Roo.DomHelper.applyStyles(dbody, ss);
24888 Roo.EventManager.on(this.doc, {
24889 //'mousedown': this.onEditorEvent,
24890 'mouseup': this.onEditorEvent,
24891 'dblclick': this.onEditorEvent,
24892 'click': this.onEditorEvent,
24893 'keyup': this.onEditorEvent,
24898 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24900 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24901 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24903 this.initialized = true;
24905 this.owner.fireEvent('initialize', this);
24910 onDestroy : function(){
24916 //for (var i =0; i < this.toolbars.length;i++) {
24917 // // fixme - ask toolbars for heights?
24918 // this.toolbars[i].onDestroy();
24921 //this.wrap.dom.innerHTML = '';
24922 //this.wrap.remove();
24927 onFirstFocus : function(){
24929 this.assignDocWin();
24932 this.activated = true;
24935 if(Roo.isGecko){ // prevent silly gecko errors
24937 var s = this.win.getSelection();
24938 if(!s.focusNode || s.focusNode.nodeType != 3){
24939 var r = s.getRangeAt(0);
24940 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24945 this.execCmd('useCSS', true);
24946 this.execCmd('styleWithCSS', false);
24949 this.owner.fireEvent('activate', this);
24953 adjustFont: function(btn){
24954 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24955 //if(Roo.isSafari){ // safari
24958 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24959 if(Roo.isSafari){ // safari
24960 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24961 v = (v < 10) ? 10 : v;
24962 v = (v > 48) ? 48 : v;
24963 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24968 v = Math.max(1, v+adjust);
24970 this.execCmd('FontSize', v );
24973 onEditorEvent : function(e)
24975 this.owner.fireEvent('editorevent', this, e);
24976 // this.updateToolbar();
24977 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24980 insertTag : function(tg)
24982 // could be a bit smarter... -> wrap the current selected tRoo..
24983 if (tg.toLowerCase() == 'span' ||
24984 tg.toLowerCase() == 'code' ||
24985 tg.toLowerCase() == 'sup' ||
24986 tg.toLowerCase() == 'sub'
24989 range = this.createRange(this.getSelection());
24990 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24991 wrappingNode.appendChild(range.extractContents());
24992 range.insertNode(wrappingNode);
24999 this.execCmd("formatblock", tg);
25003 insertText : function(txt)
25007 var range = this.createRange();
25008 range.deleteContents();
25009 //alert(Sender.getAttribute('label'));
25011 range.insertNode(this.doc.createTextNode(txt));
25017 * Executes a Midas editor command on the editor document and performs necessary focus and
25018 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25019 * @param {String} cmd The Midas command
25020 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25022 relayCmd : function(cmd, value){
25024 this.execCmd(cmd, value);
25025 this.owner.fireEvent('editorevent', this);
25026 //this.updateToolbar();
25027 this.owner.deferFocus();
25031 * Executes a Midas editor command directly on the editor document.
25032 * For visual commands, you should use {@link #relayCmd} instead.
25033 * <b>This should only be called after the editor is initialized.</b>
25034 * @param {String} cmd The Midas command
25035 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25037 execCmd : function(cmd, value){
25038 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25045 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25047 * @param {String} text | dom node..
25049 insertAtCursor : function(text)
25052 if(!this.activated){
25058 var r = this.doc.selection.createRange();
25069 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25073 // from jquery ui (MIT licenced)
25075 var win = this.win;
25077 if (win.getSelection && win.getSelection().getRangeAt) {
25078 range = win.getSelection().getRangeAt(0);
25079 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25080 range.insertNode(node);
25081 } else if (win.document.selection && win.document.selection.createRange) {
25082 // no firefox support
25083 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25084 win.document.selection.createRange().pasteHTML(txt);
25086 // no firefox support
25087 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25088 this.execCmd('InsertHTML', txt);
25097 mozKeyPress : function(e){
25099 var c = e.getCharCode(), cmd;
25102 c = String.fromCharCode(c).toLowerCase();
25116 this.cleanUpPaste.defer(100, this);
25124 e.preventDefault();
25132 fixKeys : function(){ // load time branching for fastest keydown performance
25134 return function(e){
25135 var k = e.getKey(), r;
25138 r = this.doc.selection.createRange();
25141 r.pasteHTML('    ');
25148 r = this.doc.selection.createRange();
25150 var target = r.parentElement();
25151 if(!target || target.tagName.toLowerCase() != 'li'){
25153 r.pasteHTML('<br />');
25159 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25160 this.cleanUpPaste.defer(100, this);
25166 }else if(Roo.isOpera){
25167 return function(e){
25168 var k = e.getKey();
25172 this.execCmd('InsertHTML','    ');
25175 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25176 this.cleanUpPaste.defer(100, this);
25181 }else if(Roo.isSafari){
25182 return function(e){
25183 var k = e.getKey();
25187 this.execCmd('InsertText','\t');
25191 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25192 this.cleanUpPaste.defer(100, this);
25200 getAllAncestors: function()
25202 var p = this.getSelectedNode();
25205 a.push(p); // push blank onto stack..
25206 p = this.getParentElement();
25210 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25214 a.push(this.doc.body);
25218 lastSelNode : false,
25221 getSelection : function()
25223 this.assignDocWin();
25224 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25227 getSelectedNode: function()
25229 // this may only work on Gecko!!!
25231 // should we cache this!!!!
25236 var range = this.createRange(this.getSelection()).cloneRange();
25239 var parent = range.parentElement();
25241 var testRange = range.duplicate();
25242 testRange.moveToElementText(parent);
25243 if (testRange.inRange(range)) {
25246 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25249 parent = parent.parentElement;
25254 // is ancestor a text element.
25255 var ac = range.commonAncestorContainer;
25256 if (ac.nodeType == 3) {
25257 ac = ac.parentNode;
25260 var ar = ac.childNodes;
25263 var other_nodes = [];
25264 var has_other_nodes = false;
25265 for (var i=0;i<ar.length;i++) {
25266 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25269 // fullly contained node.
25271 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25276 // probably selected..
25277 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25278 other_nodes.push(ar[i]);
25282 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25287 has_other_nodes = true;
25289 if (!nodes.length && other_nodes.length) {
25290 nodes= other_nodes;
25292 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25298 createRange: function(sel)
25300 // this has strange effects when using with
25301 // top toolbar - not sure if it's a great idea.
25302 //this.editor.contentWindow.focus();
25303 if (typeof sel != "undefined") {
25305 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25307 return this.doc.createRange();
25310 return this.doc.createRange();
25313 getParentElement: function()
25316 this.assignDocWin();
25317 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25319 var range = this.createRange(sel);
25322 var p = range.commonAncestorContainer;
25323 while (p.nodeType == 3) { // text node
25334 * Range intersection.. the hard stuff...
25338 * [ -- selected range --- ]
25342 * if end is before start or hits it. fail.
25343 * if start is after end or hits it fail.
25345 * if either hits (but other is outside. - then it's not
25351 // @see http://www.thismuchiknow.co.uk/?p=64.
25352 rangeIntersectsNode : function(range, node)
25354 var nodeRange = node.ownerDocument.createRange();
25356 nodeRange.selectNode(node);
25358 nodeRange.selectNodeContents(node);
25361 var rangeStartRange = range.cloneRange();
25362 rangeStartRange.collapse(true);
25364 var rangeEndRange = range.cloneRange();
25365 rangeEndRange.collapse(false);
25367 var nodeStartRange = nodeRange.cloneRange();
25368 nodeStartRange.collapse(true);
25370 var nodeEndRange = nodeRange.cloneRange();
25371 nodeEndRange.collapse(false);
25373 return rangeStartRange.compareBoundaryPoints(
25374 Range.START_TO_START, nodeEndRange) == -1 &&
25375 rangeEndRange.compareBoundaryPoints(
25376 Range.START_TO_START, nodeStartRange) == 1;
25380 rangeCompareNode : function(range, node)
25382 var nodeRange = node.ownerDocument.createRange();
25384 nodeRange.selectNode(node);
25386 nodeRange.selectNodeContents(node);
25390 range.collapse(true);
25392 nodeRange.collapse(true);
25394 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25395 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25397 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25399 var nodeIsBefore = ss == 1;
25400 var nodeIsAfter = ee == -1;
25402 if (nodeIsBefore && nodeIsAfter) {
25405 if (!nodeIsBefore && nodeIsAfter) {
25406 return 1; //right trailed.
25409 if (nodeIsBefore && !nodeIsAfter) {
25410 return 2; // left trailed.
25416 // private? - in a new class?
25417 cleanUpPaste : function()
25419 // cleans up the whole document..
25420 Roo.log('cleanuppaste');
25422 this.cleanUpChildren(this.doc.body);
25423 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25424 if (clean != this.doc.body.innerHTML) {
25425 this.doc.body.innerHTML = clean;
25430 cleanWordChars : function(input) {// change the chars to hex code
25431 var he = Roo.HtmlEditorCore;
25433 var output = input;
25434 Roo.each(he.swapCodes, function(sw) {
25435 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25437 output = output.replace(swapper, sw[1]);
25444 cleanUpChildren : function (n)
25446 if (!n.childNodes.length) {
25449 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25450 this.cleanUpChild(n.childNodes[i]);
25457 cleanUpChild : function (node)
25460 //console.log(node);
25461 if (node.nodeName == "#text") {
25462 // clean up silly Windows -- stuff?
25465 if (node.nodeName == "#comment") {
25466 node.parentNode.removeChild(node);
25467 // clean up silly Windows -- stuff?
25470 var lcname = node.tagName.toLowerCase();
25471 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25472 // whitelist of tags..
25474 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25476 node.parentNode.removeChild(node);
25481 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25483 // spans with no attributes - just remove them..
25484 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25485 remove_keep_children = true;
25488 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25489 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25491 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25492 // remove_keep_children = true;
25495 if (remove_keep_children) {
25496 this.cleanUpChildren(node);
25497 // inserts everything just before this node...
25498 while (node.childNodes.length) {
25499 var cn = node.childNodes[0];
25500 node.removeChild(cn);
25501 node.parentNode.insertBefore(cn, node);
25503 node.parentNode.removeChild(node);
25507 if (!node.attributes || !node.attributes.length) {
25512 this.cleanUpChildren(node);
25516 function cleanAttr(n,v)
25519 if (v.match(/^\./) || v.match(/^\//)) {
25522 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25525 if (v.match(/^#/)) {
25528 if (v.match(/^\{/)) { // allow template editing.
25531 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25532 node.removeAttribute(n);
25536 var cwhite = this.cwhite;
25537 var cblack = this.cblack;
25539 function cleanStyle(n,v)
25541 if (v.match(/expression/)) { //XSS?? should we even bother..
25542 node.removeAttribute(n);
25546 var parts = v.split(/;/);
25549 Roo.each(parts, function(p) {
25550 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25554 var l = p.split(':').shift().replace(/\s+/g,'');
25555 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25557 if ( cwhite.length && cblack.indexOf(l) > -1) {
25558 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25559 //node.removeAttribute(n);
25563 // only allow 'c whitelisted system attributes'
25564 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25565 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25566 //node.removeAttribute(n);
25576 if (clean.length) {
25577 node.setAttribute(n, clean.join(';'));
25579 node.removeAttribute(n);
25585 for (var i = node.attributes.length-1; i > -1 ; i--) {
25586 var a = node.attributes[i];
25589 if (a.name.toLowerCase().substr(0,2)=='on') {
25590 node.removeAttribute(a.name);
25593 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25594 node.removeAttribute(a.name);
25597 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25598 cleanAttr(a.name,a.value); // fixme..
25601 if (a.name == 'style') {
25602 cleanStyle(a.name,a.value);
25605 /// clean up MS crap..
25606 // tecnically this should be a list of valid class'es..
25609 if (a.name == 'class') {
25610 if (a.value.match(/^Mso/)) {
25611 node.removeAttribute('class');
25614 if (a.value.match(/^body$/)) {
25615 node.removeAttribute('class');
25626 this.cleanUpChildren(node);
25632 * Clean up MS wordisms...
25634 cleanWord : function(node)
25637 this.cleanWord(this.doc.body);
25642 node.nodeName == 'SPAN' &&
25643 !node.hasAttributes() &&
25644 node.childNodes.length == 1 &&
25645 node.firstChild.nodeName == "#text"
25647 var textNode = node.firstChild;
25648 node.removeChild(textNode);
25649 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25650 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25652 node.parentNode.insertBefore(textNode, node);
25653 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25654 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25656 node.parentNode.removeChild(node);
25659 if (node.nodeName == "#text") {
25660 // clean up silly Windows -- stuff?
25663 if (node.nodeName == "#comment") {
25664 node.parentNode.removeChild(node);
25665 // clean up silly Windows -- stuff?
25669 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25670 node.parentNode.removeChild(node);
25673 //Roo.log(node.tagName);
25674 // remove - but keep children..
25675 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25676 //Roo.log('-- removed');
25677 while (node.childNodes.length) {
25678 var cn = node.childNodes[0];
25679 node.removeChild(cn);
25680 node.parentNode.insertBefore(cn, node);
25681 // move node to parent - and clean it..
25682 this.cleanWord(cn);
25684 node.parentNode.removeChild(node);
25685 /// no need to iterate chidlren = it's got none..
25686 //this.iterateChildren(node, this.cleanWord);
25690 if (node.className.length) {
25692 var cn = node.className.split(/\W+/);
25694 Roo.each(cn, function(cls) {
25695 if (cls.match(/Mso[a-zA-Z]+/)) {
25700 node.className = cna.length ? cna.join(' ') : '';
25702 node.removeAttribute("class");
25706 if (node.hasAttribute("lang")) {
25707 node.removeAttribute("lang");
25710 if (node.hasAttribute("style")) {
25712 var styles = node.getAttribute("style").split(";");
25714 Roo.each(styles, function(s) {
25715 if (!s.match(/:/)) {
25718 var kv = s.split(":");
25719 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25722 // what ever is left... we allow.
25725 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25726 if (!nstyle.length) {
25727 node.removeAttribute('style');
25730 this.iterateChildren(node, this.cleanWord);
25736 * iterateChildren of a Node, calling fn each time, using this as the scole..
25737 * @param {DomNode} node node to iterate children of.
25738 * @param {Function} fn method of this class to call on each item.
25740 iterateChildren : function(node, fn)
25742 if (!node.childNodes.length) {
25745 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25746 fn.call(this, node.childNodes[i])
25752 * cleanTableWidths.
25754 * Quite often pasting from word etc.. results in tables with column and widths.
25755 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25758 cleanTableWidths : function(node)
25763 this.cleanTableWidths(this.doc.body);
25768 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25771 Roo.log(node.tagName);
25772 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25773 this.iterateChildren(node, this.cleanTableWidths);
25776 if (node.hasAttribute('width')) {
25777 node.removeAttribute('width');
25781 if (node.hasAttribute("style")) {
25784 var styles = node.getAttribute("style").split(";");
25786 Roo.each(styles, function(s) {
25787 if (!s.match(/:/)) {
25790 var kv = s.split(":");
25791 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25794 // what ever is left... we allow.
25797 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25798 if (!nstyle.length) {
25799 node.removeAttribute('style');
25803 this.iterateChildren(node, this.cleanTableWidths);
25811 domToHTML : function(currentElement, depth, nopadtext) {
25813 depth = depth || 0;
25814 nopadtext = nopadtext || false;
25816 if (!currentElement) {
25817 return this.domToHTML(this.doc.body);
25820 //Roo.log(currentElement);
25822 var allText = false;
25823 var nodeName = currentElement.nodeName;
25824 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25826 if (nodeName == '#text') {
25828 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25833 if (nodeName != 'BODY') {
25836 // Prints the node tagName, such as <A>, <IMG>, etc
25839 for(i = 0; i < currentElement.attributes.length;i++) {
25841 var aname = currentElement.attributes.item(i).name;
25842 if (!currentElement.attributes.item(i).value.length) {
25845 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25848 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25857 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25860 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25865 // Traverse the tree
25867 var currentElementChild = currentElement.childNodes.item(i);
25868 var allText = true;
25869 var innerHTML = '';
25871 while (currentElementChild) {
25872 // Formatting code (indent the tree so it looks nice on the screen)
25873 var nopad = nopadtext;
25874 if (lastnode == 'SPAN') {
25878 if (currentElementChild.nodeName == '#text') {
25879 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25880 toadd = nopadtext ? toadd : toadd.trim();
25881 if (!nopad && toadd.length > 80) {
25882 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25884 innerHTML += toadd;
25887 currentElementChild = currentElement.childNodes.item(i);
25893 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25895 // Recursively traverse the tree structure of the child node
25896 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25897 lastnode = currentElementChild.nodeName;
25899 currentElementChild=currentElement.childNodes.item(i);
25905 // The remaining code is mostly for formatting the tree
25906 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25911 ret+= "</"+tagName+">";
25917 applyBlacklists : function()
25919 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25920 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25924 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25925 if (b.indexOf(tag) > -1) {
25928 this.white.push(tag);
25932 Roo.each(w, function(tag) {
25933 if (b.indexOf(tag) > -1) {
25936 if (this.white.indexOf(tag) > -1) {
25939 this.white.push(tag);
25944 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25945 if (w.indexOf(tag) > -1) {
25948 this.black.push(tag);
25952 Roo.each(b, function(tag) {
25953 if (w.indexOf(tag) > -1) {
25956 if (this.black.indexOf(tag) > -1) {
25959 this.black.push(tag);
25964 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25965 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25969 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25970 if (b.indexOf(tag) > -1) {
25973 this.cwhite.push(tag);
25977 Roo.each(w, function(tag) {
25978 if (b.indexOf(tag) > -1) {
25981 if (this.cwhite.indexOf(tag) > -1) {
25984 this.cwhite.push(tag);
25989 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25990 if (w.indexOf(tag) > -1) {
25993 this.cblack.push(tag);
25997 Roo.each(b, function(tag) {
25998 if (w.indexOf(tag) > -1) {
26001 if (this.cblack.indexOf(tag) > -1) {
26004 this.cblack.push(tag);
26009 setStylesheets : function(stylesheets)
26011 if(typeof(stylesheets) == 'string'){
26012 Roo.get(this.iframe.contentDocument.head).createChild({
26014 rel : 'stylesheet',
26023 Roo.each(stylesheets, function(s) {
26028 Roo.get(_this.iframe.contentDocument.head).createChild({
26030 rel : 'stylesheet',
26039 removeStylesheets : function()
26043 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26048 setStyle : function(style)
26050 Roo.get(this.iframe.contentDocument.head).createChild({
26059 // hide stuff that is not compatible
26073 * @event specialkey
26077 * @cfg {String} fieldClass @hide
26080 * @cfg {String} focusClass @hide
26083 * @cfg {String} autoCreate @hide
26086 * @cfg {String} inputType @hide
26089 * @cfg {String} invalidClass @hide
26092 * @cfg {String} invalidText @hide
26095 * @cfg {String} msgFx @hide
26098 * @cfg {String} validateOnBlur @hide
26102 Roo.HtmlEditorCore.white = [
26103 'area', 'br', 'img', 'input', 'hr', 'wbr',
26105 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26106 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26107 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26108 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26109 'table', 'ul', 'xmp',
26111 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26114 'dir', 'menu', 'ol', 'ul', 'dl',
26120 Roo.HtmlEditorCore.black = [
26121 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26123 'base', 'basefont', 'bgsound', 'blink', 'body',
26124 'frame', 'frameset', 'head', 'html', 'ilayer',
26125 'iframe', 'layer', 'link', 'meta', 'object',
26126 'script', 'style' ,'title', 'xml' // clean later..
26128 Roo.HtmlEditorCore.clean = [
26129 'script', 'style', 'title', 'xml'
26131 Roo.HtmlEditorCore.remove = [
26136 Roo.HtmlEditorCore.ablack = [
26140 Roo.HtmlEditorCore.aclean = [
26141 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26145 Roo.HtmlEditorCore.pwhite= [
26146 'http', 'https', 'mailto'
26149 // white listed style attributes.
26150 Roo.HtmlEditorCore.cwhite= [
26151 // 'text-align', /// default is to allow most things..
26157 // black listed style attributes.
26158 Roo.HtmlEditorCore.cblack= [
26159 // 'font-size' -- this can be set by the project
26163 Roo.HtmlEditorCore.swapCodes =[
26164 [ 8211, "–" ],
26165 [ 8212, "—" ],
26182 * @class Roo.bootstrap.HtmlEditor
26183 * @extends Roo.bootstrap.TextArea
26184 * Bootstrap HtmlEditor class
26187 * Create a new HtmlEditor
26188 * @param {Object} config The config object
26191 Roo.bootstrap.HtmlEditor = function(config){
26192 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26193 if (!this.toolbars) {
26194 this.toolbars = [];
26197 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26200 * @event initialize
26201 * Fires when the editor is fully initialized (including the iframe)
26202 * @param {HtmlEditor} this
26207 * Fires when the editor is first receives the focus. Any insertion must wait
26208 * until after this event.
26209 * @param {HtmlEditor} this
26213 * @event beforesync
26214 * Fires before the textarea is updated with content from the editor iframe. Return false
26215 * to cancel the sync.
26216 * @param {HtmlEditor} this
26217 * @param {String} html
26221 * @event beforepush
26222 * Fires before the iframe editor is updated with content from the textarea. Return false
26223 * to cancel the push.
26224 * @param {HtmlEditor} this
26225 * @param {String} html
26230 * Fires when the textarea is updated with content from the editor iframe.
26231 * @param {HtmlEditor} this
26232 * @param {String} html
26237 * Fires when the iframe editor is updated with content from the textarea.
26238 * @param {HtmlEditor} this
26239 * @param {String} html
26243 * @event editmodechange
26244 * Fires when the editor switches edit modes
26245 * @param {HtmlEditor} this
26246 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26248 editmodechange: true,
26250 * @event editorevent
26251 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26252 * @param {HtmlEditor} this
26256 * @event firstfocus
26257 * Fires when on first focus - needed by toolbars..
26258 * @param {HtmlEditor} this
26263 * Auto save the htmlEditor value as a file into Events
26264 * @param {HtmlEditor} this
26268 * @event savedpreview
26269 * preview the saved version of htmlEditor
26270 * @param {HtmlEditor} this
26277 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26281 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26286 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26291 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26296 * @cfg {Number} height (in pixels)
26300 * @cfg {Number} width (in pixels)
26305 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26308 stylesheets: false,
26313 // private properties
26314 validationEvent : false,
26316 initialized : false,
26319 onFocus : Roo.emptyFn,
26321 hideMode:'offsets',
26323 tbContainer : false,
26327 toolbarContainer :function() {
26328 return this.wrap.select('.x-html-editor-tb',true).first();
26332 * Protected method that will not generally be called directly. It
26333 * is called when the editor creates its toolbar. Override this method if you need to
26334 * add custom toolbar buttons.
26335 * @param {HtmlEditor} editor
26337 createToolbar : function(){
26338 Roo.log('renewing');
26339 Roo.log("create toolbars");
26341 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26342 this.toolbars[0].render(this.toolbarContainer());
26346 // if (!editor.toolbars || !editor.toolbars.length) {
26347 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26350 // for (var i =0 ; i < editor.toolbars.length;i++) {
26351 // editor.toolbars[i] = Roo.factory(
26352 // typeof(editor.toolbars[i]) == 'string' ?
26353 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26354 // Roo.bootstrap.HtmlEditor);
26355 // editor.toolbars[i].init(editor);
26361 onRender : function(ct, position)
26363 // Roo.log("Call onRender: " + this.xtype);
26365 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26367 this.wrap = this.inputEl().wrap({
26368 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26371 this.editorcore.onRender(ct, position);
26373 if (this.resizable) {
26374 this.resizeEl = new Roo.Resizable(this.wrap, {
26378 minHeight : this.height,
26379 height: this.height,
26380 handles : this.resizable,
26383 resize : function(r, w, h) {
26384 _t.onResize(w,h); // -something
26390 this.createToolbar(this);
26393 if(!this.width && this.resizable){
26394 this.setSize(this.wrap.getSize());
26396 if (this.resizeEl) {
26397 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26398 // should trigger onReize..
26404 onResize : function(w, h)
26406 Roo.log('resize: ' +w + ',' + h );
26407 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26411 if(this.inputEl() ){
26412 if(typeof w == 'number'){
26413 var aw = w - this.wrap.getFrameWidth('lr');
26414 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26417 if(typeof h == 'number'){
26418 var tbh = -11; // fixme it needs to tool bar size!
26419 for (var i =0; i < this.toolbars.length;i++) {
26420 // fixme - ask toolbars for heights?
26421 tbh += this.toolbars[i].el.getHeight();
26422 //if (this.toolbars[i].footer) {
26423 // tbh += this.toolbars[i].footer.el.getHeight();
26431 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26432 ah -= 5; // knock a few pixes off for look..
26433 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26437 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26438 this.editorcore.onResize(ew,eh);
26443 * Toggles the editor between standard and source edit mode.
26444 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26446 toggleSourceEdit : function(sourceEditMode)
26448 this.editorcore.toggleSourceEdit(sourceEditMode);
26450 if(this.editorcore.sourceEditMode){
26451 Roo.log('editor - showing textarea');
26454 // Roo.log(this.syncValue());
26456 this.inputEl().removeClass(['hide', 'x-hidden']);
26457 this.inputEl().dom.removeAttribute('tabIndex');
26458 this.inputEl().focus();
26460 Roo.log('editor - hiding textarea');
26462 // Roo.log(this.pushValue());
26465 this.inputEl().addClass(['hide', 'x-hidden']);
26466 this.inputEl().dom.setAttribute('tabIndex', -1);
26467 //this.deferFocus();
26470 if(this.resizable){
26471 this.setSize(this.wrap.getSize());
26474 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26477 // private (for BoxComponent)
26478 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26480 // private (for BoxComponent)
26481 getResizeEl : function(){
26485 // private (for BoxComponent)
26486 getPositionEl : function(){
26491 initEvents : function(){
26492 this.originalValue = this.getValue();
26496 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26499 // markInvalid : Roo.emptyFn,
26501 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26504 // clearInvalid : Roo.emptyFn,
26506 setValue : function(v){
26507 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26508 this.editorcore.pushValue();
26513 deferFocus : function(){
26514 this.focus.defer(10, this);
26518 focus : function(){
26519 this.editorcore.focus();
26525 onDestroy : function(){
26531 for (var i =0; i < this.toolbars.length;i++) {
26532 // fixme - ask toolbars for heights?
26533 this.toolbars[i].onDestroy();
26536 this.wrap.dom.innerHTML = '';
26537 this.wrap.remove();
26542 onFirstFocus : function(){
26543 //Roo.log("onFirstFocus");
26544 this.editorcore.onFirstFocus();
26545 for (var i =0; i < this.toolbars.length;i++) {
26546 this.toolbars[i].onFirstFocus();
26552 syncValue : function()
26554 this.editorcore.syncValue();
26557 pushValue : function()
26559 this.editorcore.pushValue();
26563 // hide stuff that is not compatible
26577 * @event specialkey
26581 * @cfg {String} fieldClass @hide
26584 * @cfg {String} focusClass @hide
26587 * @cfg {String} autoCreate @hide
26590 * @cfg {String} inputType @hide
26594 * @cfg {String} invalidText @hide
26597 * @cfg {String} msgFx @hide
26600 * @cfg {String} validateOnBlur @hide
26609 Roo.namespace('Roo.bootstrap.htmleditor');
26611 * @class Roo.bootstrap.HtmlEditorToolbar1
26617 new Roo.bootstrap.HtmlEditor({
26620 new Roo.bootstrap.HtmlEditorToolbar1({
26621 disable : { fonts: 1 , format: 1, ..., ... , ...],
26627 * @cfg {Object} disable List of elements to disable..
26628 * @cfg {Array} btns List of additional buttons.
26632 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26635 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26638 Roo.apply(this, config);
26640 // default disabled, based on 'good practice'..
26641 this.disable = this.disable || {};
26642 Roo.applyIf(this.disable, {
26645 specialElements : true
26647 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26649 this.editor = config.editor;
26650 this.editorcore = config.editor.editorcore;
26652 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26654 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26655 // dont call parent... till later.
26657 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26662 editorcore : false,
26667 "h1","h2","h3","h4","h5","h6",
26669 "abbr", "acronym", "address", "cite", "samp", "var",
26673 onRender : function(ct, position)
26675 // Roo.log("Call onRender: " + this.xtype);
26677 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26679 this.el.dom.style.marginBottom = '0';
26681 var editorcore = this.editorcore;
26682 var editor= this.editor;
26685 var btn = function(id,cmd , toggle, handler, html){
26687 var event = toggle ? 'toggle' : 'click';
26692 xns: Roo.bootstrap,
26696 enableToggle:toggle !== false,
26698 pressed : toggle ? false : null,
26701 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26702 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26708 // var cb_box = function...
26713 xns: Roo.bootstrap,
26718 xns: Roo.bootstrap,
26722 Roo.each(this.formats, function(f) {
26723 style.menu.items.push({
26725 xns: Roo.bootstrap,
26726 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26731 editorcore.insertTag(this.tagname);
26738 children.push(style);
26740 btn('bold',false,true);
26741 btn('italic',false,true);
26742 btn('align-left', 'justifyleft',true);
26743 btn('align-center', 'justifycenter',true);
26744 btn('align-right' , 'justifyright',true);
26745 btn('link', false, false, function(btn) {
26746 //Roo.log("create link?");
26747 var url = prompt(this.createLinkText, this.defaultLinkValue);
26748 if(url && url != 'http:/'+'/'){
26749 this.editorcore.relayCmd('createlink', url);
26752 btn('list','insertunorderedlist',true);
26753 btn('pencil', false,true, function(btn){
26755 this.toggleSourceEdit(btn.pressed);
26758 if (this.editor.btns.length > 0) {
26759 for (var i = 0; i<this.editor.btns.length; i++) {
26760 children.push(this.editor.btns[i]);
26768 xns: Roo.bootstrap,
26773 xns: Roo.bootstrap,
26778 cog.menu.items.push({
26780 xns: Roo.bootstrap,
26781 html : Clean styles,
26786 editorcore.insertTag(this.tagname);
26795 this.xtype = 'NavSimplebar';
26797 for(var i=0;i< children.length;i++) {
26799 this.buttons.add(this.addxtypeChild(children[i]));
26803 editor.on('editorevent', this.updateToolbar, this);
26805 onBtnClick : function(id)
26807 this.editorcore.relayCmd(id);
26808 this.editorcore.focus();
26812 * Protected method that will not generally be called directly. It triggers
26813 * a toolbar update by reading the markup state of the current selection in the editor.
26815 updateToolbar: function(){
26817 if(!this.editorcore.activated){
26818 this.editor.onFirstFocus(); // is this neeed?
26822 var btns = this.buttons;
26823 var doc = this.editorcore.doc;
26824 btns.get('bold').setActive(doc.queryCommandState('bold'));
26825 btns.get('italic').setActive(doc.queryCommandState('italic'));
26826 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26828 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26829 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26830 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26832 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26833 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26836 var ans = this.editorcore.getAllAncestors();
26837 if (this.formatCombo) {
26840 var store = this.formatCombo.store;
26841 this.formatCombo.setValue("");
26842 for (var i =0; i < ans.length;i++) {
26843 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26845 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26853 // hides menus... - so this cant be on a menu...
26854 Roo.bootstrap.MenuMgr.hideAll();
26856 Roo.bootstrap.MenuMgr.hideAll();
26857 //this.editorsyncValue();
26859 onFirstFocus: function() {
26860 this.buttons.each(function(item){
26864 toggleSourceEdit : function(sourceEditMode){
26867 if(sourceEditMode){
26868 Roo.log("disabling buttons");
26869 this.buttons.each( function(item){
26870 if(item.cmd != 'pencil'){
26876 Roo.log("enabling buttons");
26877 if(this.editorcore.initialized){
26878 this.buttons.each( function(item){
26884 Roo.log("calling toggole on editor");
26885 // tell the editor that it's been pressed..
26886 this.editor.toggleSourceEdit(sourceEditMode);
26900 * @class Roo.bootstrap.Markdown
26901 * @extends Roo.bootstrap.TextArea
26902 * Bootstrap Showdown editable area
26903 * @cfg {string} content
26906 * Create a new Showdown
26909 Roo.bootstrap.Markdown = function(config){
26910 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26914 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26918 initEvents : function()
26921 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26922 this.markdownEl = this.el.createChild({
26923 cls : 'roo-markdown-area'
26925 this.inputEl().addClass('d-none');
26926 if (this.getValue() == '') {
26927 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26930 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26932 this.markdownEl.on('click', this.toggleTextEdit, this);
26933 this.on('blur', this.toggleTextEdit, this);
26934 this.on('specialkey', this.resizeTextArea, this);
26937 toggleTextEdit : function()
26939 var sh = this.markdownEl.getHeight();
26940 this.inputEl().addClass('d-none');
26941 this.markdownEl.addClass('d-none');
26942 if (!this.editing) {
26944 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26945 this.inputEl().removeClass('d-none');
26946 this.inputEl().focus();
26947 this.editing = true;
26950 // show showdown...
26951 this.updateMarkdown();
26952 this.markdownEl.removeClass('d-none');
26953 this.editing = false;
26956 updateMarkdown : function()
26958 if (this.getValue() == '') {
26959 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26963 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26966 resizeTextArea: function () {
26969 Roo.log([sh, this.getValue().split("\n").length * 30]);
26970 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26972 setValue : function(val)
26974 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26975 if (!this.editing) {
26976 this.updateMarkdown();
26982 if (!this.editing) {
26983 this.toggleTextEdit();
26991 * @class Roo.bootstrap.Table.AbstractSelectionModel
26992 * @extends Roo.util.Observable
26993 * Abstract base class for grid SelectionModels. It provides the interface that should be
26994 * implemented by descendant classes. This class should not be directly instantiated.
26997 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26998 this.locked = false;
26999 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27003 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27004 /** @ignore Called by the grid automatically. Do not call directly. */
27005 init : function(grid){
27011 * Locks the selections.
27014 this.locked = true;
27018 * Unlocks the selections.
27020 unlock : function(){
27021 this.locked = false;
27025 * Returns true if the selections are locked.
27026 * @return {Boolean}
27028 isLocked : function(){
27029 return this.locked;
27033 initEvents : function ()
27039 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27040 * @class Roo.bootstrap.Table.RowSelectionModel
27041 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27042 * It supports multiple selections and keyboard selection/navigation.
27044 * @param {Object} config
27047 Roo.bootstrap.Table.RowSelectionModel = function(config){
27048 Roo.apply(this, config);
27049 this.selections = new Roo.util.MixedCollection(false, function(o){
27054 this.lastActive = false;
27058 * @event selectionchange
27059 * Fires when the selection changes
27060 * @param {SelectionModel} this
27062 "selectionchange" : true,
27064 * @event afterselectionchange
27065 * Fires after the selection changes (eg. by key press or clicking)
27066 * @param {SelectionModel} this
27068 "afterselectionchange" : true,
27070 * @event beforerowselect
27071 * Fires when a row is selected being selected, return false to cancel.
27072 * @param {SelectionModel} this
27073 * @param {Number} rowIndex The selected index
27074 * @param {Boolean} keepExisting False if other selections will be cleared
27076 "beforerowselect" : true,
27079 * Fires when a row is selected.
27080 * @param {SelectionModel} this
27081 * @param {Number} rowIndex The selected index
27082 * @param {Roo.data.Record} r The record
27084 "rowselect" : true,
27086 * @event rowdeselect
27087 * Fires when a row is deselected.
27088 * @param {SelectionModel} this
27089 * @param {Number} rowIndex The selected index
27091 "rowdeselect" : true
27093 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27094 this.locked = false;
27097 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27099 * @cfg {Boolean} singleSelect
27100 * True to allow selection of only one row at a time (defaults to false)
27102 singleSelect : false,
27105 initEvents : function()
27108 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27109 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27110 //}else{ // allow click to work like normal
27111 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27113 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27114 this.grid.on("rowclick", this.handleMouseDown, this);
27116 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27117 "up" : function(e){
27119 this.selectPrevious(e.shiftKey);
27120 }else if(this.last !== false && this.lastActive !== false){
27121 var last = this.last;
27122 this.selectRange(this.last, this.lastActive-1);
27123 this.grid.getView().focusRow(this.lastActive);
27124 if(last !== false){
27128 this.selectFirstRow();
27130 this.fireEvent("afterselectionchange", this);
27132 "down" : function(e){
27134 this.selectNext(e.shiftKey);
27135 }else if(this.last !== false && this.lastActive !== false){
27136 var last = this.last;
27137 this.selectRange(this.last, this.lastActive+1);
27138 this.grid.getView().focusRow(this.lastActive);
27139 if(last !== false){
27143 this.selectFirstRow();
27145 this.fireEvent("afterselectionchange", this);
27149 this.grid.store.on('load', function(){
27150 this.selections.clear();
27153 var view = this.grid.view;
27154 view.on("refresh", this.onRefresh, this);
27155 view.on("rowupdated", this.onRowUpdated, this);
27156 view.on("rowremoved", this.onRemove, this);
27161 onRefresh : function()
27163 var ds = this.grid.store, i, v = this.grid.view;
27164 var s = this.selections;
27165 s.each(function(r){
27166 if((i = ds.indexOfId(r.id)) != -1){
27175 onRemove : function(v, index, r){
27176 this.selections.remove(r);
27180 onRowUpdated : function(v, index, r){
27181 if(this.isSelected(r)){
27182 v.onRowSelect(index);
27188 * @param {Array} records The records to select
27189 * @param {Boolean} keepExisting (optional) True to keep existing selections
27191 selectRecords : function(records, keepExisting)
27194 this.clearSelections();
27196 var ds = this.grid.store;
27197 for(var i = 0, len = records.length; i < len; i++){
27198 this.selectRow(ds.indexOf(records[i]), true);
27203 * Gets the number of selected rows.
27206 getCount : function(){
27207 return this.selections.length;
27211 * Selects the first row in the grid.
27213 selectFirstRow : function(){
27218 * Select the last row.
27219 * @param {Boolean} keepExisting (optional) True to keep existing selections
27221 selectLastRow : function(keepExisting){
27222 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27223 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27227 * Selects the row immediately following the last selected row.
27228 * @param {Boolean} keepExisting (optional) True to keep existing selections
27230 selectNext : function(keepExisting)
27232 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27233 this.selectRow(this.last+1, keepExisting);
27234 this.grid.getView().focusRow(this.last);
27239 * Selects the row that precedes the last selected row.
27240 * @param {Boolean} keepExisting (optional) True to keep existing selections
27242 selectPrevious : function(keepExisting){
27244 this.selectRow(this.last-1, keepExisting);
27245 this.grid.getView().focusRow(this.last);
27250 * Returns the selected records
27251 * @return {Array} Array of selected records
27253 getSelections : function(){
27254 return [].concat(this.selections.items);
27258 * Returns the first selected record.
27261 getSelected : function(){
27262 return this.selections.itemAt(0);
27267 * Clears all selections.
27269 clearSelections : function(fast)
27275 var ds = this.grid.store;
27276 var s = this.selections;
27277 s.each(function(r){
27278 this.deselectRow(ds.indexOfId(r.id));
27282 this.selections.clear();
27289 * Selects all rows.
27291 selectAll : function(){
27295 this.selections.clear();
27296 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27297 this.selectRow(i, true);
27302 * Returns True if there is a selection.
27303 * @return {Boolean}
27305 hasSelection : function(){
27306 return this.selections.length > 0;
27310 * Returns True if the specified row is selected.
27311 * @param {Number/Record} record The record or index of the record to check
27312 * @return {Boolean}
27314 isSelected : function(index){
27315 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27316 return (r && this.selections.key(r.id) ? true : false);
27320 * Returns True if the specified record id is selected.
27321 * @param {String} id The id of record to check
27322 * @return {Boolean}
27324 isIdSelected : function(id){
27325 return (this.selections.key(id) ? true : false);
27330 handleMouseDBClick : function(e, t){
27334 handleMouseDown : function(e, t)
27336 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27337 if(this.isLocked() || rowIndex < 0 ){
27340 if(e.shiftKey && this.last !== false){
27341 var last = this.last;
27342 this.selectRange(last, rowIndex, e.ctrlKey);
27343 this.last = last; // reset the last
27347 var isSelected = this.isSelected(rowIndex);
27348 //Roo.log("select row:" + rowIndex);
27350 this.deselectRow(rowIndex);
27352 this.selectRow(rowIndex, true);
27356 if(e.button !== 0 && isSelected){
27357 alert('rowIndex 2: ' + rowIndex);
27358 view.focusRow(rowIndex);
27359 }else if(e.ctrlKey && isSelected){
27360 this.deselectRow(rowIndex);
27361 }else if(!isSelected){
27362 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27363 view.focusRow(rowIndex);
27367 this.fireEvent("afterselectionchange", this);
27370 handleDragableRowClick : function(grid, rowIndex, e)
27372 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27373 this.selectRow(rowIndex, false);
27374 grid.view.focusRow(rowIndex);
27375 this.fireEvent("afterselectionchange", this);
27380 * Selects multiple rows.
27381 * @param {Array} rows Array of the indexes of the row to select
27382 * @param {Boolean} keepExisting (optional) True to keep existing selections
27384 selectRows : function(rows, keepExisting){
27386 this.clearSelections();
27388 for(var i = 0, len = rows.length; i < len; i++){
27389 this.selectRow(rows[i], true);
27394 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27395 * @param {Number} startRow The index of the first row in the range
27396 * @param {Number} endRow The index of the last row in the range
27397 * @param {Boolean} keepExisting (optional) True to retain existing selections
27399 selectRange : function(startRow, endRow, keepExisting){
27404 this.clearSelections();
27406 if(startRow <= endRow){
27407 for(var i = startRow; i <= endRow; i++){
27408 this.selectRow(i, true);
27411 for(var i = startRow; i >= endRow; i--){
27412 this.selectRow(i, true);
27418 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27419 * @param {Number} startRow The index of the first row in the range
27420 * @param {Number} endRow The index of the last row in the range
27422 deselectRange : function(startRow, endRow, preventViewNotify){
27426 for(var i = startRow; i <= endRow; i++){
27427 this.deselectRow(i, preventViewNotify);
27433 * @param {Number} row The index of the row to select
27434 * @param {Boolean} keepExisting (optional) True to keep existing selections
27436 selectRow : function(index, keepExisting, preventViewNotify)
27438 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27441 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27442 if(!keepExisting || this.singleSelect){
27443 this.clearSelections();
27446 var r = this.grid.store.getAt(index);
27447 //console.log('selectRow - record id :' + r.id);
27449 this.selections.add(r);
27450 this.last = this.lastActive = index;
27451 if(!preventViewNotify){
27452 var proxy = new Roo.Element(
27453 this.grid.getRowDom(index)
27455 proxy.addClass('bg-info info');
27457 this.fireEvent("rowselect", this, index, r);
27458 this.fireEvent("selectionchange", this);
27464 * @param {Number} row The index of the row to deselect
27466 deselectRow : function(index, preventViewNotify)
27471 if(this.last == index){
27474 if(this.lastActive == index){
27475 this.lastActive = false;
27478 var r = this.grid.store.getAt(index);
27483 this.selections.remove(r);
27484 //.console.log('deselectRow - record id :' + r.id);
27485 if(!preventViewNotify){
27487 var proxy = new Roo.Element(
27488 this.grid.getRowDom(index)
27490 proxy.removeClass('bg-info info');
27492 this.fireEvent("rowdeselect", this, index);
27493 this.fireEvent("selectionchange", this);
27497 restoreLast : function(){
27499 this.last = this._last;
27504 acceptsNav : function(row, col, cm){
27505 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27509 onEditorKey : function(field, e){
27510 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27515 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27517 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27519 }else if(k == e.ENTER && !e.ctrlKey){
27523 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27525 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27527 }else if(k == e.ESC){
27531 g.startEditing(newCell[0], newCell[1]);
27537 * Ext JS Library 1.1.1
27538 * Copyright(c) 2006-2007, Ext JS, LLC.
27540 * Originally Released Under LGPL - original licence link has changed is not relivant.
27543 * <script type="text/javascript">
27547 * @class Roo.bootstrap.PagingToolbar
27548 * @extends Roo.bootstrap.NavSimplebar
27549 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27551 * Create a new PagingToolbar
27552 * @param {Object} config The config object
27553 * @param {Roo.data.Store} store
27555 Roo.bootstrap.PagingToolbar = function(config)
27557 // old args format still supported... - xtype is prefered..
27558 // created from xtype...
27560 this.ds = config.dataSource;
27562 if (config.store && !this.ds) {
27563 this.store= Roo.factory(config.store, Roo.data);
27564 this.ds = this.store;
27565 this.ds.xmodule = this.xmodule || false;
27568 this.toolbarItems = [];
27569 if (config.items) {
27570 this.toolbarItems = config.items;
27573 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27578 this.bind(this.ds);
27581 if (Roo.bootstrap.version == 4) {
27582 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27584 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27589 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27591 * @cfg {Roo.data.Store} dataSource
27592 * The underlying data store providing the paged data
27595 * @cfg {String/HTMLElement/Element} container
27596 * container The id or element that will contain the toolbar
27599 * @cfg {Boolean} displayInfo
27600 * True to display the displayMsg (defaults to false)
27603 * @cfg {Number} pageSize
27604 * The number of records to display per page (defaults to 20)
27608 * @cfg {String} displayMsg
27609 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27611 displayMsg : 'Displaying {0} - {1} of {2}',
27613 * @cfg {String} emptyMsg
27614 * The message to display when no records are found (defaults to "No data to display")
27616 emptyMsg : 'No data to display',
27618 * Customizable piece of the default paging text (defaults to "Page")
27621 beforePageText : "Page",
27623 * Customizable piece of the default paging text (defaults to "of %0")
27626 afterPageText : "of {0}",
27628 * Customizable piece of the default paging text (defaults to "First Page")
27631 firstText : "First Page",
27633 * Customizable piece of the default paging text (defaults to "Previous Page")
27636 prevText : "Previous Page",
27638 * Customizable piece of the default paging text (defaults to "Next Page")
27641 nextText : "Next Page",
27643 * Customizable piece of the default paging text (defaults to "Last Page")
27646 lastText : "Last Page",
27648 * Customizable piece of the default paging text (defaults to "Refresh")
27651 refreshText : "Refresh",
27655 onRender : function(ct, position)
27657 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27658 this.navgroup.parentId = this.id;
27659 this.navgroup.onRender(this.el, null);
27660 // add the buttons to the navgroup
27662 if(this.displayInfo){
27663 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27664 this.displayEl = this.el.select('.x-paging-info', true).first();
27665 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27666 // this.displayEl = navel.el.select('span',true).first();
27672 Roo.each(_this.buttons, function(e){ // this might need to use render????
27673 Roo.factory(e).render(_this.el);
27677 Roo.each(_this.toolbarItems, function(e) {
27678 _this.navgroup.addItem(e);
27682 this.first = this.navgroup.addItem({
27683 tooltip: this.firstText,
27684 cls: "prev btn-outline-secondary",
27685 html : ' <i class="fa fa-step-backward"></i>',
27687 preventDefault: true,
27688 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27691 this.prev = this.navgroup.addItem({
27692 tooltip: this.prevText,
27693 cls: "prev btn-outline-secondary",
27694 html : ' <i class="fa fa-backward"></i>',
27696 preventDefault: true,
27697 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27699 //this.addSeparator();
27702 var field = this.navgroup.addItem( {
27704 cls : 'x-paging-position btn-outline-secondary',
27706 html : this.beforePageText +
27707 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27708 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27711 this.field = field.el.select('input', true).first();
27712 this.field.on("keydown", this.onPagingKeydown, this);
27713 this.field.on("focus", function(){this.dom.select();});
27716 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27717 //this.field.setHeight(18);
27718 //this.addSeparator();
27719 this.next = this.navgroup.addItem({
27720 tooltip: this.nextText,
27721 cls: "next btn-outline-secondary",
27722 html : ' <i class="fa fa-forward"></i>',
27724 preventDefault: true,
27725 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27727 this.last = this.navgroup.addItem({
27728 tooltip: this.lastText,
27729 html : ' <i class="fa fa-step-forward"></i>',
27730 cls: "next btn-outline-secondary",
27732 preventDefault: true,
27733 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27735 //this.addSeparator();
27736 this.loading = this.navgroup.addItem({
27737 tooltip: this.refreshText,
27738 cls: "btn-outline-secondary",
27739 html : ' <i class="fa fa-refresh"></i>',
27740 preventDefault: true,
27741 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27747 updateInfo : function(){
27748 if(this.displayEl){
27749 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27750 var msg = count == 0 ?
27754 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27756 this.displayEl.update(msg);
27761 onLoad : function(ds, r, o)
27763 this.cursor = o.params && o.params.start ? o.params.start : 0;
27765 var d = this.getPageData(),
27770 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27771 this.field.dom.value = ap;
27772 this.first.setDisabled(ap == 1);
27773 this.prev.setDisabled(ap == 1);
27774 this.next.setDisabled(ap == ps);
27775 this.last.setDisabled(ap == ps);
27776 this.loading.enable();
27781 getPageData : function(){
27782 var total = this.ds.getTotalCount();
27785 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27786 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27791 onLoadError : function(){
27792 this.loading.enable();
27796 onPagingKeydown : function(e){
27797 var k = e.getKey();
27798 var d = this.getPageData();
27800 var v = this.field.dom.value, pageNum;
27801 if(!v || isNaN(pageNum = parseInt(v, 10))){
27802 this.field.dom.value = d.activePage;
27805 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27806 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27809 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27811 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27812 this.field.dom.value = pageNum;
27813 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27816 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27818 var v = this.field.dom.value, pageNum;
27819 var increment = (e.shiftKey) ? 10 : 1;
27820 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27823 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27824 this.field.dom.value = d.activePage;
27827 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27829 this.field.dom.value = parseInt(v, 10) + increment;
27830 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27831 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27838 beforeLoad : function(){
27840 this.loading.disable();
27845 onClick : function(which){
27854 ds.load({params:{start: 0, limit: this.pageSize}});
27857 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27860 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27863 var total = ds.getTotalCount();
27864 var extra = total % this.pageSize;
27865 var lastStart = extra ? (total - extra) : total-this.pageSize;
27866 ds.load({params:{start: lastStart, limit: this.pageSize}});
27869 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27875 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27876 * @param {Roo.data.Store} store The data store to unbind
27878 unbind : function(ds){
27879 ds.un("beforeload", this.beforeLoad, this);
27880 ds.un("load", this.onLoad, this);
27881 ds.un("loadexception", this.onLoadError, this);
27882 ds.un("remove", this.updateInfo, this);
27883 ds.un("add", this.updateInfo, this);
27884 this.ds = undefined;
27888 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27889 * @param {Roo.data.Store} store The data store to bind
27891 bind : function(ds){
27892 ds.on("beforeload", this.beforeLoad, this);
27893 ds.on("load", this.onLoad, this);
27894 ds.on("loadexception", this.onLoadError, this);
27895 ds.on("remove", this.updateInfo, this);
27896 ds.on("add", this.updateInfo, this);
27907 * @class Roo.bootstrap.MessageBar
27908 * @extends Roo.bootstrap.Component
27909 * Bootstrap MessageBar class
27910 * @cfg {String} html contents of the MessageBar
27911 * @cfg {String} weight (info | success | warning | danger) default info
27912 * @cfg {String} beforeClass insert the bar before the given class
27913 * @cfg {Boolean} closable (true | false) default false
27914 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27917 * Create a new Element
27918 * @param {Object} config The config object
27921 Roo.bootstrap.MessageBar = function(config){
27922 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27925 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27931 beforeClass: 'bootstrap-sticky-wrap',
27933 getAutoCreate : function(){
27937 cls: 'alert alert-dismissable alert-' + this.weight,
27942 html: this.html || ''
27948 cfg.cls += ' alert-messages-fixed';
27962 onRender : function(ct, position)
27964 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27967 var cfg = Roo.apply({}, this.getAutoCreate());
27971 cfg.cls += ' ' + this.cls;
27974 cfg.style = this.style;
27976 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27978 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27981 this.el.select('>button.close').on('click', this.hide, this);
27987 if (!this.rendered) {
27993 this.fireEvent('show', this);
27999 if (!this.rendered) {
28005 this.fireEvent('hide', this);
28008 update : function()
28010 // var e = this.el.dom.firstChild;
28012 // if(this.closable){
28013 // e = e.nextSibling;
28016 // e.data = this.html || '';
28018 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28034 * @class Roo.bootstrap.Graph
28035 * @extends Roo.bootstrap.Component
28036 * Bootstrap Graph class
28040 @cfg {String} graphtype bar | vbar | pie
28041 @cfg {number} g_x coodinator | centre x (pie)
28042 @cfg {number} g_y coodinator | centre y (pie)
28043 @cfg {number} g_r radius (pie)
28044 @cfg {number} g_height height of the chart (respected by all elements in the set)
28045 @cfg {number} g_width width of the chart (respected by all elements in the set)
28046 @cfg {Object} title The title of the chart
28049 -opts (object) options for the chart
28051 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28052 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28054 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
28055 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28057 o stretch (boolean)
28059 -opts (object) options for the pie
28062 o startAngle (number)
28063 o endAngle (number)
28067 * Create a new Input
28068 * @param {Object} config The config object
28071 Roo.bootstrap.Graph = function(config){
28072 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28078 * The img click event for the img.
28079 * @param {Roo.EventObject} e
28085 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28096 //g_colors: this.colors,
28103 getAutoCreate : function(){
28114 onRender : function(ct,position){
28117 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28119 if (typeof(Raphael) == 'undefined') {
28120 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28124 this.raphael = Raphael(this.el.dom);
28126 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28127 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28128 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28129 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28131 r.text(160, 10, "Single Series Chart").attr(txtattr);
28132 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28133 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28134 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28136 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28137 r.barchart(330, 10, 300, 220, data1);
28138 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28139 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28142 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28143 // r.barchart(30, 30, 560, 250, xdata, {
28144 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28145 // axis : "0 0 1 1",
28146 // axisxlabels : xdata
28147 // //yvalues : cols,
28150 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28152 // this.load(null,xdata,{
28153 // axis : "0 0 1 1",
28154 // axisxlabels : xdata
28159 load : function(graphtype,xdata,opts)
28161 this.raphael.clear();
28163 graphtype = this.graphtype;
28168 var r = this.raphael,
28169 fin = function () {
28170 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28172 fout = function () {
28173 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28175 pfin = function() {
28176 this.sector.stop();
28177 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28180 this.label[0].stop();
28181 this.label[0].attr({ r: 7.5 });
28182 this.label[1].attr({ "font-weight": 800 });
28185 pfout = function() {
28186 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28189 this.label[0].animate({ r: 5 }, 500, "bounce");
28190 this.label[1].attr({ "font-weight": 400 });
28196 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28199 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28202 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28203 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28205 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28212 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28217 setTitle: function(o)
28222 initEvents: function() {
28225 this.el.on('click', this.onClick, this);
28229 onClick : function(e)
28231 Roo.log('img onclick');
28232 this.fireEvent('click', this, e);
28244 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28247 * @class Roo.bootstrap.dash.NumberBox
28248 * @extends Roo.bootstrap.Component
28249 * Bootstrap NumberBox class
28250 * @cfg {String} headline Box headline
28251 * @cfg {String} content Box content
28252 * @cfg {String} icon Box icon
28253 * @cfg {String} footer Footer text
28254 * @cfg {String} fhref Footer href
28257 * Create a new NumberBox
28258 * @param {Object} config The config object
28262 Roo.bootstrap.dash.NumberBox = function(config){
28263 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28267 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28276 getAutoCreate : function(){
28280 cls : 'small-box ',
28288 cls : 'roo-headline',
28289 html : this.headline
28293 cls : 'roo-content',
28294 html : this.content
28308 cls : 'ion ' + this.icon
28317 cls : 'small-box-footer',
28318 href : this.fhref || '#',
28322 cfg.cn.push(footer);
28329 onRender : function(ct,position){
28330 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28337 setHeadline: function (value)
28339 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28342 setFooter: function (value, href)
28344 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28347 this.el.select('a.small-box-footer',true).first().attr('href', href);
28352 setContent: function (value)
28354 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28357 initEvents: function()
28371 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28374 * @class Roo.bootstrap.dash.TabBox
28375 * @extends Roo.bootstrap.Component
28376 * Bootstrap TabBox class
28377 * @cfg {String} title Title of the TabBox
28378 * @cfg {String} icon Icon of the TabBox
28379 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28380 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28383 * Create a new TabBox
28384 * @param {Object} config The config object
28388 Roo.bootstrap.dash.TabBox = function(config){
28389 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28394 * When a pane is added
28395 * @param {Roo.bootstrap.dash.TabPane} pane
28399 * @event activatepane
28400 * When a pane is activated
28401 * @param {Roo.bootstrap.dash.TabPane} pane
28403 "activatepane" : true
28411 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28416 tabScrollable : false,
28418 getChildContainer : function()
28420 return this.el.select('.tab-content', true).first();
28423 getAutoCreate : function(){
28427 cls: 'pull-left header',
28435 cls: 'fa ' + this.icon
28441 cls: 'nav nav-tabs pull-right',
28447 if(this.tabScrollable){
28454 cls: 'nav nav-tabs pull-right',
28465 cls: 'nav-tabs-custom',
28470 cls: 'tab-content no-padding',
28478 initEvents : function()
28480 //Roo.log('add add pane handler');
28481 this.on('addpane', this.onAddPane, this);
28484 * Updates the box title
28485 * @param {String} html to set the title to.
28487 setTitle : function(value)
28489 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28491 onAddPane : function(pane)
28493 this.panes.push(pane);
28494 //Roo.log('addpane');
28496 // tabs are rendere left to right..
28497 if(!this.showtabs){
28501 var ctr = this.el.select('.nav-tabs', true).first();
28504 var existing = ctr.select('.nav-tab',true);
28505 var qty = existing.getCount();;
28508 var tab = ctr.createChild({
28510 cls : 'nav-tab' + (qty ? '' : ' active'),
28518 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28521 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28523 pane.el.addClass('active');
28528 onTabClick : function(ev,un,ob,pane)
28530 //Roo.log('tab - prev default');
28531 ev.preventDefault();
28534 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28535 pane.tab.addClass('active');
28536 //Roo.log(pane.title);
28537 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28538 // technically we should have a deactivate event.. but maybe add later.
28539 // and it should not de-activate the selected tab...
28540 this.fireEvent('activatepane', pane);
28541 pane.el.addClass('active');
28542 pane.fireEvent('activate');
28547 getActivePane : function()
28550 Roo.each(this.panes, function(p) {
28551 if(p.el.hasClass('active')){
28572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28574 * @class Roo.bootstrap.TabPane
28575 * @extends Roo.bootstrap.Component
28576 * Bootstrap TabPane class
28577 * @cfg {Boolean} active (false | true) Default false
28578 * @cfg {String} title title of panel
28582 * Create a new TabPane
28583 * @param {Object} config The config object
28586 Roo.bootstrap.dash.TabPane = function(config){
28587 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28593 * When a pane is activated
28594 * @param {Roo.bootstrap.dash.TabPane} pane
28601 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28606 // the tabBox that this is attached to.
28609 getAutoCreate : function()
28617 cfg.cls += ' active';
28622 initEvents : function()
28624 //Roo.log('trigger add pane handler');
28625 this.parent().fireEvent('addpane', this)
28629 * Updates the tab title
28630 * @param {String} html to set the title to.
28632 setTitle: function(str)
28638 this.tab.select('a', true).first().dom.innerHTML = str;
28655 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28658 * @class Roo.bootstrap.menu.Menu
28659 * @extends Roo.bootstrap.Component
28660 * Bootstrap Menu class - container for Menu
28661 * @cfg {String} html Text of the menu
28662 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28663 * @cfg {String} icon Font awesome icon
28664 * @cfg {String} pos Menu align to (top | bottom) default bottom
28668 * Create a new Menu
28669 * @param {Object} config The config object
28673 Roo.bootstrap.menu.Menu = function(config){
28674 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28678 * @event beforeshow
28679 * Fires before this menu is displayed
28680 * @param {Roo.bootstrap.menu.Menu} this
28684 * @event beforehide
28685 * Fires before this menu is hidden
28686 * @param {Roo.bootstrap.menu.Menu} this
28691 * Fires after this menu is displayed
28692 * @param {Roo.bootstrap.menu.Menu} this
28697 * Fires after this menu is hidden
28698 * @param {Roo.bootstrap.menu.Menu} this
28703 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28704 * @param {Roo.bootstrap.menu.Menu} this
28705 * @param {Roo.EventObject} e
28712 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28716 weight : 'default',
28721 getChildContainer : function() {
28722 if(this.isSubMenu){
28726 return this.el.select('ul.dropdown-menu', true).first();
28729 getAutoCreate : function()
28734 cls : 'roo-menu-text',
28742 cls : 'fa ' + this.icon
28753 cls : 'dropdown-button btn btn-' + this.weight,
28758 cls : 'dropdown-toggle btn btn-' + this.weight,
28768 cls : 'dropdown-menu'
28774 if(this.pos == 'top'){
28775 cfg.cls += ' dropup';
28778 if(this.isSubMenu){
28781 cls : 'dropdown-menu'
28788 onRender : function(ct, position)
28790 this.isSubMenu = ct.hasClass('dropdown-submenu');
28792 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28795 initEvents : function()
28797 if(this.isSubMenu){
28801 this.hidden = true;
28803 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28804 this.triggerEl.on('click', this.onTriggerPress, this);
28806 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28807 this.buttonEl.on('click', this.onClick, this);
28813 if(this.isSubMenu){
28817 return this.el.select('ul.dropdown-menu', true).first();
28820 onClick : function(e)
28822 this.fireEvent("click", this, e);
28825 onTriggerPress : function(e)
28827 if (this.isVisible()) {
28834 isVisible : function(){
28835 return !this.hidden;
28840 this.fireEvent("beforeshow", this);
28842 this.hidden = false;
28843 this.el.addClass('open');
28845 Roo.get(document).on("mouseup", this.onMouseUp, this);
28847 this.fireEvent("show", this);
28854 this.fireEvent("beforehide", this);
28856 this.hidden = true;
28857 this.el.removeClass('open');
28859 Roo.get(document).un("mouseup", this.onMouseUp);
28861 this.fireEvent("hide", this);
28864 onMouseUp : function()
28878 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28881 * @class Roo.bootstrap.menu.Item
28882 * @extends Roo.bootstrap.Component
28883 * Bootstrap MenuItem class
28884 * @cfg {Boolean} submenu (true | false) default false
28885 * @cfg {String} html text of the item
28886 * @cfg {String} href the link
28887 * @cfg {Boolean} disable (true | false) default false
28888 * @cfg {Boolean} preventDefault (true | false) default true
28889 * @cfg {String} icon Font awesome icon
28890 * @cfg {String} pos Submenu align to (left | right) default right
28894 * Create a new Item
28895 * @param {Object} config The config object
28899 Roo.bootstrap.menu.Item = function(config){
28900 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28904 * Fires when the mouse is hovering over this menu
28905 * @param {Roo.bootstrap.menu.Item} this
28906 * @param {Roo.EventObject} e
28911 * Fires when the mouse exits this menu
28912 * @param {Roo.bootstrap.menu.Item} this
28913 * @param {Roo.EventObject} e
28919 * The raw click event for the entire grid.
28920 * @param {Roo.EventObject} e
28926 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28931 preventDefault: true,
28936 getAutoCreate : function()
28941 cls : 'roo-menu-item-text',
28949 cls : 'fa ' + this.icon
28958 href : this.href || '#',
28965 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28969 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28971 if(this.pos == 'left'){
28972 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28979 initEvents : function()
28981 this.el.on('mouseover', this.onMouseOver, this);
28982 this.el.on('mouseout', this.onMouseOut, this);
28984 this.el.select('a', true).first().on('click', this.onClick, this);
28988 onClick : function(e)
28990 if(this.preventDefault){
28991 e.preventDefault();
28994 this.fireEvent("click", this, e);
28997 onMouseOver : function(e)
28999 if(this.submenu && this.pos == 'left'){
29000 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29003 this.fireEvent("mouseover", this, e);
29006 onMouseOut : function(e)
29008 this.fireEvent("mouseout", this, e);
29020 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29023 * @class Roo.bootstrap.menu.Separator
29024 * @extends Roo.bootstrap.Component
29025 * Bootstrap Separator class
29028 * Create a new Separator
29029 * @param {Object} config The config object
29033 Roo.bootstrap.menu.Separator = function(config){
29034 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29037 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29039 getAutoCreate : function(){
29042 cls: 'dropdown-divider divider'
29060 * @class Roo.bootstrap.Tooltip
29061 * Bootstrap Tooltip class
29062 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29063 * to determine which dom element triggers the tooltip.
29065 * It needs to add support for additional attributes like tooltip-position
29068 * Create a new Toolti
29069 * @param {Object} config The config object
29072 Roo.bootstrap.Tooltip = function(config){
29073 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29075 this.alignment = Roo.bootstrap.Tooltip.alignment;
29077 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29078 this.alignment = config.alignment;
29083 Roo.apply(Roo.bootstrap.Tooltip, {
29085 * @function init initialize tooltip monitoring.
29089 currentTip : false,
29090 currentRegion : false,
29096 Roo.get(document).on('mouseover', this.enter ,this);
29097 Roo.get(document).on('mouseout', this.leave, this);
29100 this.currentTip = new Roo.bootstrap.Tooltip();
29103 enter : function(ev)
29105 var dom = ev.getTarget();
29107 //Roo.log(['enter',dom]);
29108 var el = Roo.fly(dom);
29109 if (this.currentEl) {
29111 //Roo.log(this.currentEl);
29112 //Roo.log(this.currentEl.contains(dom));
29113 if (this.currentEl == el) {
29116 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29122 if (this.currentTip.el) {
29123 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29127 if(!el || el.dom == document){
29133 if (!el.attr('tooltip')) {
29134 pel = el.findParent("[tooltip]");
29136 bindEl = Roo.get(pel);
29142 // you can not look for children, as if el is the body.. then everythign is the child..
29143 if (!pel && !el.attr('tooltip')) { //
29144 if (!el.select("[tooltip]").elements.length) {
29147 // is the mouse over this child...?
29148 bindEl = el.select("[tooltip]").first();
29149 var xy = ev.getXY();
29150 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29151 //Roo.log("not in region.");
29154 //Roo.log("child element over..");
29157 this.currentEl = el;
29158 this.currentTip.bind(bindEl);
29159 this.currentRegion = Roo.lib.Region.getRegion(dom);
29160 this.currentTip.enter();
29163 leave : function(ev)
29165 var dom = ev.getTarget();
29166 //Roo.log(['leave',dom]);
29167 if (!this.currentEl) {
29172 if (dom != this.currentEl.dom) {
29175 var xy = ev.getXY();
29176 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29179 // only activate leave if mouse cursor is outside... bounding box..
29184 if (this.currentTip) {
29185 this.currentTip.leave();
29187 //Roo.log('clear currentEl');
29188 this.currentEl = false;
29193 'left' : ['r-l', [-2,0], 'right'],
29194 'right' : ['l-r', [2,0], 'left'],
29195 'bottom' : ['t-b', [0,2], 'top'],
29196 'top' : [ 'b-t', [0,-2], 'bottom']
29202 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29207 delay : null, // can be { show : 300 , hide: 500}
29211 hoverState : null, //???
29213 placement : 'bottom',
29217 getAutoCreate : function(){
29224 cls : 'tooltip-arrow arrow'
29227 cls : 'tooltip-inner'
29234 bind : function(el)
29239 initEvents : function()
29241 this.arrowEl = this.el.select('.arrow', true).first();
29242 this.innerEl = this.el.select('.tooltip-inner', true).first();
29245 enter : function () {
29247 if (this.timeout != null) {
29248 clearTimeout(this.timeout);
29251 this.hoverState = 'in';
29252 //Roo.log("enter - show");
29253 if (!this.delay || !this.delay.show) {
29258 this.timeout = setTimeout(function () {
29259 if (_t.hoverState == 'in') {
29262 }, this.delay.show);
29266 clearTimeout(this.timeout);
29268 this.hoverState = 'out';
29269 if (!this.delay || !this.delay.hide) {
29275 this.timeout = setTimeout(function () {
29276 //Roo.log("leave - timeout");
29278 if (_t.hoverState == 'out') {
29280 Roo.bootstrap.Tooltip.currentEl = false;
29285 show : function (msg)
29288 this.render(document.body);
29291 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29293 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29295 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29297 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29298 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29300 var placement = typeof this.placement == 'function' ?
29301 this.placement.call(this, this.el, on_el) :
29304 var autoToken = /\s?auto?\s?/i;
29305 var autoPlace = autoToken.test(placement);
29307 placement = placement.replace(autoToken, '') || 'top';
29311 //this.el.setXY([0,0]);
29313 //this.el.dom.style.display='block';
29315 //this.el.appendTo(on_el);
29317 var p = this.getPosition();
29318 var box = this.el.getBox();
29324 var align = this.alignment[placement];
29326 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29328 if(placement == 'top' || placement == 'bottom'){
29330 placement = 'right';
29333 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29334 placement = 'left';
29337 var scroll = Roo.select('body', true).first().getScroll();
29339 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29343 align = this.alignment[placement];
29345 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29349 var elems = document.getElementsByTagName('div');
29350 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29351 for (var i = 0; i < elems.length; i++) {
29352 var zindex = Number.parseInt(
29353 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29356 if (zindex > highest) {
29363 this.el.dom.style.zIndex = highest;
29365 this.el.alignTo(this.bindEl, align[0],align[1]);
29366 //var arrow = this.el.select('.arrow',true).first();
29367 //arrow.set(align[2],
29369 this.el.addClass(placement);
29370 this.el.addClass("bs-tooltip-"+ placement);
29372 this.el.addClass('in fade show');
29374 this.hoverState = null;
29376 if (this.el.hasClass('fade')) {
29391 //this.el.setXY([0,0]);
29392 this.el.removeClass(['show', 'in']);
29408 * @class Roo.bootstrap.LocationPicker
29409 * @extends Roo.bootstrap.Component
29410 * Bootstrap LocationPicker class
29411 * @cfg {Number} latitude Position when init default 0
29412 * @cfg {Number} longitude Position when init default 0
29413 * @cfg {Number} zoom default 15
29414 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29415 * @cfg {Boolean} mapTypeControl default false
29416 * @cfg {Boolean} disableDoubleClickZoom default false
29417 * @cfg {Boolean} scrollwheel default true
29418 * @cfg {Boolean} streetViewControl default false
29419 * @cfg {Number} radius default 0
29420 * @cfg {String} locationName
29421 * @cfg {Boolean} draggable default true
29422 * @cfg {Boolean} enableAutocomplete default false
29423 * @cfg {Boolean} enableReverseGeocode default true
29424 * @cfg {String} markerTitle
29427 * Create a new LocationPicker
29428 * @param {Object} config The config object
29432 Roo.bootstrap.LocationPicker = function(config){
29434 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29439 * Fires when the picker initialized.
29440 * @param {Roo.bootstrap.LocationPicker} this
29441 * @param {Google Location} location
29445 * @event positionchanged
29446 * Fires when the picker position changed.
29447 * @param {Roo.bootstrap.LocationPicker} this
29448 * @param {Google Location} location
29450 positionchanged : true,
29453 * Fires when the map resize.
29454 * @param {Roo.bootstrap.LocationPicker} this
29459 * Fires when the map show.
29460 * @param {Roo.bootstrap.LocationPicker} this
29465 * Fires when the map hide.
29466 * @param {Roo.bootstrap.LocationPicker} this
29471 * Fires when click the map.
29472 * @param {Roo.bootstrap.LocationPicker} this
29473 * @param {Map event} e
29477 * @event mapRightClick
29478 * Fires when right click the map.
29479 * @param {Roo.bootstrap.LocationPicker} this
29480 * @param {Map event} e
29482 mapRightClick : true,
29484 * @event markerClick
29485 * Fires when click the marker.
29486 * @param {Roo.bootstrap.LocationPicker} this
29487 * @param {Map event} e
29489 markerClick : true,
29491 * @event markerRightClick
29492 * Fires when right click the marker.
29493 * @param {Roo.bootstrap.LocationPicker} this
29494 * @param {Map event} e
29496 markerRightClick : true,
29498 * @event OverlayViewDraw
29499 * Fires when OverlayView Draw
29500 * @param {Roo.bootstrap.LocationPicker} this
29502 OverlayViewDraw : true,
29504 * @event OverlayViewOnAdd
29505 * Fires when OverlayView Draw
29506 * @param {Roo.bootstrap.LocationPicker} this
29508 OverlayViewOnAdd : true,
29510 * @event OverlayViewOnRemove
29511 * Fires when OverlayView Draw
29512 * @param {Roo.bootstrap.LocationPicker} this
29514 OverlayViewOnRemove : true,
29516 * @event OverlayViewShow
29517 * Fires when OverlayView Draw
29518 * @param {Roo.bootstrap.LocationPicker} this
29519 * @param {Pixel} cpx
29521 OverlayViewShow : true,
29523 * @event OverlayViewHide
29524 * Fires when OverlayView Draw
29525 * @param {Roo.bootstrap.LocationPicker} this
29527 OverlayViewHide : true,
29529 * @event loadexception
29530 * Fires when load google lib failed.
29531 * @param {Roo.bootstrap.LocationPicker} this
29533 loadexception : true
29538 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29540 gMapContext: false,
29546 mapTypeControl: false,
29547 disableDoubleClickZoom: false,
29549 streetViewControl: false,
29553 enableAutocomplete: false,
29554 enableReverseGeocode: true,
29557 getAutoCreate: function()
29562 cls: 'roo-location-picker'
29568 initEvents: function(ct, position)
29570 if(!this.el.getWidth() || this.isApplied()){
29574 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29579 initial: function()
29581 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29582 this.fireEvent('loadexception', this);
29586 if(!this.mapTypeId){
29587 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29590 this.gMapContext = this.GMapContext();
29592 this.initOverlayView();
29594 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29598 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29599 _this.setPosition(_this.gMapContext.marker.position);
29602 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29603 _this.fireEvent('mapClick', this, event);
29607 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29608 _this.fireEvent('mapRightClick', this, event);
29612 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29613 _this.fireEvent('markerClick', this, event);
29617 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29618 _this.fireEvent('markerRightClick', this, event);
29622 this.setPosition(this.gMapContext.location);
29624 this.fireEvent('initial', this, this.gMapContext.location);
29627 initOverlayView: function()
29631 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29635 _this.fireEvent('OverlayViewDraw', _this);
29640 _this.fireEvent('OverlayViewOnAdd', _this);
29643 onRemove: function()
29645 _this.fireEvent('OverlayViewOnRemove', _this);
29648 show: function(cpx)
29650 _this.fireEvent('OverlayViewShow', _this, cpx);
29655 _this.fireEvent('OverlayViewHide', _this);
29661 fromLatLngToContainerPixel: function(event)
29663 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29666 isApplied: function()
29668 return this.getGmapContext() == false ? false : true;
29671 getGmapContext: function()
29673 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29676 GMapContext: function()
29678 var position = new google.maps.LatLng(this.latitude, this.longitude);
29680 var _map = new google.maps.Map(this.el.dom, {
29683 mapTypeId: this.mapTypeId,
29684 mapTypeControl: this.mapTypeControl,
29685 disableDoubleClickZoom: this.disableDoubleClickZoom,
29686 scrollwheel: this.scrollwheel,
29687 streetViewControl: this.streetViewControl,
29688 locationName: this.locationName,
29689 draggable: this.draggable,
29690 enableAutocomplete: this.enableAutocomplete,
29691 enableReverseGeocode: this.enableReverseGeocode
29694 var _marker = new google.maps.Marker({
29695 position: position,
29697 title: this.markerTitle,
29698 draggable: this.draggable
29705 location: position,
29706 radius: this.radius,
29707 locationName: this.locationName,
29708 addressComponents: {
29709 formatted_address: null,
29710 addressLine1: null,
29711 addressLine2: null,
29713 streetNumber: null,
29717 stateOrProvince: null
29720 domContainer: this.el.dom,
29721 geodecoder: new google.maps.Geocoder()
29725 drawCircle: function(center, radius, options)
29727 if (this.gMapContext.circle != null) {
29728 this.gMapContext.circle.setMap(null);
29732 options = Roo.apply({}, options, {
29733 strokeColor: "#0000FF",
29734 strokeOpacity: .35,
29736 fillColor: "#0000FF",
29740 options.map = this.gMapContext.map;
29741 options.radius = radius;
29742 options.center = center;
29743 this.gMapContext.circle = new google.maps.Circle(options);
29744 return this.gMapContext.circle;
29750 setPosition: function(location)
29752 this.gMapContext.location = location;
29753 this.gMapContext.marker.setPosition(location);
29754 this.gMapContext.map.panTo(location);
29755 this.drawCircle(location, this.gMapContext.radius, {});
29759 if (this.gMapContext.settings.enableReverseGeocode) {
29760 this.gMapContext.geodecoder.geocode({
29761 latLng: this.gMapContext.location
29762 }, function(results, status) {
29764 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29765 _this.gMapContext.locationName = results[0].formatted_address;
29766 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29768 _this.fireEvent('positionchanged', this, location);
29775 this.fireEvent('positionchanged', this, location);
29780 google.maps.event.trigger(this.gMapContext.map, "resize");
29782 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29784 this.fireEvent('resize', this);
29787 setPositionByLatLng: function(latitude, longitude)
29789 this.setPosition(new google.maps.LatLng(latitude, longitude));
29792 getCurrentPosition: function()
29795 latitude: this.gMapContext.location.lat(),
29796 longitude: this.gMapContext.location.lng()
29800 getAddressName: function()
29802 return this.gMapContext.locationName;
29805 getAddressComponents: function()
29807 return this.gMapContext.addressComponents;
29810 address_component_from_google_geocode: function(address_components)
29814 for (var i = 0; i < address_components.length; i++) {
29815 var component = address_components[i];
29816 if (component.types.indexOf("postal_code") >= 0) {
29817 result.postalCode = component.short_name;
29818 } else if (component.types.indexOf("street_number") >= 0) {
29819 result.streetNumber = component.short_name;
29820 } else if (component.types.indexOf("route") >= 0) {
29821 result.streetName = component.short_name;
29822 } else if (component.types.indexOf("neighborhood") >= 0) {
29823 result.city = component.short_name;
29824 } else if (component.types.indexOf("locality") >= 0) {
29825 result.city = component.short_name;
29826 } else if (component.types.indexOf("sublocality") >= 0) {
29827 result.district = component.short_name;
29828 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29829 result.stateOrProvince = component.short_name;
29830 } else if (component.types.indexOf("country") >= 0) {
29831 result.country = component.short_name;
29835 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29836 result.addressLine2 = "";
29840 setZoomLevel: function(zoom)
29842 this.gMapContext.map.setZoom(zoom);
29855 this.fireEvent('show', this);
29866 this.fireEvent('hide', this);
29871 Roo.apply(Roo.bootstrap.LocationPicker, {
29873 OverlayView : function(map, options)
29875 options = options || {};
29882 * @class Roo.bootstrap.Alert
29883 * @extends Roo.bootstrap.Component
29884 * Bootstrap Alert class - shows an alert area box
29886 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29887 Enter a valid email address
29890 * @cfg {String} title The title of alert
29891 * @cfg {String} html The content of alert
29892 * @cfg {String} weight ( success | info | warning | danger )
29893 * @cfg {String} fa font-awesomeicon
29894 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29895 * @cfg {Boolean} close true to show a x closer
29899 * Create a new alert
29900 * @param {Object} config The config object
29904 Roo.bootstrap.Alert = function(config){
29905 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29909 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29915 faicon: false, // BC
29919 getAutoCreate : function()
29931 style : this.close ? '' : 'display:none'
29935 cls : 'roo-alert-icon'
29940 cls : 'roo-alert-title',
29945 cls : 'roo-alert-text',
29952 cfg.cn[0].cls += ' fa ' + this.faicon;
29955 cfg.cn[0].cls += ' fa ' + this.fa;
29959 cfg.cls += ' alert-' + this.weight;
29965 initEvents: function()
29967 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29968 this.titleEl = this.el.select('.roo-alert-title',true).first();
29969 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29970 if (this.seconds > 0) {
29971 this.hide.defer(this.seconds, this);
29975 setTitle : function(str)
29977 this.titleEl.dom.innerHTML = str;
29980 setText : function(str)
29982 this.titleEl.dom.innerHTML = str;
29985 setWeight : function(weight)
29988 this.el.removeClass('alert-' + this.weight);
29991 this.weight = weight;
29993 this.el.addClass('alert-' + this.weight);
29996 setIcon : function(icon)
29999 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30002 this.faicon = icon;
30004 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30025 * @class Roo.bootstrap.UploadCropbox
30026 * @extends Roo.bootstrap.Component
30027 * Bootstrap UploadCropbox class
30028 * @cfg {String} emptyText show when image has been loaded
30029 * @cfg {String} rotateNotify show when image too small to rotate
30030 * @cfg {Number} errorTimeout default 3000
30031 * @cfg {Number} minWidth default 300
30032 * @cfg {Number} minHeight default 300
30033 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30034 * @cfg {Boolean} isDocument (true|false) default false
30035 * @cfg {String} url action url
30036 * @cfg {String} paramName default 'imageUpload'
30037 * @cfg {String} method default POST
30038 * @cfg {Boolean} loadMask (true|false) default true
30039 * @cfg {Boolean} loadingText default 'Loading...'
30042 * Create a new UploadCropbox
30043 * @param {Object} config The config object
30046 Roo.bootstrap.UploadCropbox = function(config){
30047 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30051 * @event beforeselectfile
30052 * Fire before select file
30053 * @param {Roo.bootstrap.UploadCropbox} this
30055 "beforeselectfile" : true,
30058 * Fire after initEvent
30059 * @param {Roo.bootstrap.UploadCropbox} this
30064 * Fire after initEvent
30065 * @param {Roo.bootstrap.UploadCropbox} this
30066 * @param {String} data
30071 * Fire when preparing the file data
30072 * @param {Roo.bootstrap.UploadCropbox} this
30073 * @param {Object} file
30078 * Fire when get exception
30079 * @param {Roo.bootstrap.UploadCropbox} this
30080 * @param {XMLHttpRequest} xhr
30082 "exception" : true,
30084 * @event beforeloadcanvas
30085 * Fire before load the canvas
30086 * @param {Roo.bootstrap.UploadCropbox} this
30087 * @param {String} src
30089 "beforeloadcanvas" : true,
30092 * Fire when trash image
30093 * @param {Roo.bootstrap.UploadCropbox} this
30098 * Fire when download the image
30099 * @param {Roo.bootstrap.UploadCropbox} this
30103 * @event footerbuttonclick
30104 * Fire when footerbuttonclick
30105 * @param {Roo.bootstrap.UploadCropbox} this
30106 * @param {String} type
30108 "footerbuttonclick" : true,
30112 * @param {Roo.bootstrap.UploadCropbox} this
30117 * Fire when rotate the image
30118 * @param {Roo.bootstrap.UploadCropbox} this
30119 * @param {String} pos
30124 * Fire when inspect the file
30125 * @param {Roo.bootstrap.UploadCropbox} this
30126 * @param {Object} file
30131 * Fire when xhr upload the file
30132 * @param {Roo.bootstrap.UploadCropbox} this
30133 * @param {Object} data
30138 * Fire when arrange the file data
30139 * @param {Roo.bootstrap.UploadCropbox} this
30140 * @param {Object} formData
30145 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30148 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30150 emptyText : 'Click to upload image',
30151 rotateNotify : 'Image is too small to rotate',
30152 errorTimeout : 3000,
30166 cropType : 'image/jpeg',
30168 canvasLoaded : false,
30169 isDocument : false,
30171 paramName : 'imageUpload',
30173 loadingText : 'Loading...',
30176 getAutoCreate : function()
30180 cls : 'roo-upload-cropbox',
30184 cls : 'roo-upload-cropbox-selector',
30189 cls : 'roo-upload-cropbox-body',
30190 style : 'cursor:pointer',
30194 cls : 'roo-upload-cropbox-preview'
30198 cls : 'roo-upload-cropbox-thumb'
30202 cls : 'roo-upload-cropbox-empty-notify',
30203 html : this.emptyText
30207 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30208 html : this.rotateNotify
30214 cls : 'roo-upload-cropbox-footer',
30217 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30227 onRender : function(ct, position)
30229 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30231 if (this.buttons.length) {
30233 Roo.each(this.buttons, function(bb) {
30235 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30237 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30243 this.maskEl = this.el;
30247 initEvents : function()
30249 this.urlAPI = (window.createObjectURL && window) ||
30250 (window.URL && URL.revokeObjectURL && URL) ||
30251 (window.webkitURL && webkitURL);
30253 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30254 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30256 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30257 this.selectorEl.hide();
30259 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30260 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30263 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30264 this.thumbEl.hide();
30266 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30267 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30269 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30270 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30271 this.errorEl.hide();
30273 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30274 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30275 this.footerEl.hide();
30277 this.setThumbBoxSize();
30283 this.fireEvent('initial', this);
30290 window.addEventListener("resize", function() { _this.resize(); } );
30292 this.bodyEl.on('click', this.beforeSelectFile, this);
30295 this.bodyEl.on('touchstart', this.onTouchStart, this);
30296 this.bodyEl.on('touchmove', this.onTouchMove, this);
30297 this.bodyEl.on('touchend', this.onTouchEnd, this);
30301 this.bodyEl.on('mousedown', this.onMouseDown, this);
30302 this.bodyEl.on('mousemove', this.onMouseMove, this);
30303 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30304 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30305 Roo.get(document).on('mouseup', this.onMouseUp, this);
30308 this.selectorEl.on('change', this.onFileSelected, this);
30314 this.baseScale = 1;
30316 this.baseRotate = 1;
30317 this.dragable = false;
30318 this.pinching = false;
30321 this.cropData = false;
30322 this.notifyEl.dom.innerHTML = this.emptyText;
30324 this.selectorEl.dom.value = '';
30328 resize : function()
30330 if(this.fireEvent('resize', this) != false){
30331 this.setThumbBoxPosition();
30332 this.setCanvasPosition();
30336 onFooterButtonClick : function(e, el, o, type)
30339 case 'rotate-left' :
30340 this.onRotateLeft(e);
30342 case 'rotate-right' :
30343 this.onRotateRight(e);
30346 this.beforeSelectFile(e);
30361 this.fireEvent('footerbuttonclick', this, type);
30364 beforeSelectFile : function(e)
30366 e.preventDefault();
30368 if(this.fireEvent('beforeselectfile', this) != false){
30369 this.selectorEl.dom.click();
30373 onFileSelected : function(e)
30375 e.preventDefault();
30377 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30381 var file = this.selectorEl.dom.files[0];
30383 if(this.fireEvent('inspect', this, file) != false){
30384 this.prepare(file);
30389 trash : function(e)
30391 this.fireEvent('trash', this);
30394 download : function(e)
30396 this.fireEvent('download', this);
30399 loadCanvas : function(src)
30401 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30405 this.imageEl = document.createElement('img');
30409 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30411 this.imageEl.src = src;
30415 onLoadCanvas : function()
30417 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30418 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30420 this.bodyEl.un('click', this.beforeSelectFile, this);
30422 this.notifyEl.hide();
30423 this.thumbEl.show();
30424 this.footerEl.show();
30426 this.baseRotateLevel();
30428 if(this.isDocument){
30429 this.setThumbBoxSize();
30432 this.setThumbBoxPosition();
30434 this.baseScaleLevel();
30440 this.canvasLoaded = true;
30443 this.maskEl.unmask();
30448 setCanvasPosition : function()
30450 if(!this.canvasEl){
30454 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30455 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30457 this.previewEl.setLeft(pw);
30458 this.previewEl.setTop(ph);
30462 onMouseDown : function(e)
30466 this.dragable = true;
30467 this.pinching = false;
30469 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30470 this.dragable = false;
30474 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30475 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30479 onMouseMove : function(e)
30483 if(!this.canvasLoaded){
30487 if (!this.dragable){
30491 var minX = Math.ceil(this.thumbEl.getLeft(true));
30492 var minY = Math.ceil(this.thumbEl.getTop(true));
30494 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30495 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30497 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30498 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30500 x = x - this.mouseX;
30501 y = y - this.mouseY;
30503 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30504 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30506 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30507 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30509 this.previewEl.setLeft(bgX);
30510 this.previewEl.setTop(bgY);
30512 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30513 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30516 onMouseUp : function(e)
30520 this.dragable = false;
30523 onMouseWheel : function(e)
30527 this.startScale = this.scale;
30529 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30531 if(!this.zoomable()){
30532 this.scale = this.startScale;
30541 zoomable : function()
30543 var minScale = this.thumbEl.getWidth() / this.minWidth;
30545 if(this.minWidth < this.minHeight){
30546 minScale = this.thumbEl.getHeight() / this.minHeight;
30549 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30550 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30554 (this.rotate == 0 || this.rotate == 180) &&
30556 width > this.imageEl.OriginWidth ||
30557 height > this.imageEl.OriginHeight ||
30558 (width < this.minWidth && height < this.minHeight)
30566 (this.rotate == 90 || this.rotate == 270) &&
30568 width > this.imageEl.OriginWidth ||
30569 height > this.imageEl.OriginHeight ||
30570 (width < this.minHeight && height < this.minWidth)
30577 !this.isDocument &&
30578 (this.rotate == 0 || this.rotate == 180) &&
30580 width < this.minWidth ||
30581 width > this.imageEl.OriginWidth ||
30582 height < this.minHeight ||
30583 height > this.imageEl.OriginHeight
30590 !this.isDocument &&
30591 (this.rotate == 90 || this.rotate == 270) &&
30593 width < this.minHeight ||
30594 width > this.imageEl.OriginWidth ||
30595 height < this.minWidth ||
30596 height > this.imageEl.OriginHeight
30606 onRotateLeft : function(e)
30608 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30610 var minScale = this.thumbEl.getWidth() / this.minWidth;
30612 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30613 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30615 this.startScale = this.scale;
30617 while (this.getScaleLevel() < minScale){
30619 this.scale = this.scale + 1;
30621 if(!this.zoomable()){
30626 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30627 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30632 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30639 this.scale = this.startScale;
30641 this.onRotateFail();
30646 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30648 if(this.isDocument){
30649 this.setThumbBoxSize();
30650 this.setThumbBoxPosition();
30651 this.setCanvasPosition();
30656 this.fireEvent('rotate', this, 'left');
30660 onRotateRight : function(e)
30662 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30664 var minScale = this.thumbEl.getWidth() / this.minWidth;
30666 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30667 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30669 this.startScale = this.scale;
30671 while (this.getScaleLevel() < minScale){
30673 this.scale = this.scale + 1;
30675 if(!this.zoomable()){
30680 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30681 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30686 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30693 this.scale = this.startScale;
30695 this.onRotateFail();
30700 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30702 if(this.isDocument){
30703 this.setThumbBoxSize();
30704 this.setThumbBoxPosition();
30705 this.setCanvasPosition();
30710 this.fireEvent('rotate', this, 'right');
30713 onRotateFail : function()
30715 this.errorEl.show(true);
30719 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30724 this.previewEl.dom.innerHTML = '';
30726 var canvasEl = document.createElement("canvas");
30728 var contextEl = canvasEl.getContext("2d");
30730 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30731 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30732 var center = this.imageEl.OriginWidth / 2;
30734 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30735 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30736 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30737 center = this.imageEl.OriginHeight / 2;
30740 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30742 contextEl.translate(center, center);
30743 contextEl.rotate(this.rotate * Math.PI / 180);
30745 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30747 this.canvasEl = document.createElement("canvas");
30749 this.contextEl = this.canvasEl.getContext("2d");
30751 switch (this.rotate) {
30754 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30755 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30757 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30762 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30763 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30765 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30766 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);
30770 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30775 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30776 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30778 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30779 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);
30783 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);
30788 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30789 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30791 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30792 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30796 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);
30803 this.previewEl.appendChild(this.canvasEl);
30805 this.setCanvasPosition();
30810 if(!this.canvasLoaded){
30814 var imageCanvas = document.createElement("canvas");
30816 var imageContext = imageCanvas.getContext("2d");
30818 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30819 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30821 var center = imageCanvas.width / 2;
30823 imageContext.translate(center, center);
30825 imageContext.rotate(this.rotate * Math.PI / 180);
30827 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30829 var canvas = document.createElement("canvas");
30831 var context = canvas.getContext("2d");
30833 canvas.width = this.minWidth;
30834 canvas.height = this.minHeight;
30836 switch (this.rotate) {
30839 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30840 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30842 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30843 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30845 var targetWidth = this.minWidth - 2 * x;
30846 var targetHeight = this.minHeight - 2 * y;
30850 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30851 scale = targetWidth / width;
30854 if(x > 0 && y == 0){
30855 scale = targetHeight / height;
30858 if(x > 0 && y > 0){
30859 scale = targetWidth / width;
30861 if(width < height){
30862 scale = targetHeight / height;
30866 context.scale(scale, scale);
30868 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30869 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30871 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30872 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30874 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30879 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30880 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30882 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30883 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30885 var targetWidth = this.minWidth - 2 * x;
30886 var targetHeight = this.minHeight - 2 * y;
30890 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30891 scale = targetWidth / width;
30894 if(x > 0 && y == 0){
30895 scale = targetHeight / height;
30898 if(x > 0 && y > 0){
30899 scale = targetWidth / width;
30901 if(width < height){
30902 scale = targetHeight / height;
30906 context.scale(scale, scale);
30908 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30909 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30911 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30912 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30914 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30916 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30921 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30922 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30924 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30925 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30927 var targetWidth = this.minWidth - 2 * x;
30928 var targetHeight = this.minHeight - 2 * y;
30932 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30933 scale = targetWidth / width;
30936 if(x > 0 && y == 0){
30937 scale = targetHeight / height;
30940 if(x > 0 && y > 0){
30941 scale = targetWidth / width;
30943 if(width < height){
30944 scale = targetHeight / height;
30948 context.scale(scale, scale);
30950 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30951 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30953 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30954 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30956 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30957 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30959 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30964 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30965 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30967 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30968 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30970 var targetWidth = this.minWidth - 2 * x;
30971 var targetHeight = this.minHeight - 2 * y;
30975 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30976 scale = targetWidth / width;
30979 if(x > 0 && y == 0){
30980 scale = targetHeight / height;
30983 if(x > 0 && y > 0){
30984 scale = targetWidth / width;
30986 if(width < height){
30987 scale = targetHeight / height;
30991 context.scale(scale, scale);
30993 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30994 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30996 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30997 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30999 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31001 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31008 this.cropData = canvas.toDataURL(this.cropType);
31010 if(this.fireEvent('crop', this, this.cropData) !== false){
31011 this.process(this.file, this.cropData);
31018 setThumbBoxSize : function()
31022 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31023 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31024 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31026 this.minWidth = width;
31027 this.minHeight = height;
31029 if(this.rotate == 90 || this.rotate == 270){
31030 this.minWidth = height;
31031 this.minHeight = width;
31036 width = Math.ceil(this.minWidth * height / this.minHeight);
31038 if(this.minWidth > this.minHeight){
31040 height = Math.ceil(this.minHeight * width / this.minWidth);
31043 this.thumbEl.setStyle({
31044 width : width + 'px',
31045 height : height + 'px'
31052 setThumbBoxPosition : function()
31054 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31055 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31057 this.thumbEl.setLeft(x);
31058 this.thumbEl.setTop(y);
31062 baseRotateLevel : function()
31064 this.baseRotate = 1;
31067 typeof(this.exif) != 'undefined' &&
31068 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31069 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31071 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31074 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31078 baseScaleLevel : function()
31082 if(this.isDocument){
31084 if(this.baseRotate == 6 || this.baseRotate == 8){
31086 height = this.thumbEl.getHeight();
31087 this.baseScale = height / this.imageEl.OriginWidth;
31089 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31090 width = this.thumbEl.getWidth();
31091 this.baseScale = width / this.imageEl.OriginHeight;
31097 height = this.thumbEl.getHeight();
31098 this.baseScale = height / this.imageEl.OriginHeight;
31100 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31101 width = this.thumbEl.getWidth();
31102 this.baseScale = width / this.imageEl.OriginWidth;
31108 if(this.baseRotate == 6 || this.baseRotate == 8){
31110 width = this.thumbEl.getHeight();
31111 this.baseScale = width / this.imageEl.OriginHeight;
31113 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31114 height = this.thumbEl.getWidth();
31115 this.baseScale = height / this.imageEl.OriginHeight;
31118 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31119 height = this.thumbEl.getWidth();
31120 this.baseScale = height / this.imageEl.OriginHeight;
31122 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31123 width = this.thumbEl.getHeight();
31124 this.baseScale = width / this.imageEl.OriginWidth;
31131 width = this.thumbEl.getWidth();
31132 this.baseScale = width / this.imageEl.OriginWidth;
31134 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31135 height = this.thumbEl.getHeight();
31136 this.baseScale = height / this.imageEl.OriginHeight;
31139 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31141 height = this.thumbEl.getHeight();
31142 this.baseScale = height / this.imageEl.OriginHeight;
31144 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31145 width = this.thumbEl.getWidth();
31146 this.baseScale = width / this.imageEl.OriginWidth;
31154 getScaleLevel : function()
31156 return this.baseScale * Math.pow(1.1, this.scale);
31159 onTouchStart : function(e)
31161 if(!this.canvasLoaded){
31162 this.beforeSelectFile(e);
31166 var touches = e.browserEvent.touches;
31172 if(touches.length == 1){
31173 this.onMouseDown(e);
31177 if(touches.length != 2){
31183 for(var i = 0, finger; finger = touches[i]; i++){
31184 coords.push(finger.pageX, finger.pageY);
31187 var x = Math.pow(coords[0] - coords[2], 2);
31188 var y = Math.pow(coords[1] - coords[3], 2);
31190 this.startDistance = Math.sqrt(x + y);
31192 this.startScale = this.scale;
31194 this.pinching = true;
31195 this.dragable = false;
31199 onTouchMove : function(e)
31201 if(!this.pinching && !this.dragable){
31205 var touches = e.browserEvent.touches;
31212 this.onMouseMove(e);
31218 for(var i = 0, finger; finger = touches[i]; i++){
31219 coords.push(finger.pageX, finger.pageY);
31222 var x = Math.pow(coords[0] - coords[2], 2);
31223 var y = Math.pow(coords[1] - coords[3], 2);
31225 this.endDistance = Math.sqrt(x + y);
31227 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31229 if(!this.zoomable()){
31230 this.scale = this.startScale;
31238 onTouchEnd : function(e)
31240 this.pinching = false;
31241 this.dragable = false;
31245 process : function(file, crop)
31248 this.maskEl.mask(this.loadingText);
31251 this.xhr = new XMLHttpRequest();
31253 file.xhr = this.xhr;
31255 this.xhr.open(this.method, this.url, true);
31258 "Accept": "application/json",
31259 "Cache-Control": "no-cache",
31260 "X-Requested-With": "XMLHttpRequest"
31263 for (var headerName in headers) {
31264 var headerValue = headers[headerName];
31266 this.xhr.setRequestHeader(headerName, headerValue);
31272 this.xhr.onload = function()
31274 _this.xhrOnLoad(_this.xhr);
31277 this.xhr.onerror = function()
31279 _this.xhrOnError(_this.xhr);
31282 var formData = new FormData();
31284 formData.append('returnHTML', 'NO');
31287 formData.append('crop', crop);
31290 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31291 formData.append(this.paramName, file, file.name);
31294 if(typeof(file.filename) != 'undefined'){
31295 formData.append('filename', file.filename);
31298 if(typeof(file.mimetype) != 'undefined'){
31299 formData.append('mimetype', file.mimetype);
31302 if(this.fireEvent('arrange', this, formData) != false){
31303 this.xhr.send(formData);
31307 xhrOnLoad : function(xhr)
31310 this.maskEl.unmask();
31313 if (xhr.readyState !== 4) {
31314 this.fireEvent('exception', this, xhr);
31318 var response = Roo.decode(xhr.responseText);
31320 if(!response.success){
31321 this.fireEvent('exception', this, xhr);
31325 var response = Roo.decode(xhr.responseText);
31327 this.fireEvent('upload', this, response);
31331 xhrOnError : function()
31334 this.maskEl.unmask();
31337 Roo.log('xhr on error');
31339 var response = Roo.decode(xhr.responseText);
31345 prepare : function(file)
31348 this.maskEl.mask(this.loadingText);
31354 if(typeof(file) === 'string'){
31355 this.loadCanvas(file);
31359 if(!file || !this.urlAPI){
31364 this.cropType = file.type;
31368 if(this.fireEvent('prepare', this, this.file) != false){
31370 var reader = new FileReader();
31372 reader.onload = function (e) {
31373 if (e.target.error) {
31374 Roo.log(e.target.error);
31378 var buffer = e.target.result,
31379 dataView = new DataView(buffer),
31381 maxOffset = dataView.byteLength - 4,
31385 if (dataView.getUint16(0) === 0xffd8) {
31386 while (offset < maxOffset) {
31387 markerBytes = dataView.getUint16(offset);
31389 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31390 markerLength = dataView.getUint16(offset + 2) + 2;
31391 if (offset + markerLength > dataView.byteLength) {
31392 Roo.log('Invalid meta data: Invalid segment size.');
31396 if(markerBytes == 0xffe1){
31397 _this.parseExifData(
31404 offset += markerLength;
31414 var url = _this.urlAPI.createObjectURL(_this.file);
31416 _this.loadCanvas(url);
31421 reader.readAsArrayBuffer(this.file);
31427 parseExifData : function(dataView, offset, length)
31429 var tiffOffset = offset + 10,
31433 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31434 // No Exif data, might be XMP data instead
31438 // Check for the ASCII code for "Exif" (0x45786966):
31439 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31440 // No Exif data, might be XMP data instead
31443 if (tiffOffset + 8 > dataView.byteLength) {
31444 Roo.log('Invalid Exif data: Invalid segment size.');
31447 // Check for the two null bytes:
31448 if (dataView.getUint16(offset + 8) !== 0x0000) {
31449 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31452 // Check the byte alignment:
31453 switch (dataView.getUint16(tiffOffset)) {
31455 littleEndian = true;
31458 littleEndian = false;
31461 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31464 // Check for the TIFF tag marker (0x002A):
31465 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31466 Roo.log('Invalid Exif data: Missing TIFF marker.');
31469 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31470 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31472 this.parseExifTags(
31475 tiffOffset + dirOffset,
31480 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31485 if (dirOffset + 6 > dataView.byteLength) {
31486 Roo.log('Invalid Exif data: Invalid directory offset.');
31489 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31490 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31491 if (dirEndOffset + 4 > dataView.byteLength) {
31492 Roo.log('Invalid Exif data: Invalid directory size.');
31495 for (i = 0; i < tagsNumber; i += 1) {
31499 dirOffset + 2 + 12 * i, // tag offset
31503 // Return the offset to the next directory:
31504 return dataView.getUint32(dirEndOffset, littleEndian);
31507 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31509 var tag = dataView.getUint16(offset, littleEndian);
31511 this.exif[tag] = this.getExifValue(
31515 dataView.getUint16(offset + 2, littleEndian), // tag type
31516 dataView.getUint32(offset + 4, littleEndian), // tag length
31521 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31523 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31532 Roo.log('Invalid Exif data: Invalid tag type.');
31536 tagSize = tagType.size * length;
31537 // Determine if the value is contained in the dataOffset bytes,
31538 // or if the value at the dataOffset is a pointer to the actual data:
31539 dataOffset = tagSize > 4 ?
31540 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31541 if (dataOffset + tagSize > dataView.byteLength) {
31542 Roo.log('Invalid Exif data: Invalid data offset.');
31545 if (length === 1) {
31546 return tagType.getValue(dataView, dataOffset, littleEndian);
31549 for (i = 0; i < length; i += 1) {
31550 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31553 if (tagType.ascii) {
31555 // Concatenate the chars:
31556 for (i = 0; i < values.length; i += 1) {
31558 // Ignore the terminating NULL byte(s):
31559 if (c === '\u0000') {
31571 Roo.apply(Roo.bootstrap.UploadCropbox, {
31573 'Orientation': 0x0112
31577 1: 0, //'top-left',
31579 3: 180, //'bottom-right',
31580 // 4: 'bottom-left',
31582 6: 90, //'right-top',
31583 // 7: 'right-bottom',
31584 8: 270 //'left-bottom'
31588 // byte, 8-bit unsigned int:
31590 getValue: function (dataView, dataOffset) {
31591 return dataView.getUint8(dataOffset);
31595 // ascii, 8-bit byte:
31597 getValue: function (dataView, dataOffset) {
31598 return String.fromCharCode(dataView.getUint8(dataOffset));
31603 // short, 16 bit int:
31605 getValue: function (dataView, dataOffset, littleEndian) {
31606 return dataView.getUint16(dataOffset, littleEndian);
31610 // long, 32 bit int:
31612 getValue: function (dataView, dataOffset, littleEndian) {
31613 return dataView.getUint32(dataOffset, littleEndian);
31617 // rational = two long values, first is numerator, second is denominator:
31619 getValue: function (dataView, dataOffset, littleEndian) {
31620 return dataView.getUint32(dataOffset, littleEndian) /
31621 dataView.getUint32(dataOffset + 4, littleEndian);
31625 // slong, 32 bit signed int:
31627 getValue: function (dataView, dataOffset, littleEndian) {
31628 return dataView.getInt32(dataOffset, littleEndian);
31632 // srational, two slongs, first is numerator, second is denominator:
31634 getValue: function (dataView, dataOffset, littleEndian) {
31635 return dataView.getInt32(dataOffset, littleEndian) /
31636 dataView.getInt32(dataOffset + 4, littleEndian);
31646 cls : 'btn-group roo-upload-cropbox-rotate-left',
31647 action : 'rotate-left',
31651 cls : 'btn btn-default',
31652 html : '<i class="fa fa-undo"></i>'
31658 cls : 'btn-group roo-upload-cropbox-picture',
31659 action : 'picture',
31663 cls : 'btn btn-default',
31664 html : '<i class="fa fa-picture-o"></i>'
31670 cls : 'btn-group roo-upload-cropbox-rotate-right',
31671 action : 'rotate-right',
31675 cls : 'btn btn-default',
31676 html : '<i class="fa fa-repeat"></i>'
31684 cls : 'btn-group roo-upload-cropbox-rotate-left',
31685 action : 'rotate-left',
31689 cls : 'btn btn-default',
31690 html : '<i class="fa fa-undo"></i>'
31696 cls : 'btn-group roo-upload-cropbox-download',
31697 action : 'download',
31701 cls : 'btn btn-default',
31702 html : '<i class="fa fa-download"></i>'
31708 cls : 'btn-group roo-upload-cropbox-crop',
31713 cls : 'btn btn-default',
31714 html : '<i class="fa fa-crop"></i>'
31720 cls : 'btn-group roo-upload-cropbox-trash',
31725 cls : 'btn btn-default',
31726 html : '<i class="fa fa-trash"></i>'
31732 cls : 'btn-group roo-upload-cropbox-rotate-right',
31733 action : 'rotate-right',
31737 cls : 'btn btn-default',
31738 html : '<i class="fa fa-repeat"></i>'
31746 cls : 'btn-group roo-upload-cropbox-rotate-left',
31747 action : 'rotate-left',
31751 cls : 'btn btn-default',
31752 html : '<i class="fa fa-undo"></i>'
31758 cls : 'btn-group roo-upload-cropbox-rotate-right',
31759 action : 'rotate-right',
31763 cls : 'btn btn-default',
31764 html : '<i class="fa fa-repeat"></i>'
31777 * @class Roo.bootstrap.DocumentManager
31778 * @extends Roo.bootstrap.Component
31779 * Bootstrap DocumentManager class
31780 * @cfg {String} paramName default 'imageUpload'
31781 * @cfg {String} toolTipName default 'filename'
31782 * @cfg {String} method default POST
31783 * @cfg {String} url action url
31784 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31785 * @cfg {Boolean} multiple multiple upload default true
31786 * @cfg {Number} thumbSize default 300
31787 * @cfg {String} fieldLabel
31788 * @cfg {Number} labelWidth default 4
31789 * @cfg {String} labelAlign (left|top) default left
31790 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31791 * @cfg {Number} labellg set the width of label (1-12)
31792 * @cfg {Number} labelmd set the width of label (1-12)
31793 * @cfg {Number} labelsm set the width of label (1-12)
31794 * @cfg {Number} labelxs set the width of label (1-12)
31797 * Create a new DocumentManager
31798 * @param {Object} config The config object
31801 Roo.bootstrap.DocumentManager = function(config){
31802 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31805 this.delegates = [];
31810 * Fire when initial the DocumentManager
31811 * @param {Roo.bootstrap.DocumentManager} this
31816 * inspect selected file
31817 * @param {Roo.bootstrap.DocumentManager} this
31818 * @param {File} file
31823 * Fire when xhr load exception
31824 * @param {Roo.bootstrap.DocumentManager} this
31825 * @param {XMLHttpRequest} xhr
31827 "exception" : true,
31829 * @event afterupload
31830 * Fire when xhr load exception
31831 * @param {Roo.bootstrap.DocumentManager} this
31832 * @param {XMLHttpRequest} xhr
31834 "afterupload" : true,
31837 * prepare the form data
31838 * @param {Roo.bootstrap.DocumentManager} this
31839 * @param {Object} formData
31844 * Fire when remove the file
31845 * @param {Roo.bootstrap.DocumentManager} this
31846 * @param {Object} file
31851 * Fire after refresh the file
31852 * @param {Roo.bootstrap.DocumentManager} this
31857 * Fire after click the image
31858 * @param {Roo.bootstrap.DocumentManager} this
31859 * @param {Object} file
31864 * Fire when upload a image and editable set to true
31865 * @param {Roo.bootstrap.DocumentManager} this
31866 * @param {Object} file
31870 * @event beforeselectfile
31871 * Fire before select file
31872 * @param {Roo.bootstrap.DocumentManager} this
31874 "beforeselectfile" : true,
31877 * Fire before process file
31878 * @param {Roo.bootstrap.DocumentManager} this
31879 * @param {Object} file
31883 * @event previewrendered
31884 * Fire when preview rendered
31885 * @param {Roo.bootstrap.DocumentManager} this
31886 * @param {Object} file
31888 "previewrendered" : true,
31891 "previewResize" : true
31896 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31905 paramName : 'imageUpload',
31906 toolTipName : 'filename',
31909 labelAlign : 'left',
31919 getAutoCreate : function()
31921 var managerWidget = {
31923 cls : 'roo-document-manager',
31927 cls : 'roo-document-manager-selector',
31932 cls : 'roo-document-manager-uploader',
31936 cls : 'roo-document-manager-upload-btn',
31937 html : '<i class="fa fa-plus"></i>'
31948 cls : 'column col-md-12',
31953 if(this.fieldLabel.length){
31958 cls : 'column col-md-12',
31959 html : this.fieldLabel
31963 cls : 'column col-md-12',
31968 if(this.labelAlign == 'left'){
31973 html : this.fieldLabel
31982 if(this.labelWidth > 12){
31983 content[0].style = "width: " + this.labelWidth + 'px';
31986 if(this.labelWidth < 13 && this.labelmd == 0){
31987 this.labelmd = this.labelWidth;
31990 if(this.labellg > 0){
31991 content[0].cls += ' col-lg-' + this.labellg;
31992 content[1].cls += ' col-lg-' + (12 - this.labellg);
31995 if(this.labelmd > 0){
31996 content[0].cls += ' col-md-' + this.labelmd;
31997 content[1].cls += ' col-md-' + (12 - this.labelmd);
32000 if(this.labelsm > 0){
32001 content[0].cls += ' col-sm-' + this.labelsm;
32002 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32005 if(this.labelxs > 0){
32006 content[0].cls += ' col-xs-' + this.labelxs;
32007 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32015 cls : 'row clearfix',
32023 initEvents : function()
32025 this.managerEl = this.el.select('.roo-document-manager', true).first();
32026 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32028 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32029 this.selectorEl.hide();
32032 this.selectorEl.attr('multiple', 'multiple');
32035 this.selectorEl.on('change', this.onFileSelected, this);
32037 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32038 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32040 this.uploader.on('click', this.onUploaderClick, this);
32042 this.renderProgressDialog();
32046 window.addEventListener("resize", function() { _this.refresh(); } );
32048 this.fireEvent('initial', this);
32051 renderProgressDialog : function()
32055 this.progressDialog = new Roo.bootstrap.Modal({
32056 cls : 'roo-document-manager-progress-dialog',
32057 allow_close : false,
32068 btnclick : function() {
32069 _this.uploadCancel();
32075 this.progressDialog.render(Roo.get(document.body));
32077 this.progress = new Roo.bootstrap.Progress({
32078 cls : 'roo-document-manager-progress',
32083 this.progress.render(this.progressDialog.getChildContainer());
32085 this.progressBar = new Roo.bootstrap.ProgressBar({
32086 cls : 'roo-document-manager-progress-bar',
32089 aria_valuemax : 12,
32093 this.progressBar.render(this.progress.getChildContainer());
32096 onUploaderClick : function(e)
32098 e.preventDefault();
32100 if(this.fireEvent('beforeselectfile', this) != false){
32101 this.selectorEl.dom.click();
32106 onFileSelected : function(e)
32108 e.preventDefault();
32110 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32114 Roo.each(this.selectorEl.dom.files, function(file){
32115 if(this.fireEvent('inspect', this, file) != false){
32116 this.files.push(file);
32126 this.selectorEl.dom.value = '';
32128 if(!this.files || !this.files.length){
32132 if(this.boxes > 0 && this.files.length > this.boxes){
32133 this.files = this.files.slice(0, this.boxes);
32136 this.uploader.show();
32138 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32139 this.uploader.hide();
32148 Roo.each(this.files, function(file){
32150 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32151 var f = this.renderPreview(file);
32156 if(file.type.indexOf('image') != -1){
32157 this.delegates.push(
32159 _this.process(file);
32160 }).createDelegate(this)
32168 _this.process(file);
32169 }).createDelegate(this)
32174 this.files = files;
32176 this.delegates = this.delegates.concat(docs);
32178 if(!this.delegates.length){
32183 this.progressBar.aria_valuemax = this.delegates.length;
32190 arrange : function()
32192 if(!this.delegates.length){
32193 this.progressDialog.hide();
32198 var delegate = this.delegates.shift();
32200 this.progressDialog.show();
32202 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32204 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32209 refresh : function()
32211 this.uploader.show();
32213 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32214 this.uploader.hide();
32217 Roo.isTouch ? this.closable(false) : this.closable(true);
32219 this.fireEvent('refresh', this);
32222 onRemove : function(e, el, o)
32224 e.preventDefault();
32226 this.fireEvent('remove', this, o);
32230 remove : function(o)
32234 Roo.each(this.files, function(file){
32235 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32244 this.files = files;
32251 Roo.each(this.files, function(file){
32256 file.target.remove();
32265 onClick : function(e, el, o)
32267 e.preventDefault();
32269 this.fireEvent('click', this, o);
32273 closable : function(closable)
32275 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32277 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32289 xhrOnLoad : function(xhr)
32291 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32295 if (xhr.readyState !== 4) {
32297 this.fireEvent('exception', this, xhr);
32301 var response = Roo.decode(xhr.responseText);
32303 if(!response.success){
32305 this.fireEvent('exception', this, xhr);
32309 var file = this.renderPreview(response.data);
32311 this.files.push(file);
32315 this.fireEvent('afterupload', this, xhr);
32319 xhrOnError : function(xhr)
32321 Roo.log('xhr on error');
32323 var response = Roo.decode(xhr.responseText);
32330 process : function(file)
32332 if(this.fireEvent('process', this, file) !== false){
32333 if(this.editable && file.type.indexOf('image') != -1){
32334 this.fireEvent('edit', this, file);
32338 this.uploadStart(file, false);
32345 uploadStart : function(file, crop)
32347 this.xhr = new XMLHttpRequest();
32349 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32354 file.xhr = this.xhr;
32356 this.managerEl.createChild({
32358 cls : 'roo-document-manager-loading',
32362 tooltip : file.name,
32363 cls : 'roo-document-manager-thumb',
32364 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32370 this.xhr.open(this.method, this.url, true);
32373 "Accept": "application/json",
32374 "Cache-Control": "no-cache",
32375 "X-Requested-With": "XMLHttpRequest"
32378 for (var headerName in headers) {
32379 var headerValue = headers[headerName];
32381 this.xhr.setRequestHeader(headerName, headerValue);
32387 this.xhr.onload = function()
32389 _this.xhrOnLoad(_this.xhr);
32392 this.xhr.onerror = function()
32394 _this.xhrOnError(_this.xhr);
32397 var formData = new FormData();
32399 formData.append('returnHTML', 'NO');
32402 formData.append('crop', crop);
32405 formData.append(this.paramName, file, file.name);
32412 if(this.fireEvent('prepare', this, formData, options) != false){
32414 if(options.manually){
32418 this.xhr.send(formData);
32422 this.uploadCancel();
32425 uploadCancel : function()
32431 this.delegates = [];
32433 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32440 renderPreview : function(file)
32442 if(typeof(file.target) != 'undefined' && file.target){
32446 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32448 var previewEl = this.managerEl.createChild({
32450 cls : 'roo-document-manager-preview',
32454 tooltip : file[this.toolTipName],
32455 cls : 'roo-document-manager-thumb',
32456 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32461 html : '<i class="fa fa-times-circle"></i>'
32466 var close = previewEl.select('button.close', true).first();
32468 close.on('click', this.onRemove, this, file);
32470 file.target = previewEl;
32472 var image = previewEl.select('img', true).first();
32476 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32478 image.on('click', this.onClick, this, file);
32480 this.fireEvent('previewrendered', this, file);
32486 onPreviewLoad : function(file, image)
32488 if(typeof(file.target) == 'undefined' || !file.target){
32492 var width = image.dom.naturalWidth || image.dom.width;
32493 var height = image.dom.naturalHeight || image.dom.height;
32495 if(!this.previewResize) {
32499 if(width > height){
32500 file.target.addClass('wide');
32504 file.target.addClass('tall');
32509 uploadFromSource : function(file, crop)
32511 this.xhr = new XMLHttpRequest();
32513 this.managerEl.createChild({
32515 cls : 'roo-document-manager-loading',
32519 tooltip : file.name,
32520 cls : 'roo-document-manager-thumb',
32521 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32527 this.xhr.open(this.method, this.url, true);
32530 "Accept": "application/json",
32531 "Cache-Control": "no-cache",
32532 "X-Requested-With": "XMLHttpRequest"
32535 for (var headerName in headers) {
32536 var headerValue = headers[headerName];
32538 this.xhr.setRequestHeader(headerName, headerValue);
32544 this.xhr.onload = function()
32546 _this.xhrOnLoad(_this.xhr);
32549 this.xhr.onerror = function()
32551 _this.xhrOnError(_this.xhr);
32554 var formData = new FormData();
32556 formData.append('returnHTML', 'NO');
32558 formData.append('crop', crop);
32560 if(typeof(file.filename) != 'undefined'){
32561 formData.append('filename', file.filename);
32564 if(typeof(file.mimetype) != 'undefined'){
32565 formData.append('mimetype', file.mimetype);
32570 if(this.fireEvent('prepare', this, formData) != false){
32571 this.xhr.send(formData);
32581 * @class Roo.bootstrap.DocumentViewer
32582 * @extends Roo.bootstrap.Component
32583 * Bootstrap DocumentViewer class
32584 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32585 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32588 * Create a new DocumentViewer
32589 * @param {Object} config The config object
32592 Roo.bootstrap.DocumentViewer = function(config){
32593 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32598 * Fire after initEvent
32599 * @param {Roo.bootstrap.DocumentViewer} this
32605 * @param {Roo.bootstrap.DocumentViewer} this
32610 * Fire after download button
32611 * @param {Roo.bootstrap.DocumentViewer} this
32616 * Fire after trash button
32617 * @param {Roo.bootstrap.DocumentViewer} this
32624 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32626 showDownload : true,
32630 getAutoCreate : function()
32634 cls : 'roo-document-viewer',
32638 cls : 'roo-document-viewer-body',
32642 cls : 'roo-document-viewer-thumb',
32646 cls : 'roo-document-viewer-image'
32654 cls : 'roo-document-viewer-footer',
32657 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32661 cls : 'btn-group roo-document-viewer-download',
32665 cls : 'btn btn-default',
32666 html : '<i class="fa fa-download"></i>'
32672 cls : 'btn-group roo-document-viewer-trash',
32676 cls : 'btn btn-default',
32677 html : '<i class="fa fa-trash"></i>'
32690 initEvents : function()
32692 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32693 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32695 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32696 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32698 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32699 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32701 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32702 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32704 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32705 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32707 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32708 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32710 this.bodyEl.on('click', this.onClick, this);
32711 this.downloadBtn.on('click', this.onDownload, this);
32712 this.trashBtn.on('click', this.onTrash, this);
32714 this.downloadBtn.hide();
32715 this.trashBtn.hide();
32717 if(this.showDownload){
32718 this.downloadBtn.show();
32721 if(this.showTrash){
32722 this.trashBtn.show();
32725 if(!this.showDownload && !this.showTrash) {
32726 this.footerEl.hide();
32731 initial : function()
32733 this.fireEvent('initial', this);
32737 onClick : function(e)
32739 e.preventDefault();
32741 this.fireEvent('click', this);
32744 onDownload : function(e)
32746 e.preventDefault();
32748 this.fireEvent('download', this);
32751 onTrash : function(e)
32753 e.preventDefault();
32755 this.fireEvent('trash', this);
32767 * @class Roo.bootstrap.NavProgressBar
32768 * @extends Roo.bootstrap.Component
32769 * Bootstrap NavProgressBar class
32772 * Create a new nav progress bar
32773 * @param {Object} config The config object
32776 Roo.bootstrap.NavProgressBar = function(config){
32777 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32779 this.bullets = this.bullets || [];
32781 // Roo.bootstrap.NavProgressBar.register(this);
32785 * Fires when the active item changes
32786 * @param {Roo.bootstrap.NavProgressBar} this
32787 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32788 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32795 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32800 getAutoCreate : function()
32802 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32806 cls : 'roo-navigation-bar-group',
32810 cls : 'roo-navigation-top-bar'
32814 cls : 'roo-navigation-bullets-bar',
32818 cls : 'roo-navigation-bar'
32825 cls : 'roo-navigation-bottom-bar'
32835 initEvents: function()
32840 onRender : function(ct, position)
32842 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32844 if(this.bullets.length){
32845 Roo.each(this.bullets, function(b){
32854 addItem : function(cfg)
32856 var item = new Roo.bootstrap.NavProgressItem(cfg);
32858 item.parentId = this.id;
32859 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32862 var top = new Roo.bootstrap.Element({
32864 cls : 'roo-navigation-bar-text'
32867 var bottom = new Roo.bootstrap.Element({
32869 cls : 'roo-navigation-bar-text'
32872 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32873 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32875 var topText = new Roo.bootstrap.Element({
32877 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32880 var bottomText = new Roo.bootstrap.Element({
32882 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32885 topText.onRender(top.el, null);
32886 bottomText.onRender(bottom.el, null);
32889 item.bottomEl = bottom;
32892 this.barItems.push(item);
32897 getActive : function()
32899 var active = false;
32901 Roo.each(this.barItems, function(v){
32903 if (!v.isActive()) {
32915 setActiveItem : function(item)
32919 Roo.each(this.barItems, function(v){
32920 if (v.rid == item.rid) {
32924 if (v.isActive()) {
32925 v.setActive(false);
32930 item.setActive(true);
32932 this.fireEvent('changed', this, item, prev);
32935 getBarItem: function(rid)
32939 Roo.each(this.barItems, function(e) {
32940 if (e.rid != rid) {
32951 indexOfItem : function(item)
32955 Roo.each(this.barItems, function(v, i){
32957 if (v.rid != item.rid) {
32968 setActiveNext : function()
32970 var i = this.indexOfItem(this.getActive());
32972 if (i > this.barItems.length) {
32976 this.setActiveItem(this.barItems[i+1]);
32979 setActivePrev : function()
32981 var i = this.indexOfItem(this.getActive());
32987 this.setActiveItem(this.barItems[i-1]);
32990 format : function()
32992 if(!this.barItems.length){
32996 var width = 100 / this.barItems.length;
32998 Roo.each(this.barItems, function(i){
32999 i.el.setStyle('width', width + '%');
33000 i.topEl.el.setStyle('width', width + '%');
33001 i.bottomEl.el.setStyle('width', width + '%');
33010 * Nav Progress Item
33015 * @class Roo.bootstrap.NavProgressItem
33016 * @extends Roo.bootstrap.Component
33017 * Bootstrap NavProgressItem class
33018 * @cfg {String} rid the reference id
33019 * @cfg {Boolean} active (true|false) Is item active default false
33020 * @cfg {Boolean} disabled (true|false) Is item active default false
33021 * @cfg {String} html
33022 * @cfg {String} position (top|bottom) text position default bottom
33023 * @cfg {String} icon show icon instead of number
33026 * Create a new NavProgressItem
33027 * @param {Object} config The config object
33029 Roo.bootstrap.NavProgressItem = function(config){
33030 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33035 * The raw click event for the entire grid.
33036 * @param {Roo.bootstrap.NavProgressItem} this
33037 * @param {Roo.EventObject} e
33044 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33050 position : 'bottom',
33053 getAutoCreate : function()
33055 var iconCls = 'roo-navigation-bar-item-icon';
33057 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33061 cls: 'roo-navigation-bar-item',
33071 cfg.cls += ' active';
33074 cfg.cls += ' disabled';
33080 disable : function()
33082 this.setDisabled(true);
33085 enable : function()
33087 this.setDisabled(false);
33090 initEvents: function()
33092 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33094 this.iconEl.on('click', this.onClick, this);
33097 onClick : function(e)
33099 e.preventDefault();
33105 if(this.fireEvent('click', this, e) === false){
33109 this.parent().setActiveItem(this);
33112 isActive: function ()
33114 return this.active;
33117 setActive : function(state)
33119 if(this.active == state){
33123 this.active = state;
33126 this.el.addClass('active');
33130 this.el.removeClass('active');
33135 setDisabled : function(state)
33137 if(this.disabled == state){
33141 this.disabled = state;
33144 this.el.addClass('disabled');
33148 this.el.removeClass('disabled');
33151 tooltipEl : function()
33153 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33166 * @class Roo.bootstrap.FieldLabel
33167 * @extends Roo.bootstrap.Component
33168 * Bootstrap FieldLabel class
33169 * @cfg {String} html contents of the element
33170 * @cfg {String} tag tag of the element default label
33171 * @cfg {String} cls class of the element
33172 * @cfg {String} target label target
33173 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33174 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33175 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33176 * @cfg {String} iconTooltip default "This field is required"
33177 * @cfg {String} indicatorpos (left|right) default left
33180 * Create a new FieldLabel
33181 * @param {Object} config The config object
33184 Roo.bootstrap.FieldLabel = function(config){
33185 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33190 * Fires after the field has been marked as invalid.
33191 * @param {Roo.form.FieldLabel} this
33192 * @param {String} msg The validation message
33197 * Fires after the field has been validated with no errors.
33198 * @param {Roo.form.FieldLabel} this
33204 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33211 invalidClass : 'has-warning',
33212 validClass : 'has-success',
33213 iconTooltip : 'This field is required',
33214 indicatorpos : 'left',
33216 getAutoCreate : function(){
33219 if (!this.allowBlank) {
33225 cls : 'roo-bootstrap-field-label ' + this.cls,
33230 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33231 tooltip : this.iconTooltip
33240 if(this.indicatorpos == 'right'){
33243 cls : 'roo-bootstrap-field-label ' + this.cls,
33252 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33253 tooltip : this.iconTooltip
33262 initEvents: function()
33264 Roo.bootstrap.Element.superclass.initEvents.call(this);
33266 this.indicator = this.indicatorEl();
33268 if(this.indicator){
33269 this.indicator.removeClass('visible');
33270 this.indicator.addClass('invisible');
33273 Roo.bootstrap.FieldLabel.register(this);
33276 indicatorEl : function()
33278 var indicator = this.el.select('i.roo-required-indicator',true).first();
33289 * Mark this field as valid
33291 markValid : function()
33293 if(this.indicator){
33294 this.indicator.removeClass('visible');
33295 this.indicator.addClass('invisible');
33297 if (Roo.bootstrap.version == 3) {
33298 this.el.removeClass(this.invalidClass);
33299 this.el.addClass(this.validClass);
33301 this.el.removeClass('is-invalid');
33302 this.el.addClass('is-valid');
33306 this.fireEvent('valid', this);
33310 * Mark this field as invalid
33311 * @param {String} msg The validation message
33313 markInvalid : function(msg)
33315 if(this.indicator){
33316 this.indicator.removeClass('invisible');
33317 this.indicator.addClass('visible');
33319 if (Roo.bootstrap.version == 3) {
33320 this.el.removeClass(this.validClass);
33321 this.el.addClass(this.invalidClass);
33323 this.el.removeClass('is-valid');
33324 this.el.addClass('is-invalid');
33328 this.fireEvent('invalid', this, msg);
33334 Roo.apply(Roo.bootstrap.FieldLabel, {
33339 * register a FieldLabel Group
33340 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33342 register : function(label)
33344 if(this.groups.hasOwnProperty(label.target)){
33348 this.groups[label.target] = label;
33352 * fetch a FieldLabel Group based on the target
33353 * @param {string} target
33354 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33356 get: function(target) {
33357 if (typeof(this.groups[target]) == 'undefined') {
33361 return this.groups[target] ;
33370 * page DateSplitField.
33376 * @class Roo.bootstrap.DateSplitField
33377 * @extends Roo.bootstrap.Component
33378 * Bootstrap DateSplitField class
33379 * @cfg {string} fieldLabel - the label associated
33380 * @cfg {Number} labelWidth set the width of label (0-12)
33381 * @cfg {String} labelAlign (top|left)
33382 * @cfg {Boolean} dayAllowBlank (true|false) default false
33383 * @cfg {Boolean} monthAllowBlank (true|false) default false
33384 * @cfg {Boolean} yearAllowBlank (true|false) default false
33385 * @cfg {string} dayPlaceholder
33386 * @cfg {string} monthPlaceholder
33387 * @cfg {string} yearPlaceholder
33388 * @cfg {string} dayFormat default 'd'
33389 * @cfg {string} monthFormat default 'm'
33390 * @cfg {string} yearFormat default 'Y'
33391 * @cfg {Number} labellg set the width of label (1-12)
33392 * @cfg {Number} labelmd set the width of label (1-12)
33393 * @cfg {Number} labelsm set the width of label (1-12)
33394 * @cfg {Number} labelxs set the width of label (1-12)
33398 * Create a new DateSplitField
33399 * @param {Object} config The config object
33402 Roo.bootstrap.DateSplitField = function(config){
33403 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33409 * getting the data of years
33410 * @param {Roo.bootstrap.DateSplitField} this
33411 * @param {Object} years
33416 * getting the data of days
33417 * @param {Roo.bootstrap.DateSplitField} this
33418 * @param {Object} days
33423 * Fires after the field has been marked as invalid.
33424 * @param {Roo.form.Field} this
33425 * @param {String} msg The validation message
33430 * Fires after the field has been validated with no errors.
33431 * @param {Roo.form.Field} this
33437 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33440 labelAlign : 'top',
33442 dayAllowBlank : false,
33443 monthAllowBlank : false,
33444 yearAllowBlank : false,
33445 dayPlaceholder : '',
33446 monthPlaceholder : '',
33447 yearPlaceholder : '',
33451 isFormField : true,
33457 getAutoCreate : function()
33461 cls : 'row roo-date-split-field-group',
33466 cls : 'form-hidden-field roo-date-split-field-group-value',
33472 var labelCls = 'col-md-12';
33473 var contentCls = 'col-md-4';
33475 if(this.fieldLabel){
33479 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33483 html : this.fieldLabel
33488 if(this.labelAlign == 'left'){
33490 if(this.labelWidth > 12){
33491 label.style = "width: " + this.labelWidth + 'px';
33494 if(this.labelWidth < 13 && this.labelmd == 0){
33495 this.labelmd = this.labelWidth;
33498 if(this.labellg > 0){
33499 labelCls = ' col-lg-' + this.labellg;
33500 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33503 if(this.labelmd > 0){
33504 labelCls = ' col-md-' + this.labelmd;
33505 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33508 if(this.labelsm > 0){
33509 labelCls = ' col-sm-' + this.labelsm;
33510 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33513 if(this.labelxs > 0){
33514 labelCls = ' col-xs-' + this.labelxs;
33515 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33519 label.cls += ' ' + labelCls;
33521 cfg.cn.push(label);
33524 Roo.each(['day', 'month', 'year'], function(t){
33527 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33534 inputEl: function ()
33536 return this.el.select('.roo-date-split-field-group-value', true).first();
33539 onRender : function(ct, position)
33543 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33545 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33547 this.dayField = new Roo.bootstrap.ComboBox({
33548 allowBlank : this.dayAllowBlank,
33549 alwaysQuery : true,
33550 displayField : 'value',
33553 forceSelection : true,
33555 placeholder : this.dayPlaceholder,
33556 selectOnFocus : true,
33557 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33558 triggerAction : 'all',
33560 valueField : 'value',
33561 store : new Roo.data.SimpleStore({
33562 data : (function() {
33564 _this.fireEvent('days', _this, days);
33567 fields : [ 'value' ]
33570 select : function (_self, record, index)
33572 _this.setValue(_this.getValue());
33577 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33579 this.monthField = new Roo.bootstrap.MonthField({
33580 after : '<i class=\"fa fa-calendar\"></i>',
33581 allowBlank : this.monthAllowBlank,
33582 placeholder : this.monthPlaceholder,
33585 render : function (_self)
33587 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33588 e.preventDefault();
33592 select : function (_self, oldvalue, newvalue)
33594 _this.setValue(_this.getValue());
33599 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33601 this.yearField = new Roo.bootstrap.ComboBox({
33602 allowBlank : this.yearAllowBlank,
33603 alwaysQuery : true,
33604 displayField : 'value',
33607 forceSelection : true,
33609 placeholder : this.yearPlaceholder,
33610 selectOnFocus : true,
33611 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33612 triggerAction : 'all',
33614 valueField : 'value',
33615 store : new Roo.data.SimpleStore({
33616 data : (function() {
33618 _this.fireEvent('years', _this, years);
33621 fields : [ 'value' ]
33624 select : function (_self, record, index)
33626 _this.setValue(_this.getValue());
33631 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33634 setValue : function(v, format)
33636 this.inputEl.dom.value = v;
33638 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33640 var d = Date.parseDate(v, f);
33647 this.setDay(d.format(this.dayFormat));
33648 this.setMonth(d.format(this.monthFormat));
33649 this.setYear(d.format(this.yearFormat));
33656 setDay : function(v)
33658 this.dayField.setValue(v);
33659 this.inputEl.dom.value = this.getValue();
33664 setMonth : function(v)
33666 this.monthField.setValue(v, true);
33667 this.inputEl.dom.value = this.getValue();
33672 setYear : function(v)
33674 this.yearField.setValue(v);
33675 this.inputEl.dom.value = this.getValue();
33680 getDay : function()
33682 return this.dayField.getValue();
33685 getMonth : function()
33687 return this.monthField.getValue();
33690 getYear : function()
33692 return this.yearField.getValue();
33695 getValue : function()
33697 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33699 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33709 this.inputEl.dom.value = '';
33714 validate : function()
33716 var d = this.dayField.validate();
33717 var m = this.monthField.validate();
33718 var y = this.yearField.validate();
33723 (!this.dayAllowBlank && !d) ||
33724 (!this.monthAllowBlank && !m) ||
33725 (!this.yearAllowBlank && !y)
33730 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33739 this.markInvalid();
33744 markValid : function()
33747 var label = this.el.select('label', true).first();
33748 var icon = this.el.select('i.fa-star', true).first();
33754 this.fireEvent('valid', this);
33758 * Mark this field as invalid
33759 * @param {String} msg The validation message
33761 markInvalid : function(msg)
33764 var label = this.el.select('label', true).first();
33765 var icon = this.el.select('i.fa-star', true).first();
33767 if(label && !icon){
33768 this.el.select('.roo-date-split-field-label', true).createChild({
33770 cls : 'text-danger fa fa-lg fa-star',
33771 tooltip : 'This field is required',
33772 style : 'margin-right:5px;'
33776 this.fireEvent('invalid', this, msg);
33779 clearInvalid : function()
33781 var label = this.el.select('label', true).first();
33782 var icon = this.el.select('i.fa-star', true).first();
33788 this.fireEvent('valid', this);
33791 getName: function()
33801 * http://masonry.desandro.com
33803 * The idea is to render all the bricks based on vertical width...
33805 * The original code extends 'outlayer' - we might need to use that....
33811 * @class Roo.bootstrap.LayoutMasonry
33812 * @extends Roo.bootstrap.Component
33813 * Bootstrap Layout Masonry class
33816 * Create a new Element
33817 * @param {Object} config The config object
33820 Roo.bootstrap.LayoutMasonry = function(config){
33822 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33826 Roo.bootstrap.LayoutMasonry.register(this);
33832 * Fire after layout the items
33833 * @param {Roo.bootstrap.LayoutMasonry} this
33834 * @param {Roo.EventObject} e
33841 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33844 * @cfg {Boolean} isLayoutInstant = no animation?
33846 isLayoutInstant : false, // needed?
33849 * @cfg {Number} boxWidth width of the columns
33854 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33859 * @cfg {Number} padWidth padding below box..
33864 * @cfg {Number} gutter gutter width..
33869 * @cfg {Number} maxCols maximum number of columns
33875 * @cfg {Boolean} isAutoInitial defalut true
33877 isAutoInitial : true,
33882 * @cfg {Boolean} isHorizontal defalut false
33884 isHorizontal : false,
33886 currentSize : null,
33892 bricks: null, //CompositeElement
33896 _isLayoutInited : false,
33898 // isAlternative : false, // only use for vertical layout...
33901 * @cfg {Number} alternativePadWidth padding below box..
33903 alternativePadWidth : 50,
33905 selectedBrick : [],
33907 getAutoCreate : function(){
33909 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33913 cls: 'blog-masonary-wrapper ' + this.cls,
33915 cls : 'mas-boxes masonary'
33922 getChildContainer: function( )
33924 if (this.boxesEl) {
33925 return this.boxesEl;
33928 this.boxesEl = this.el.select('.mas-boxes').first();
33930 return this.boxesEl;
33934 initEvents : function()
33938 if(this.isAutoInitial){
33939 Roo.log('hook children rendered');
33940 this.on('childrenrendered', function() {
33941 Roo.log('children rendered');
33947 initial : function()
33949 this.selectedBrick = [];
33951 this.currentSize = this.el.getBox(true);
33953 Roo.EventManager.onWindowResize(this.resize, this);
33955 if(!this.isAutoInitial){
33963 //this.layout.defer(500,this);
33967 resize : function()
33969 var cs = this.el.getBox(true);
33972 this.currentSize.width == cs.width &&
33973 this.currentSize.x == cs.x &&
33974 this.currentSize.height == cs.height &&
33975 this.currentSize.y == cs.y
33977 Roo.log("no change in with or X or Y");
33981 this.currentSize = cs;
33987 layout : function()
33989 this._resetLayout();
33991 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33993 this.layoutItems( isInstant );
33995 this._isLayoutInited = true;
33997 this.fireEvent('layout', this);
34001 _resetLayout : function()
34003 if(this.isHorizontal){
34004 this.horizontalMeasureColumns();
34008 this.verticalMeasureColumns();
34012 verticalMeasureColumns : function()
34014 this.getContainerWidth();
34016 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34017 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34021 var boxWidth = this.boxWidth + this.padWidth;
34023 if(this.containerWidth < this.boxWidth){
34024 boxWidth = this.containerWidth
34027 var containerWidth = this.containerWidth;
34029 var cols = Math.floor(containerWidth / boxWidth);
34031 this.cols = Math.max( cols, 1 );
34033 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34035 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34037 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34039 this.colWidth = boxWidth + avail - this.padWidth;
34041 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34042 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34045 horizontalMeasureColumns : function()
34047 this.getContainerWidth();
34049 var boxWidth = this.boxWidth;
34051 if(this.containerWidth < boxWidth){
34052 boxWidth = this.containerWidth;
34055 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34057 this.el.setHeight(boxWidth);
34061 getContainerWidth : function()
34063 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34066 layoutItems : function( isInstant )
34068 Roo.log(this.bricks);
34070 var items = Roo.apply([], this.bricks);
34072 if(this.isHorizontal){
34073 this._horizontalLayoutItems( items , isInstant );
34077 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34078 // this._verticalAlternativeLayoutItems( items , isInstant );
34082 this._verticalLayoutItems( items , isInstant );
34086 _verticalLayoutItems : function ( items , isInstant)
34088 if ( !items || !items.length ) {
34093 ['xs', 'xs', 'xs', 'tall'],
34094 ['xs', 'xs', 'tall'],
34095 ['xs', 'xs', 'sm'],
34096 ['xs', 'xs', 'xs'],
34102 ['sm', 'xs', 'xs'],
34106 ['tall', 'xs', 'xs', 'xs'],
34107 ['tall', 'xs', 'xs'],
34119 Roo.each(items, function(item, k){
34121 switch (item.size) {
34122 // these layouts take up a full box,
34133 boxes.push([item]);
34156 var filterPattern = function(box, length)
34164 var pattern = box.slice(0, length);
34168 Roo.each(pattern, function(i){
34169 format.push(i.size);
34172 Roo.each(standard, function(s){
34174 if(String(s) != String(format)){
34183 if(!match && length == 1){
34188 filterPattern(box, length - 1);
34192 queue.push(pattern);
34194 box = box.slice(length, box.length);
34196 filterPattern(box, 4);
34202 Roo.each(boxes, function(box, k){
34208 if(box.length == 1){
34213 filterPattern(box, 4);
34217 this._processVerticalLayoutQueue( queue, isInstant );
34221 // _verticalAlternativeLayoutItems : function( items , isInstant )
34223 // if ( !items || !items.length ) {
34227 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34231 _horizontalLayoutItems : function ( items , isInstant)
34233 if ( !items || !items.length || items.length < 3) {
34239 var eItems = items.slice(0, 3);
34241 items = items.slice(3, items.length);
34244 ['xs', 'xs', 'xs', 'wide'],
34245 ['xs', 'xs', 'wide'],
34246 ['xs', 'xs', 'sm'],
34247 ['xs', 'xs', 'xs'],
34253 ['sm', 'xs', 'xs'],
34257 ['wide', 'xs', 'xs', 'xs'],
34258 ['wide', 'xs', 'xs'],
34271 Roo.each(items, function(item, k){
34273 switch (item.size) {
34284 boxes.push([item]);
34308 var filterPattern = function(box, length)
34316 var pattern = box.slice(0, length);
34320 Roo.each(pattern, function(i){
34321 format.push(i.size);
34324 Roo.each(standard, function(s){
34326 if(String(s) != String(format)){
34335 if(!match && length == 1){
34340 filterPattern(box, length - 1);
34344 queue.push(pattern);
34346 box = box.slice(length, box.length);
34348 filterPattern(box, 4);
34354 Roo.each(boxes, function(box, k){
34360 if(box.length == 1){
34365 filterPattern(box, 4);
34372 var pos = this.el.getBox(true);
34376 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34378 var hit_end = false;
34380 Roo.each(queue, function(box){
34384 Roo.each(box, function(b){
34386 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34396 Roo.each(box, function(b){
34398 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34401 mx = Math.max(mx, b.x);
34405 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34409 Roo.each(box, function(b){
34411 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34425 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34428 /** Sets position of item in DOM
34429 * @param {Element} item
34430 * @param {Number} x - horizontal position
34431 * @param {Number} y - vertical position
34432 * @param {Boolean} isInstant - disables transitions
34434 _processVerticalLayoutQueue : function( queue, isInstant )
34436 var pos = this.el.getBox(true);
34441 for (var i = 0; i < this.cols; i++){
34445 Roo.each(queue, function(box, k){
34447 var col = k % this.cols;
34449 Roo.each(box, function(b,kk){
34451 b.el.position('absolute');
34453 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34454 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34456 if(b.size == 'md-left' || b.size == 'md-right'){
34457 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34458 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34461 b.el.setWidth(width);
34462 b.el.setHeight(height);
34464 b.el.select('iframe',true).setSize(width,height);
34468 for (var i = 0; i < this.cols; i++){
34470 if(maxY[i] < maxY[col]){
34475 col = Math.min(col, i);
34479 x = pos.x + col * (this.colWidth + this.padWidth);
34483 var positions = [];
34485 switch (box.length){
34487 positions = this.getVerticalOneBoxColPositions(x, y, box);
34490 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34493 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34496 positions = this.getVerticalFourBoxColPositions(x, y, box);
34502 Roo.each(box, function(b,kk){
34504 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34506 var sz = b.el.getSize();
34508 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34516 for (var i = 0; i < this.cols; i++){
34517 mY = Math.max(mY, maxY[i]);
34520 this.el.setHeight(mY - pos.y);
34524 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34526 // var pos = this.el.getBox(true);
34529 // var maxX = pos.right;
34531 // var maxHeight = 0;
34533 // Roo.each(items, function(item, k){
34537 // item.el.position('absolute');
34539 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34541 // item.el.setWidth(width);
34543 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34545 // item.el.setHeight(height);
34548 // item.el.setXY([x, y], isInstant ? false : true);
34550 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34553 // y = y + height + this.alternativePadWidth;
34555 // maxHeight = maxHeight + height + this.alternativePadWidth;
34559 // this.el.setHeight(maxHeight);
34563 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34565 var pos = this.el.getBox(true);
34570 var maxX = pos.right;
34572 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34574 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34576 Roo.each(queue, function(box, k){
34578 Roo.each(box, function(b, kk){
34580 b.el.position('absolute');
34582 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34583 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34585 if(b.size == 'md-left' || b.size == 'md-right'){
34586 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34587 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34590 b.el.setWidth(width);
34591 b.el.setHeight(height);
34599 var positions = [];
34601 switch (box.length){
34603 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34606 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34609 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34612 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34618 Roo.each(box, function(b,kk){
34620 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34622 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34630 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34632 Roo.each(eItems, function(b,k){
34634 b.size = (k == 0) ? 'sm' : 'xs';
34635 b.x = (k == 0) ? 2 : 1;
34636 b.y = (k == 0) ? 2 : 1;
34638 b.el.position('absolute');
34640 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34642 b.el.setWidth(width);
34644 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34646 b.el.setHeight(height);
34650 var positions = [];
34653 x : maxX - this.unitWidth * 2 - this.gutter,
34658 x : maxX - this.unitWidth,
34659 y : minY + (this.unitWidth + this.gutter) * 2
34663 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34667 Roo.each(eItems, function(b,k){
34669 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34675 getVerticalOneBoxColPositions : function(x, y, box)
34679 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34681 if(box[0].size == 'md-left'){
34685 if(box[0].size == 'md-right'){
34690 x : x + (this.unitWidth + this.gutter) * rand,
34697 getVerticalTwoBoxColPositions : function(x, y, box)
34701 if(box[0].size == 'xs'){
34705 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34709 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34723 x : x + (this.unitWidth + this.gutter) * 2,
34724 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34731 getVerticalThreeBoxColPositions : function(x, y, box)
34735 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34743 x : x + (this.unitWidth + this.gutter) * 1,
34748 x : x + (this.unitWidth + this.gutter) * 2,
34756 if(box[0].size == 'xs' && box[1].size == 'xs'){
34765 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34769 x : x + (this.unitWidth + this.gutter) * 1,
34783 x : x + (this.unitWidth + this.gutter) * 2,
34788 x : x + (this.unitWidth + this.gutter) * 2,
34789 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34796 getVerticalFourBoxColPositions : function(x, y, box)
34800 if(box[0].size == 'xs'){
34809 y : y + (this.unitHeight + this.gutter) * 1
34814 y : y + (this.unitHeight + this.gutter) * 2
34818 x : x + (this.unitWidth + this.gutter) * 1,
34832 x : x + (this.unitWidth + this.gutter) * 2,
34837 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34838 y : y + (this.unitHeight + this.gutter) * 1
34842 x : x + (this.unitWidth + this.gutter) * 2,
34843 y : y + (this.unitWidth + this.gutter) * 2
34850 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34854 if(box[0].size == 'md-left'){
34856 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34863 if(box[0].size == 'md-right'){
34865 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34866 y : minY + (this.unitWidth + this.gutter) * 1
34872 var rand = Math.floor(Math.random() * (4 - box[0].y));
34875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34876 y : minY + (this.unitWidth + this.gutter) * rand
34883 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34887 if(box[0].size == 'xs'){
34890 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34896 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34909 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34910 y : minY + (this.unitWidth + this.gutter) * 2
34917 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34921 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34924 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34929 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34930 y : minY + (this.unitWidth + this.gutter) * 1
34934 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34935 y : minY + (this.unitWidth + this.gutter) * 2
34942 if(box[0].size == 'xs' && box[1].size == 'xs'){
34945 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34950 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34955 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34956 y : minY + (this.unitWidth + this.gutter) * 1
34964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34969 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970 y : minY + (this.unitWidth + this.gutter) * 2
34974 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34975 y : minY + (this.unitWidth + this.gutter) * 2
34982 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34986 if(box[0].size == 'xs'){
34989 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34994 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34999 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),
35004 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35005 y : minY + (this.unitWidth + this.gutter) * 1
35013 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35018 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35019 y : minY + (this.unitWidth + this.gutter) * 2
35023 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35024 y : minY + (this.unitWidth + this.gutter) * 2
35028 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),
35029 y : minY + (this.unitWidth + this.gutter) * 2
35037 * remove a Masonry Brick
35038 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35040 removeBrick : function(brick_id)
35046 for (var i = 0; i<this.bricks.length; i++) {
35047 if (this.bricks[i].id == brick_id) {
35048 this.bricks.splice(i,1);
35049 this.el.dom.removeChild(Roo.get(brick_id).dom);
35056 * adds a Masonry Brick
35057 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35059 addBrick : function(cfg)
35061 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35062 //this.register(cn);
35063 cn.parentId = this.id;
35064 cn.render(this.el);
35069 * register a Masonry Brick
35070 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35073 register : function(brick)
35075 this.bricks.push(brick);
35076 brick.masonryId = this.id;
35080 * clear all the Masonry Brick
35082 clearAll : function()
35085 //this.getChildContainer().dom.innerHTML = "";
35086 this.el.dom.innerHTML = '';
35089 getSelected : function()
35091 if (!this.selectedBrick) {
35095 return this.selectedBrick;
35099 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35103 * register a Masonry Layout
35104 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35107 register : function(layout)
35109 this.groups[layout.id] = layout;
35112 * fetch a Masonry Layout based on the masonry layout ID
35113 * @param {string} the masonry layout to add
35114 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35117 get: function(layout_id) {
35118 if (typeof(this.groups[layout_id]) == 'undefined') {
35121 return this.groups[layout_id] ;
35133 * http://masonry.desandro.com
35135 * The idea is to render all the bricks based on vertical width...
35137 * The original code extends 'outlayer' - we might need to use that....
35143 * @class Roo.bootstrap.LayoutMasonryAuto
35144 * @extends Roo.bootstrap.Component
35145 * Bootstrap Layout Masonry class
35148 * Create a new Element
35149 * @param {Object} config The config object
35152 Roo.bootstrap.LayoutMasonryAuto = function(config){
35153 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35156 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35159 * @cfg {Boolean} isFitWidth - resize the width..
35161 isFitWidth : false, // options..
35163 * @cfg {Boolean} isOriginLeft = left align?
35165 isOriginLeft : true,
35167 * @cfg {Boolean} isOriginTop = top align?
35169 isOriginTop : false,
35171 * @cfg {Boolean} isLayoutInstant = no animation?
35173 isLayoutInstant : false, // needed?
35175 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35177 isResizingContainer : true,
35179 * @cfg {Number} columnWidth width of the columns
35185 * @cfg {Number} maxCols maximum number of columns
35190 * @cfg {Number} padHeight padding below box..
35196 * @cfg {Boolean} isAutoInitial defalut true
35199 isAutoInitial : true,
35205 initialColumnWidth : 0,
35206 currentSize : null,
35208 colYs : null, // array.
35215 bricks: null, //CompositeElement
35216 cols : 0, // array?
35217 // element : null, // wrapped now this.el
35218 _isLayoutInited : null,
35221 getAutoCreate : function(){
35225 cls: 'blog-masonary-wrapper ' + this.cls,
35227 cls : 'mas-boxes masonary'
35234 getChildContainer: function( )
35236 if (this.boxesEl) {
35237 return this.boxesEl;
35240 this.boxesEl = this.el.select('.mas-boxes').first();
35242 return this.boxesEl;
35246 initEvents : function()
35250 if(this.isAutoInitial){
35251 Roo.log('hook children rendered');
35252 this.on('childrenrendered', function() {
35253 Roo.log('children rendered');
35260 initial : function()
35262 this.reloadItems();
35264 this.currentSize = this.el.getBox(true);
35266 /// was window resize... - let's see if this works..
35267 Roo.EventManager.onWindowResize(this.resize, this);
35269 if(!this.isAutoInitial){
35274 this.layout.defer(500,this);
35277 reloadItems: function()
35279 this.bricks = this.el.select('.masonry-brick', true);
35281 this.bricks.each(function(b) {
35282 //Roo.log(b.getSize());
35283 if (!b.attr('originalwidth')) {
35284 b.attr('originalwidth', b.getSize().width);
35289 Roo.log(this.bricks.elements.length);
35292 resize : function()
35295 var cs = this.el.getBox(true);
35297 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35298 Roo.log("no change in with or X");
35301 this.currentSize = cs;
35305 layout : function()
35308 this._resetLayout();
35309 //this._manageStamps();
35311 // don't animate first layout
35312 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35313 this.layoutItems( isInstant );
35315 // flag for initalized
35316 this._isLayoutInited = true;
35319 layoutItems : function( isInstant )
35321 //var items = this._getItemsForLayout( this.items );
35322 // original code supports filtering layout items.. we just ignore it..
35324 this._layoutItems( this.bricks , isInstant );
35326 this._postLayout();
35328 _layoutItems : function ( items , isInstant)
35330 //this.fireEvent( 'layout', this, items );
35333 if ( !items || !items.elements.length ) {
35334 // no items, emit event with empty array
35339 items.each(function(item) {
35340 Roo.log("layout item");
35342 // get x/y object from method
35343 var position = this._getItemLayoutPosition( item );
35345 position.item = item;
35346 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35347 queue.push( position );
35350 this._processLayoutQueue( queue );
35352 /** Sets position of item in DOM
35353 * @param {Element} item
35354 * @param {Number} x - horizontal position
35355 * @param {Number} y - vertical position
35356 * @param {Boolean} isInstant - disables transitions
35358 _processLayoutQueue : function( queue )
35360 for ( var i=0, len = queue.length; i < len; i++ ) {
35361 var obj = queue[i];
35362 obj.item.position('absolute');
35363 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35369 * Any logic you want to do after each layout,
35370 * i.e. size the container
35372 _postLayout : function()
35374 this.resizeContainer();
35377 resizeContainer : function()
35379 if ( !this.isResizingContainer ) {
35382 var size = this._getContainerSize();
35384 this.el.setSize(size.width,size.height);
35385 this.boxesEl.setSize(size.width,size.height);
35391 _resetLayout : function()
35393 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35394 this.colWidth = this.el.getWidth();
35395 //this.gutter = this.el.getWidth();
35397 this.measureColumns();
35403 this.colYs.push( 0 );
35409 measureColumns : function()
35411 this.getContainerWidth();
35412 // if columnWidth is 0, default to outerWidth of first item
35413 if ( !this.columnWidth ) {
35414 var firstItem = this.bricks.first();
35415 Roo.log(firstItem);
35416 this.columnWidth = this.containerWidth;
35417 if (firstItem && firstItem.attr('originalwidth') ) {
35418 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35420 // columnWidth fall back to item of first element
35421 Roo.log("set column width?");
35422 this.initialColumnWidth = this.columnWidth ;
35424 // if first elem has no width, default to size of container
35429 if (this.initialColumnWidth) {
35430 this.columnWidth = this.initialColumnWidth;
35435 // column width is fixed at the top - however if container width get's smaller we should
35438 // this bit calcs how man columns..
35440 var columnWidth = this.columnWidth += this.gutter;
35442 // calculate columns
35443 var containerWidth = this.containerWidth + this.gutter;
35445 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35446 // fix rounding errors, typically with gutters
35447 var excess = columnWidth - containerWidth % columnWidth;
35450 // if overshoot is less than a pixel, round up, otherwise floor it
35451 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35452 cols = Math[ mathMethod ]( cols );
35453 this.cols = Math.max( cols, 1 );
35454 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35456 // padding positioning..
35457 var totalColWidth = this.cols * this.columnWidth;
35458 var padavail = this.containerWidth - totalColWidth;
35459 // so for 2 columns - we need 3 'pads'
35461 var padNeeded = (1+this.cols) * this.padWidth;
35463 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35465 this.columnWidth += padExtra
35466 //this.padWidth = Math.floor(padavail / ( this.cols));
35468 // adjust colum width so that padding is fixed??
35470 // we have 3 columns ... total = width * 3
35471 // we have X left over... that should be used by
35473 //if (this.expandC) {
35481 getContainerWidth : function()
35483 /* // container is parent if fit width
35484 var container = this.isFitWidth ? this.element.parentNode : this.element;
35485 // check that this.size and size are there
35486 // IE8 triggers resize on body size change, so they might not be
35488 var size = getSize( container ); //FIXME
35489 this.containerWidth = size && size.innerWidth; //FIXME
35492 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35496 _getItemLayoutPosition : function( item ) // what is item?
35498 // we resize the item to our columnWidth..
35500 item.setWidth(this.columnWidth);
35501 item.autoBoxAdjust = false;
35503 var sz = item.getSize();
35505 // how many columns does this brick span
35506 var remainder = this.containerWidth % this.columnWidth;
35508 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35509 // round if off by 1 pixel, otherwise use ceil
35510 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35511 colSpan = Math.min( colSpan, this.cols );
35513 // normally this should be '1' as we dont' currently allow multi width columns..
35515 var colGroup = this._getColGroup( colSpan );
35516 // get the minimum Y value from the columns
35517 var minimumY = Math.min.apply( Math, colGroup );
35518 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35520 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35522 // position the brick
35524 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35525 y: this.currentSize.y + minimumY + this.padHeight
35529 // apply setHeight to necessary columns
35530 var setHeight = minimumY + sz.height + this.padHeight;
35531 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35533 var setSpan = this.cols + 1 - colGroup.length;
35534 for ( var i = 0; i < setSpan; i++ ) {
35535 this.colYs[ shortColIndex + i ] = setHeight ;
35542 * @param {Number} colSpan - number of columns the element spans
35543 * @returns {Array} colGroup
35545 _getColGroup : function( colSpan )
35547 if ( colSpan < 2 ) {
35548 // if brick spans only one column, use all the column Ys
35553 // how many different places could this brick fit horizontally
35554 var groupCount = this.cols + 1 - colSpan;
35555 // for each group potential horizontal position
35556 for ( var i = 0; i < groupCount; i++ ) {
35557 // make an array of colY values for that one group
35558 var groupColYs = this.colYs.slice( i, i + colSpan );
35559 // and get the max value of the array
35560 colGroup[i] = Math.max.apply( Math, groupColYs );
35565 _manageStamp : function( stamp )
35567 var stampSize = stamp.getSize();
35568 var offset = stamp.getBox();
35569 // get the columns that this stamp affects
35570 var firstX = this.isOriginLeft ? offset.x : offset.right;
35571 var lastX = firstX + stampSize.width;
35572 var firstCol = Math.floor( firstX / this.columnWidth );
35573 firstCol = Math.max( 0, firstCol );
35575 var lastCol = Math.floor( lastX / this.columnWidth );
35576 // lastCol should not go over if multiple of columnWidth #425
35577 lastCol -= lastX % this.columnWidth ? 0 : 1;
35578 lastCol = Math.min( this.cols - 1, lastCol );
35580 // set colYs to bottom of the stamp
35581 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35584 for ( var i = firstCol; i <= lastCol; i++ ) {
35585 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35590 _getContainerSize : function()
35592 this.maxY = Math.max.apply( Math, this.colYs );
35597 if ( this.isFitWidth ) {
35598 size.width = this._getContainerFitWidth();
35604 _getContainerFitWidth : function()
35606 var unusedCols = 0;
35607 // count unused columns
35610 if ( this.colYs[i] !== 0 ) {
35615 // fit container to columns that have been used
35616 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35619 needsResizeLayout : function()
35621 var previousWidth = this.containerWidth;
35622 this.getContainerWidth();
35623 return previousWidth !== this.containerWidth;
35638 * @class Roo.bootstrap.MasonryBrick
35639 * @extends Roo.bootstrap.Component
35640 * Bootstrap MasonryBrick class
35643 * Create a new MasonryBrick
35644 * @param {Object} config The config object
35647 Roo.bootstrap.MasonryBrick = function(config){
35649 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35651 Roo.bootstrap.MasonryBrick.register(this);
35657 * When a MasonryBrick is clcik
35658 * @param {Roo.bootstrap.MasonryBrick} this
35659 * @param {Roo.EventObject} e
35665 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35668 * @cfg {String} title
35672 * @cfg {String} html
35676 * @cfg {String} bgimage
35680 * @cfg {String} videourl
35684 * @cfg {String} cls
35688 * @cfg {String} href
35692 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35697 * @cfg {String} placetitle (center|bottom)
35702 * @cfg {Boolean} isFitContainer defalut true
35704 isFitContainer : true,
35707 * @cfg {Boolean} preventDefault defalut false
35709 preventDefault : false,
35712 * @cfg {Boolean} inverse defalut false
35714 maskInverse : false,
35716 getAutoCreate : function()
35718 if(!this.isFitContainer){
35719 return this.getSplitAutoCreate();
35722 var cls = 'masonry-brick masonry-brick-full';
35724 if(this.href.length){
35725 cls += ' masonry-brick-link';
35728 if(this.bgimage.length){
35729 cls += ' masonry-brick-image';
35732 if(this.maskInverse){
35733 cls += ' mask-inverse';
35736 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35737 cls += ' enable-mask';
35741 cls += ' masonry-' + this.size + '-brick';
35744 if(this.placetitle.length){
35746 switch (this.placetitle) {
35748 cls += ' masonry-center-title';
35751 cls += ' masonry-bottom-title';
35758 if(!this.html.length && !this.bgimage.length){
35759 cls += ' masonry-center-title';
35762 if(!this.html.length && this.bgimage.length){
35763 cls += ' masonry-bottom-title';
35768 cls += ' ' + this.cls;
35772 tag: (this.href.length) ? 'a' : 'div',
35777 cls: 'masonry-brick-mask'
35781 cls: 'masonry-brick-paragraph',
35787 if(this.href.length){
35788 cfg.href = this.href;
35791 var cn = cfg.cn[1].cn;
35793 if(this.title.length){
35796 cls: 'masonry-brick-title',
35801 if(this.html.length){
35804 cls: 'masonry-brick-text',
35809 if (!this.title.length && !this.html.length) {
35810 cfg.cn[1].cls += ' hide';
35813 if(this.bgimage.length){
35816 cls: 'masonry-brick-image-view',
35821 if(this.videourl.length){
35822 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35823 // youtube support only?
35826 cls: 'masonry-brick-image-view',
35829 allowfullscreen : true
35837 getSplitAutoCreate : function()
35839 var cls = 'masonry-brick masonry-brick-split';
35841 if(this.href.length){
35842 cls += ' masonry-brick-link';
35845 if(this.bgimage.length){
35846 cls += ' masonry-brick-image';
35850 cls += ' masonry-' + this.size + '-brick';
35853 switch (this.placetitle) {
35855 cls += ' masonry-center-title';
35858 cls += ' masonry-bottom-title';
35861 if(!this.bgimage.length){
35862 cls += ' masonry-center-title';
35865 if(this.bgimage.length){
35866 cls += ' masonry-bottom-title';
35872 cls += ' ' + this.cls;
35876 tag: (this.href.length) ? 'a' : 'div',
35881 cls: 'masonry-brick-split-head',
35885 cls: 'masonry-brick-paragraph',
35892 cls: 'masonry-brick-split-body',
35898 if(this.href.length){
35899 cfg.href = this.href;
35902 if(this.title.length){
35903 cfg.cn[0].cn[0].cn.push({
35905 cls: 'masonry-brick-title',
35910 if(this.html.length){
35911 cfg.cn[1].cn.push({
35913 cls: 'masonry-brick-text',
35918 if(this.bgimage.length){
35919 cfg.cn[0].cn.push({
35921 cls: 'masonry-brick-image-view',
35926 if(this.videourl.length){
35927 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35928 // youtube support only?
35929 cfg.cn[0].cn.cn.push({
35931 cls: 'masonry-brick-image-view',
35934 allowfullscreen : true
35941 initEvents: function()
35943 switch (this.size) {
35976 this.el.on('touchstart', this.onTouchStart, this);
35977 this.el.on('touchmove', this.onTouchMove, this);
35978 this.el.on('touchend', this.onTouchEnd, this);
35979 this.el.on('contextmenu', this.onContextMenu, this);
35981 this.el.on('mouseenter' ,this.enter, this);
35982 this.el.on('mouseleave', this.leave, this);
35983 this.el.on('click', this.onClick, this);
35986 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35987 this.parent().bricks.push(this);
35992 onClick: function(e, el)
35994 var time = this.endTimer - this.startTimer;
35995 // Roo.log(e.preventDefault());
35998 e.preventDefault();
36003 if(!this.preventDefault){
36007 e.preventDefault();
36009 if (this.activeClass != '') {
36010 this.selectBrick();
36013 this.fireEvent('click', this, e);
36016 enter: function(e, el)
36018 e.preventDefault();
36020 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36024 if(this.bgimage.length && this.html.length){
36025 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36029 leave: function(e, el)
36031 e.preventDefault();
36033 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36037 if(this.bgimage.length && this.html.length){
36038 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36042 onTouchStart: function(e, el)
36044 // e.preventDefault();
36046 this.touchmoved = false;
36048 if(!this.isFitContainer){
36052 if(!this.bgimage.length || !this.html.length){
36056 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36058 this.timer = new Date().getTime();
36062 onTouchMove: function(e, el)
36064 this.touchmoved = true;
36067 onContextMenu : function(e,el)
36069 e.preventDefault();
36070 e.stopPropagation();
36074 onTouchEnd: function(e, el)
36076 // e.preventDefault();
36078 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36085 if(!this.bgimage.length || !this.html.length){
36087 if(this.href.length){
36088 window.location.href = this.href;
36094 if(!this.isFitContainer){
36098 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36100 window.location.href = this.href;
36103 //selection on single brick only
36104 selectBrick : function() {
36106 if (!this.parentId) {
36110 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36111 var index = m.selectedBrick.indexOf(this.id);
36114 m.selectedBrick.splice(index,1);
36115 this.el.removeClass(this.activeClass);
36119 for(var i = 0; i < m.selectedBrick.length; i++) {
36120 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36121 b.el.removeClass(b.activeClass);
36124 m.selectedBrick = [];
36126 m.selectedBrick.push(this.id);
36127 this.el.addClass(this.activeClass);
36131 isSelected : function(){
36132 return this.el.hasClass(this.activeClass);
36137 Roo.apply(Roo.bootstrap.MasonryBrick, {
36140 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36142 * register a Masonry Brick
36143 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36146 register : function(brick)
36148 //this.groups[brick.id] = brick;
36149 this.groups.add(brick.id, brick);
36152 * fetch a masonry brick based on the masonry brick ID
36153 * @param {string} the masonry brick to add
36154 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36157 get: function(brick_id)
36159 // if (typeof(this.groups[brick_id]) == 'undefined') {
36162 // return this.groups[brick_id] ;
36164 if(this.groups.key(brick_id)) {
36165 return this.groups.key(brick_id);
36183 * @class Roo.bootstrap.Brick
36184 * @extends Roo.bootstrap.Component
36185 * Bootstrap Brick class
36188 * Create a new Brick
36189 * @param {Object} config The config object
36192 Roo.bootstrap.Brick = function(config){
36193 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36199 * When a Brick is click
36200 * @param {Roo.bootstrap.Brick} this
36201 * @param {Roo.EventObject} e
36207 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36210 * @cfg {String} title
36214 * @cfg {String} html
36218 * @cfg {String} bgimage
36222 * @cfg {String} cls
36226 * @cfg {String} href
36230 * @cfg {String} video
36234 * @cfg {Boolean} square
36238 getAutoCreate : function()
36240 var cls = 'roo-brick';
36242 if(this.href.length){
36243 cls += ' roo-brick-link';
36246 if(this.bgimage.length){
36247 cls += ' roo-brick-image';
36250 if(!this.html.length && !this.bgimage.length){
36251 cls += ' roo-brick-center-title';
36254 if(!this.html.length && this.bgimage.length){
36255 cls += ' roo-brick-bottom-title';
36259 cls += ' ' + this.cls;
36263 tag: (this.href.length) ? 'a' : 'div',
36268 cls: 'roo-brick-paragraph',
36274 if(this.href.length){
36275 cfg.href = this.href;
36278 var cn = cfg.cn[0].cn;
36280 if(this.title.length){
36283 cls: 'roo-brick-title',
36288 if(this.html.length){
36291 cls: 'roo-brick-text',
36298 if(this.bgimage.length){
36301 cls: 'roo-brick-image-view',
36309 initEvents: function()
36311 if(this.title.length || this.html.length){
36312 this.el.on('mouseenter' ,this.enter, this);
36313 this.el.on('mouseleave', this.leave, this);
36316 Roo.EventManager.onWindowResize(this.resize, this);
36318 if(this.bgimage.length){
36319 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36320 this.imageEl.on('load', this.onImageLoad, this);
36327 onImageLoad : function()
36332 resize : function()
36334 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36336 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36338 if(this.bgimage.length){
36339 var image = this.el.select('.roo-brick-image-view', true).first();
36341 image.setWidth(paragraph.getWidth());
36344 image.setHeight(paragraph.getWidth());
36347 this.el.setHeight(image.getHeight());
36348 paragraph.setHeight(image.getHeight());
36354 enter: function(e, el)
36356 e.preventDefault();
36358 if(this.bgimage.length){
36359 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36360 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36364 leave: function(e, el)
36366 e.preventDefault();
36368 if(this.bgimage.length){
36369 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36370 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36385 * @class Roo.bootstrap.NumberField
36386 * @extends Roo.bootstrap.Input
36387 * Bootstrap NumberField class
36393 * Create a new NumberField
36394 * @param {Object} config The config object
36397 Roo.bootstrap.NumberField = function(config){
36398 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36401 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36404 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36406 allowDecimals : true,
36408 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36410 decimalSeparator : ".",
36412 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36414 decimalPrecision : 2,
36416 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36418 allowNegative : true,
36421 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36425 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36427 minValue : Number.NEGATIVE_INFINITY,
36429 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36431 maxValue : Number.MAX_VALUE,
36433 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36435 minText : "The minimum value for this field is {0}",
36437 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36439 maxText : "The maximum value for this field is {0}",
36441 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36442 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36444 nanText : "{0} is not a valid number",
36446 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36448 thousandsDelimiter : false,
36450 * @cfg {String} valueAlign alignment of value
36452 valueAlign : "left",
36454 getAutoCreate : function()
36456 var hiddenInput = {
36460 cls: 'hidden-number-input'
36464 hiddenInput.name = this.name;
36469 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36471 this.name = hiddenInput.name;
36473 if(cfg.cn.length > 0) {
36474 cfg.cn.push(hiddenInput);
36481 initEvents : function()
36483 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36485 var allowed = "0123456789";
36487 if(this.allowDecimals){
36488 allowed += this.decimalSeparator;
36491 if(this.allowNegative){
36495 if(this.thousandsDelimiter) {
36499 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36501 var keyPress = function(e){
36503 var k = e.getKey();
36505 var c = e.getCharCode();
36508 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36509 allowed.indexOf(String.fromCharCode(c)) === -1
36515 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36519 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36524 this.el.on("keypress", keyPress, this);
36527 validateValue : function(value)
36530 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36534 var num = this.parseValue(value);
36537 this.markInvalid(String.format(this.nanText, value));
36541 if(num < this.minValue){
36542 this.markInvalid(String.format(this.minText, this.minValue));
36546 if(num > this.maxValue){
36547 this.markInvalid(String.format(this.maxText, this.maxValue));
36554 getValue : function()
36556 var v = this.hiddenEl().getValue();
36558 return this.fixPrecision(this.parseValue(v));
36561 parseValue : function(value)
36563 if(this.thousandsDelimiter) {
36565 r = new RegExp(",", "g");
36566 value = value.replace(r, "");
36569 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36570 return isNaN(value) ? '' : value;
36573 fixPrecision : function(value)
36575 if(this.thousandsDelimiter) {
36577 r = new RegExp(",", "g");
36578 value = value.replace(r, "");
36581 var nan = isNaN(value);
36583 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36584 return nan ? '' : value;
36586 return parseFloat(value).toFixed(this.decimalPrecision);
36589 setValue : function(v)
36591 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36597 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36599 this.inputEl().dom.value = (v == '') ? '' :
36600 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36602 if(!this.allowZero && v === '0') {
36603 this.hiddenEl().dom.value = '';
36604 this.inputEl().dom.value = '';
36611 decimalPrecisionFcn : function(v)
36613 return Math.floor(v);
36616 beforeBlur : function()
36618 var v = this.parseValue(this.getRawValue());
36620 if(v || v === 0 || v === ''){
36625 hiddenEl : function()
36627 return this.el.select('input.hidden-number-input',true).first();
36639 * @class Roo.bootstrap.DocumentSlider
36640 * @extends Roo.bootstrap.Component
36641 * Bootstrap DocumentSlider class
36644 * Create a new DocumentViewer
36645 * @param {Object} config The config object
36648 Roo.bootstrap.DocumentSlider = function(config){
36649 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36656 * Fire after initEvent
36657 * @param {Roo.bootstrap.DocumentSlider} this
36662 * Fire after update
36663 * @param {Roo.bootstrap.DocumentSlider} this
36669 * @param {Roo.bootstrap.DocumentSlider} this
36675 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36681 getAutoCreate : function()
36685 cls : 'roo-document-slider',
36689 cls : 'roo-document-slider-header',
36693 cls : 'roo-document-slider-header-title'
36699 cls : 'roo-document-slider-body',
36703 cls : 'roo-document-slider-prev',
36707 cls : 'fa fa-chevron-left'
36713 cls : 'roo-document-slider-thumb',
36717 cls : 'roo-document-slider-image'
36723 cls : 'roo-document-slider-next',
36727 cls : 'fa fa-chevron-right'
36739 initEvents : function()
36741 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36742 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36744 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36745 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36747 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36748 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36750 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36751 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36753 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36754 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36756 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36757 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36759 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36760 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36762 this.thumbEl.on('click', this.onClick, this);
36764 this.prevIndicator.on('click', this.prev, this);
36766 this.nextIndicator.on('click', this.next, this);
36770 initial : function()
36772 if(this.files.length){
36773 this.indicator = 1;
36777 this.fireEvent('initial', this);
36780 update : function()
36782 this.imageEl.attr('src', this.files[this.indicator - 1]);
36784 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36786 this.prevIndicator.show();
36788 if(this.indicator == 1){
36789 this.prevIndicator.hide();
36792 this.nextIndicator.show();
36794 if(this.indicator == this.files.length){
36795 this.nextIndicator.hide();
36798 this.thumbEl.scrollTo('top');
36800 this.fireEvent('update', this);
36803 onClick : function(e)
36805 e.preventDefault();
36807 this.fireEvent('click', this);
36812 e.preventDefault();
36814 this.indicator = Math.max(1, this.indicator - 1);
36821 e.preventDefault();
36823 this.indicator = Math.min(this.files.length, this.indicator + 1);
36837 * @class Roo.bootstrap.RadioSet
36838 * @extends Roo.bootstrap.Input
36839 * Bootstrap RadioSet class
36840 * @cfg {String} indicatorpos (left|right) default left
36841 * @cfg {Boolean} inline (true|false) inline the element (default true)
36842 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36844 * Create a new RadioSet
36845 * @param {Object} config The config object
36848 Roo.bootstrap.RadioSet = function(config){
36850 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36854 Roo.bootstrap.RadioSet.register(this);
36859 * Fires when the element is checked or unchecked.
36860 * @param {Roo.bootstrap.RadioSet} this This radio
36861 * @param {Roo.bootstrap.Radio} item The checked item
36866 * Fires when the element is click.
36867 * @param {Roo.bootstrap.RadioSet} this This radio set
36868 * @param {Roo.bootstrap.Radio} item The checked item
36869 * @param {Roo.EventObject} e The event object
36876 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36884 indicatorpos : 'left',
36886 getAutoCreate : function()
36890 cls : 'roo-radio-set-label',
36894 html : this.fieldLabel
36898 if (Roo.bootstrap.version == 3) {
36901 if(this.indicatorpos == 'left'){
36904 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36905 tooltip : 'This field is required'
36910 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36911 tooltip : 'This field is required'
36917 cls : 'roo-radio-set-items'
36920 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36922 if (align === 'left' && this.fieldLabel.length) {
36925 cls : "roo-radio-set-right",
36931 if(this.labelWidth > 12){
36932 label.style = "width: " + this.labelWidth + 'px';
36935 if(this.labelWidth < 13 && this.labelmd == 0){
36936 this.labelmd = this.labelWidth;
36939 if(this.labellg > 0){
36940 label.cls += ' col-lg-' + this.labellg;
36941 items.cls += ' col-lg-' + (12 - this.labellg);
36944 if(this.labelmd > 0){
36945 label.cls += ' col-md-' + this.labelmd;
36946 items.cls += ' col-md-' + (12 - this.labelmd);
36949 if(this.labelsm > 0){
36950 label.cls += ' col-sm-' + this.labelsm;
36951 items.cls += ' col-sm-' + (12 - this.labelsm);
36954 if(this.labelxs > 0){
36955 label.cls += ' col-xs-' + this.labelxs;
36956 items.cls += ' col-xs-' + (12 - this.labelxs);
36962 cls : 'roo-radio-set',
36966 cls : 'roo-radio-set-input',
36969 value : this.value ? this.value : ''
36976 if(this.weight.length){
36977 cfg.cls += ' roo-radio-' + this.weight;
36981 cfg.cls += ' roo-radio-set-inline';
36985 ['xs','sm','md','lg'].map(function(size){
36986 if (settings[size]) {
36987 cfg.cls += ' col-' + size + '-' + settings[size];
36995 initEvents : function()
36997 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36998 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37000 if(!this.fieldLabel.length){
37001 this.labelEl.hide();
37004 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37005 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37007 this.indicator = this.indicatorEl();
37009 if(this.indicator){
37010 this.indicator.addClass('invisible');
37013 this.originalValue = this.getValue();
37017 inputEl: function ()
37019 return this.el.select('.roo-radio-set-input', true).first();
37022 getChildContainer : function()
37024 return this.itemsEl;
37027 register : function(item)
37029 this.radioes.push(item);
37033 validate : function()
37035 if(this.getVisibilityEl().hasClass('hidden')){
37041 Roo.each(this.radioes, function(i){
37050 if(this.allowBlank) {
37054 if(this.disabled || valid){
37059 this.markInvalid();
37064 markValid : function()
37066 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37067 this.indicatorEl().removeClass('visible');
37068 this.indicatorEl().addClass('invisible');
37072 if (Roo.bootstrap.version == 3) {
37073 this.el.removeClass([this.invalidClass, this.validClass]);
37074 this.el.addClass(this.validClass);
37076 this.el.removeClass(['is-invalid','is-valid']);
37077 this.el.addClass(['is-valid']);
37079 this.fireEvent('valid', this);
37082 markInvalid : function(msg)
37084 if(this.allowBlank || this.disabled){
37088 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37089 this.indicatorEl().removeClass('invisible');
37090 this.indicatorEl().addClass('visible');
37092 if (Roo.bootstrap.version == 3) {
37093 this.el.removeClass([this.invalidClass, this.validClass]);
37094 this.el.addClass(this.invalidClass);
37096 this.el.removeClass(['is-invalid','is-valid']);
37097 this.el.addClass(['is-invalid']);
37100 this.fireEvent('invalid', this, msg);
37104 setValue : function(v, suppressEvent)
37106 if(this.value === v){
37113 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37116 Roo.each(this.radioes, function(i){
37118 i.el.removeClass('checked');
37121 Roo.each(this.radioes, function(i){
37123 if(i.value === v || i.value.toString() === v.toString()){
37125 i.el.addClass('checked');
37127 if(suppressEvent !== true){
37128 this.fireEvent('check', this, i);
37139 clearInvalid : function(){
37141 if(!this.el || this.preventMark){
37145 this.el.removeClass([this.invalidClass]);
37147 this.fireEvent('valid', this);
37152 Roo.apply(Roo.bootstrap.RadioSet, {
37156 register : function(set)
37158 this.groups[set.name] = set;
37161 get: function(name)
37163 if (typeof(this.groups[name]) == 'undefined') {
37167 return this.groups[name] ;
37173 * Ext JS Library 1.1.1
37174 * Copyright(c) 2006-2007, Ext JS, LLC.
37176 * Originally Released Under LGPL - original licence link has changed is not relivant.
37179 * <script type="text/javascript">
37184 * @class Roo.bootstrap.SplitBar
37185 * @extends Roo.util.Observable
37186 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37190 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37191 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37192 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37193 split.minSize = 100;
37194 split.maxSize = 600;
37195 split.animate = true;
37196 split.on('moved', splitterMoved);
37199 * Create a new SplitBar
37200 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37201 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37202 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37203 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37204 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37205 position of the SplitBar).
37207 Roo.bootstrap.SplitBar = function(cfg){
37212 // dragElement : elm
37213 // resizingElement: el,
37215 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37216 // placement : Roo.bootstrap.SplitBar.LEFT ,
37217 // existingProxy ???
37220 this.el = Roo.get(cfg.dragElement, true);
37221 this.el.dom.unselectable = "on";
37223 this.resizingEl = Roo.get(cfg.resizingElement, true);
37227 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37228 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37231 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37234 * The minimum size of the resizing element. (Defaults to 0)
37240 * The maximum size of the resizing element. (Defaults to 2000)
37243 this.maxSize = 2000;
37246 * Whether to animate the transition to the new size
37249 this.animate = false;
37252 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37255 this.useShim = false;
37260 if(!cfg.existingProxy){
37262 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37264 this.proxy = Roo.get(cfg.existingProxy).dom;
37267 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37270 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37273 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37276 this.dragSpecs = {};
37279 * @private The adapter to use to positon and resize elements
37281 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37282 this.adapter.init(this);
37284 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37286 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37287 this.el.addClass("roo-splitbar-h");
37290 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37291 this.el.addClass("roo-splitbar-v");
37297 * Fires when the splitter is moved (alias for {@link #event-moved})
37298 * @param {Roo.bootstrap.SplitBar} this
37299 * @param {Number} newSize the new width or height
37304 * Fires when the splitter is moved
37305 * @param {Roo.bootstrap.SplitBar} this
37306 * @param {Number} newSize the new width or height
37310 * @event beforeresize
37311 * Fires before the splitter is dragged
37312 * @param {Roo.bootstrap.SplitBar} this
37314 "beforeresize" : true,
37316 "beforeapply" : true
37319 Roo.util.Observable.call(this);
37322 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37323 onStartProxyDrag : function(x, y){
37324 this.fireEvent("beforeresize", this);
37326 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37328 o.enableDisplayMode("block");
37329 // all splitbars share the same overlay
37330 Roo.bootstrap.SplitBar.prototype.overlay = o;
37332 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37333 this.overlay.show();
37334 Roo.get(this.proxy).setDisplayed("block");
37335 var size = this.adapter.getElementSize(this);
37336 this.activeMinSize = this.getMinimumSize();;
37337 this.activeMaxSize = this.getMaximumSize();;
37338 var c1 = size - this.activeMinSize;
37339 var c2 = Math.max(this.activeMaxSize - size, 0);
37340 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37341 this.dd.resetConstraints();
37342 this.dd.setXConstraint(
37343 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37344 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37346 this.dd.setYConstraint(0, 0);
37348 this.dd.resetConstraints();
37349 this.dd.setXConstraint(0, 0);
37350 this.dd.setYConstraint(
37351 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37352 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37355 this.dragSpecs.startSize = size;
37356 this.dragSpecs.startPoint = [x, y];
37357 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37361 * @private Called after the drag operation by the DDProxy
37363 onEndProxyDrag : function(e){
37364 Roo.get(this.proxy).setDisplayed(false);
37365 var endPoint = Roo.lib.Event.getXY(e);
37367 this.overlay.hide();
37370 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37371 newSize = this.dragSpecs.startSize +
37372 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37373 endPoint[0] - this.dragSpecs.startPoint[0] :
37374 this.dragSpecs.startPoint[0] - endPoint[0]
37377 newSize = this.dragSpecs.startSize +
37378 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37379 endPoint[1] - this.dragSpecs.startPoint[1] :
37380 this.dragSpecs.startPoint[1] - endPoint[1]
37383 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37384 if(newSize != this.dragSpecs.startSize){
37385 if(this.fireEvent('beforeapply', this, newSize) !== false){
37386 this.adapter.setElementSize(this, newSize);
37387 this.fireEvent("moved", this, newSize);
37388 this.fireEvent("resize", this, newSize);
37394 * Get the adapter this SplitBar uses
37395 * @return The adapter object
37397 getAdapter : function(){
37398 return this.adapter;
37402 * Set the adapter this SplitBar uses
37403 * @param {Object} adapter A SplitBar adapter object
37405 setAdapter : function(adapter){
37406 this.adapter = adapter;
37407 this.adapter.init(this);
37411 * Gets the minimum size for the resizing element
37412 * @return {Number} The minimum size
37414 getMinimumSize : function(){
37415 return this.minSize;
37419 * Sets the minimum size for the resizing element
37420 * @param {Number} minSize The minimum size
37422 setMinimumSize : function(minSize){
37423 this.minSize = minSize;
37427 * Gets the maximum size for the resizing element
37428 * @return {Number} The maximum size
37430 getMaximumSize : function(){
37431 return this.maxSize;
37435 * Sets the maximum size for the resizing element
37436 * @param {Number} maxSize The maximum size
37438 setMaximumSize : function(maxSize){
37439 this.maxSize = maxSize;
37443 * Sets the initialize size for the resizing element
37444 * @param {Number} size The initial size
37446 setCurrentSize : function(size){
37447 var oldAnimate = this.animate;
37448 this.animate = false;
37449 this.adapter.setElementSize(this, size);
37450 this.animate = oldAnimate;
37454 * Destroy this splitbar.
37455 * @param {Boolean} removeEl True to remove the element
37457 destroy : function(removeEl){
37459 this.shim.remove();
37462 this.proxy.parentNode.removeChild(this.proxy);
37470 * @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.
37472 Roo.bootstrap.SplitBar.createProxy = function(dir){
37473 var proxy = new Roo.Element(document.createElement("div"));
37474 proxy.unselectable();
37475 var cls = 'roo-splitbar-proxy';
37476 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37477 document.body.appendChild(proxy.dom);
37482 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37483 * Default Adapter. It assumes the splitter and resizing element are not positioned
37484 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37486 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37489 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37490 // do nothing for now
37491 init : function(s){
37495 * Called before drag operations to get the current size of the resizing element.
37496 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37498 getElementSize : function(s){
37499 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37500 return s.resizingEl.getWidth();
37502 return s.resizingEl.getHeight();
37507 * Called after drag operations to set the size of the resizing element.
37508 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37509 * @param {Number} newSize The new size to set
37510 * @param {Function} onComplete A function to be invoked when resizing is complete
37512 setElementSize : function(s, newSize, onComplete){
37513 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37515 s.resizingEl.setWidth(newSize);
37517 onComplete(s, newSize);
37520 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37525 s.resizingEl.setHeight(newSize);
37527 onComplete(s, newSize);
37530 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37537 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37538 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37539 * Adapter that moves the splitter element to align with the resized sizing element.
37540 * Used with an absolute positioned SplitBar.
37541 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37542 * document.body, make sure you assign an id to the body element.
37544 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37545 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37546 this.container = Roo.get(container);
37549 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37550 init : function(s){
37551 this.basic.init(s);
37554 getElementSize : function(s){
37555 return this.basic.getElementSize(s);
37558 setElementSize : function(s, newSize, onComplete){
37559 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37562 moveSplitter : function(s){
37563 var yes = Roo.bootstrap.SplitBar;
37564 switch(s.placement){
37566 s.el.setX(s.resizingEl.getRight());
37569 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37572 s.el.setY(s.resizingEl.getBottom());
37575 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37582 * Orientation constant - Create a vertical SplitBar
37586 Roo.bootstrap.SplitBar.VERTICAL = 1;
37589 * Orientation constant - Create a horizontal SplitBar
37593 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37596 * Placement constant - The resizing element is to the left of the splitter element
37600 Roo.bootstrap.SplitBar.LEFT = 1;
37603 * Placement constant - The resizing element is to the right of the splitter element
37607 Roo.bootstrap.SplitBar.RIGHT = 2;
37610 * Placement constant - The resizing element is positioned above the splitter element
37614 Roo.bootstrap.SplitBar.TOP = 3;
37617 * Placement constant - The resizing element is positioned under splitter element
37621 Roo.bootstrap.SplitBar.BOTTOM = 4;
37622 Roo.namespace("Roo.bootstrap.layout");/*
37624 * Ext JS Library 1.1.1
37625 * Copyright(c) 2006-2007, Ext JS, LLC.
37627 * Originally Released Under LGPL - original licence link has changed is not relivant.
37630 * <script type="text/javascript">
37634 * @class Roo.bootstrap.layout.Manager
37635 * @extends Roo.bootstrap.Component
37636 * Base class for layout managers.
37638 Roo.bootstrap.layout.Manager = function(config)
37640 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37646 /** false to disable window resize monitoring @type Boolean */
37647 this.monitorWindowResize = true;
37652 * Fires when a layout is performed.
37653 * @param {Roo.LayoutManager} this
37657 * @event regionresized
37658 * Fires when the user resizes a region.
37659 * @param {Roo.LayoutRegion} region The resized region
37660 * @param {Number} newSize The new size (width for east/west, height for north/south)
37662 "regionresized" : true,
37664 * @event regioncollapsed
37665 * Fires when a region is collapsed.
37666 * @param {Roo.LayoutRegion} region The collapsed region
37668 "regioncollapsed" : true,
37670 * @event regionexpanded
37671 * Fires when a region is expanded.
37672 * @param {Roo.LayoutRegion} region The expanded region
37674 "regionexpanded" : true
37676 this.updating = false;
37679 this.el = Roo.get(config.el);
37685 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37690 monitorWindowResize : true,
37696 onRender : function(ct, position)
37699 this.el = Roo.get(ct);
37702 //this.fireEvent('render',this);
37706 initEvents: function()
37710 // ie scrollbar fix
37711 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37712 document.body.scroll = "no";
37713 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37714 this.el.position('relative');
37716 this.id = this.el.id;
37717 this.el.addClass("roo-layout-container");
37718 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37719 if(this.el.dom != document.body ) {
37720 this.el.on('resize', this.layout,this);
37721 this.el.on('show', this.layout,this);
37727 * Returns true if this layout is currently being updated
37728 * @return {Boolean}
37730 isUpdating : function(){
37731 return this.updating;
37735 * Suspend the LayoutManager from doing auto-layouts while
37736 * making multiple add or remove calls
37738 beginUpdate : function(){
37739 this.updating = true;
37743 * Restore auto-layouts and optionally disable the manager from performing a layout
37744 * @param {Boolean} noLayout true to disable a layout update
37746 endUpdate : function(noLayout){
37747 this.updating = false;
37753 layout: function(){
37757 onRegionResized : function(region, newSize){
37758 this.fireEvent("regionresized", region, newSize);
37762 onRegionCollapsed : function(region){
37763 this.fireEvent("regioncollapsed", region);
37766 onRegionExpanded : function(region){
37767 this.fireEvent("regionexpanded", region);
37771 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37772 * performs box-model adjustments.
37773 * @return {Object} The size as an object {width: (the width), height: (the height)}
37775 getViewSize : function()
37778 if(this.el.dom != document.body){
37779 size = this.el.getSize();
37781 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37783 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37784 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37789 * Returns the Element this layout is bound to.
37790 * @return {Roo.Element}
37792 getEl : function(){
37797 * Returns the specified region.
37798 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37799 * @return {Roo.LayoutRegion}
37801 getRegion : function(target){
37802 return this.regions[target.toLowerCase()];
37805 onWindowResize : function(){
37806 if(this.monitorWindowResize){
37813 * Ext JS Library 1.1.1
37814 * Copyright(c) 2006-2007, Ext JS, LLC.
37816 * Originally Released Under LGPL - original licence link has changed is not relivant.
37819 * <script type="text/javascript">
37822 * @class Roo.bootstrap.layout.Border
37823 * @extends Roo.bootstrap.layout.Manager
37824 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37825 * please see: examples/bootstrap/nested.html<br><br>
37827 <b>The container the layout is rendered into can be either the body element or any other element.
37828 If it is not the body element, the container needs to either be an absolute positioned element,
37829 or you will need to add "position:relative" to the css of the container. You will also need to specify
37830 the container size if it is not the body element.</b>
37833 * Create a new Border
37834 * @param {Object} config Configuration options
37836 Roo.bootstrap.layout.Border = function(config){
37837 config = config || {};
37838 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37842 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37843 if(config[region]){
37844 config[region].region = region;
37845 this.addRegion(config[region]);
37851 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37853 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37855 parent : false, // this might point to a 'nest' or a ???
37858 * Creates and adds a new region if it doesn't already exist.
37859 * @param {String} target The target region key (north, south, east, west or center).
37860 * @param {Object} config The regions config object
37861 * @return {BorderLayoutRegion} The new region
37863 addRegion : function(config)
37865 if(!this.regions[config.region]){
37866 var r = this.factory(config);
37867 this.bindRegion(r);
37869 return this.regions[config.region];
37873 bindRegion : function(r){
37874 this.regions[r.config.region] = r;
37876 r.on("visibilitychange", this.layout, this);
37877 r.on("paneladded", this.layout, this);
37878 r.on("panelremoved", this.layout, this);
37879 r.on("invalidated", this.layout, this);
37880 r.on("resized", this.onRegionResized, this);
37881 r.on("collapsed", this.onRegionCollapsed, this);
37882 r.on("expanded", this.onRegionExpanded, this);
37886 * Performs a layout update.
37888 layout : function()
37890 if(this.updating) {
37894 // render all the rebions if they have not been done alreayd?
37895 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37896 if(this.regions[region] && !this.regions[region].bodyEl){
37897 this.regions[region].onRender(this.el)
37901 var size = this.getViewSize();
37902 var w = size.width;
37903 var h = size.height;
37908 //var x = 0, y = 0;
37910 var rs = this.regions;
37911 var north = rs["north"];
37912 var south = rs["south"];
37913 var west = rs["west"];
37914 var east = rs["east"];
37915 var center = rs["center"];
37916 //if(this.hideOnLayout){ // not supported anymore
37917 //c.el.setStyle("display", "none");
37919 if(north && north.isVisible()){
37920 var b = north.getBox();
37921 var m = north.getMargins();
37922 b.width = w - (m.left+m.right);
37925 centerY = b.height + b.y + m.bottom;
37926 centerH -= centerY;
37927 north.updateBox(this.safeBox(b));
37929 if(south && south.isVisible()){
37930 var b = south.getBox();
37931 var m = south.getMargins();
37932 b.width = w - (m.left+m.right);
37934 var totalHeight = (b.height + m.top + m.bottom);
37935 b.y = h - totalHeight + m.top;
37936 centerH -= totalHeight;
37937 south.updateBox(this.safeBox(b));
37939 if(west && west.isVisible()){
37940 var b = west.getBox();
37941 var m = west.getMargins();
37942 b.height = centerH - (m.top+m.bottom);
37944 b.y = centerY + m.top;
37945 var totalWidth = (b.width + m.left + m.right);
37946 centerX += totalWidth;
37947 centerW -= totalWidth;
37948 west.updateBox(this.safeBox(b));
37950 if(east && east.isVisible()){
37951 var b = east.getBox();
37952 var m = east.getMargins();
37953 b.height = centerH - (m.top+m.bottom);
37954 var totalWidth = (b.width + m.left + m.right);
37955 b.x = w - totalWidth + m.left;
37956 b.y = centerY + m.top;
37957 centerW -= totalWidth;
37958 east.updateBox(this.safeBox(b));
37961 var m = center.getMargins();
37963 x: centerX + m.left,
37964 y: centerY + m.top,
37965 width: centerW - (m.left+m.right),
37966 height: centerH - (m.top+m.bottom)
37968 //if(this.hideOnLayout){
37969 //center.el.setStyle("display", "block");
37971 center.updateBox(this.safeBox(centerBox));
37974 this.fireEvent("layout", this);
37978 safeBox : function(box){
37979 box.width = Math.max(0, box.width);
37980 box.height = Math.max(0, box.height);
37985 * Adds a ContentPanel (or subclass) to this layout.
37986 * @param {String} target The target region key (north, south, east, west or center).
37987 * @param {Roo.ContentPanel} panel The panel to add
37988 * @return {Roo.ContentPanel} The added panel
37990 add : function(target, panel){
37992 target = target.toLowerCase();
37993 return this.regions[target].add(panel);
37997 * Remove a ContentPanel (or subclass) to this layout.
37998 * @param {String} target The target region key (north, south, east, west or center).
37999 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38000 * @return {Roo.ContentPanel} The removed panel
38002 remove : function(target, panel){
38003 target = target.toLowerCase();
38004 return this.regions[target].remove(panel);
38008 * Searches all regions for a panel with the specified id
38009 * @param {String} panelId
38010 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38012 findPanel : function(panelId){
38013 var rs = this.regions;
38014 for(var target in rs){
38015 if(typeof rs[target] != "function"){
38016 var p = rs[target].getPanel(panelId);
38026 * Searches all regions for a panel with the specified id and activates (shows) it.
38027 * @param {String/ContentPanel} panelId The panels id or the panel itself
38028 * @return {Roo.ContentPanel} The shown panel or null
38030 showPanel : function(panelId) {
38031 var rs = this.regions;
38032 for(var target in rs){
38033 var r = rs[target];
38034 if(typeof r != "function"){
38035 if(r.hasPanel(panelId)){
38036 return r.showPanel(panelId);
38044 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38045 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38048 restoreState : function(provider){
38050 provider = Roo.state.Manager;
38052 var sm = new Roo.LayoutStateManager();
38053 sm.init(this, provider);
38059 * Adds a xtype elements to the layout.
38063 xtype : 'ContentPanel',
38070 xtype : 'NestedLayoutPanel',
38076 items : [ ... list of content panels or nested layout panels.. ]
38080 * @param {Object} cfg Xtype definition of item to add.
38082 addxtype : function(cfg)
38084 // basically accepts a pannel...
38085 // can accept a layout region..!?!?
38086 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38089 // theory? children can only be panels??
38091 //if (!cfg.xtype.match(/Panel$/)) {
38096 if (typeof(cfg.region) == 'undefined') {
38097 Roo.log("Failed to add Panel, region was not set");
38101 var region = cfg.region;
38107 xitems = cfg.items;
38112 if ( region == 'center') {
38113 Roo.log("Center: " + cfg.title);
38119 case 'Content': // ContentPanel (el, cfg)
38120 case 'Scroll': // ContentPanel (el, cfg)
38122 cfg.autoCreate = cfg.autoCreate || true;
38123 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38125 // var el = this.el.createChild();
38126 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38129 this.add(region, ret);
38133 case 'TreePanel': // our new panel!
38134 cfg.el = this.el.createChild();
38135 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38136 this.add(region, ret);
38141 // create a new Layout (which is a Border Layout...
38143 var clayout = cfg.layout;
38144 clayout.el = this.el.createChild();
38145 clayout.items = clayout.items || [];
38149 // replace this exitems with the clayout ones..
38150 xitems = clayout.items;
38152 // force background off if it's in center...
38153 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38154 cfg.background = false;
38156 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38159 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38160 //console.log('adding nested layout panel ' + cfg.toSource());
38161 this.add(region, ret);
38162 nb = {}; /// find first...
38167 // needs grid and region
38169 //var el = this.getRegion(region).el.createChild();
38171 *var el = this.el.createChild();
38172 // create the grid first...
38173 cfg.grid.container = el;
38174 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38177 if (region == 'center' && this.active ) {
38178 cfg.background = false;
38181 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38183 this.add(region, ret);
38185 if (cfg.background) {
38186 // render grid on panel activation (if panel background)
38187 ret.on('activate', function(gp) {
38188 if (!gp.grid.rendered) {
38189 // gp.grid.render(el);
38193 // cfg.grid.render(el);
38199 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38200 // it was the old xcomponent building that caused this before.
38201 // espeically if border is the top element in the tree.
38211 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38213 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38214 this.add(region, ret);
38218 throw "Can not add '" + cfg.xtype + "' to Border";
38224 this.beginUpdate();
38228 Roo.each(xitems, function(i) {
38229 region = nb && i.region ? i.region : false;
38231 var add = ret.addxtype(i);
38234 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38235 if (!i.background) {
38236 abn[region] = nb[region] ;
38243 // make the last non-background panel active..
38244 //if (nb) { Roo.log(abn); }
38247 for(var r in abn) {
38248 region = this.getRegion(r);
38250 // tried using nb[r], but it does not work..
38252 region.showPanel(abn[r]);
38263 factory : function(cfg)
38266 var validRegions = Roo.bootstrap.layout.Border.regions;
38268 var target = cfg.region;
38271 var r = Roo.bootstrap.layout;
38275 return new r.North(cfg);
38277 return new r.South(cfg);
38279 return new r.East(cfg);
38281 return new r.West(cfg);
38283 return new r.Center(cfg);
38285 throw 'Layout region "'+target+'" not supported.';
38292 * Ext JS Library 1.1.1
38293 * Copyright(c) 2006-2007, Ext JS, LLC.
38295 * Originally Released Under LGPL - original licence link has changed is not relivant.
38298 * <script type="text/javascript">
38302 * @class Roo.bootstrap.layout.Basic
38303 * @extends Roo.util.Observable
38304 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38305 * and does not have a titlebar, tabs or any other features. All it does is size and position
38306 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38307 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38308 * @cfg {string} region the region that it inhabits..
38309 * @cfg {bool} skipConfig skip config?
38313 Roo.bootstrap.layout.Basic = function(config){
38315 this.mgr = config.mgr;
38317 this.position = config.region;
38319 var skipConfig = config.skipConfig;
38323 * @scope Roo.BasicLayoutRegion
38327 * @event beforeremove
38328 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38329 * @param {Roo.LayoutRegion} this
38330 * @param {Roo.ContentPanel} panel The panel
38331 * @param {Object} e The cancel event object
38333 "beforeremove" : true,
38335 * @event invalidated
38336 * Fires when the layout for this region is changed.
38337 * @param {Roo.LayoutRegion} this
38339 "invalidated" : true,
38341 * @event visibilitychange
38342 * Fires when this region is shown or hidden
38343 * @param {Roo.LayoutRegion} this
38344 * @param {Boolean} visibility true or false
38346 "visibilitychange" : true,
38348 * @event paneladded
38349 * Fires when a panel is added.
38350 * @param {Roo.LayoutRegion} this
38351 * @param {Roo.ContentPanel} panel The panel
38353 "paneladded" : true,
38355 * @event panelremoved
38356 * Fires when a panel is removed.
38357 * @param {Roo.LayoutRegion} this
38358 * @param {Roo.ContentPanel} panel The panel
38360 "panelremoved" : true,
38362 * @event beforecollapse
38363 * Fires when this region before collapse.
38364 * @param {Roo.LayoutRegion} this
38366 "beforecollapse" : true,
38369 * Fires when this region is collapsed.
38370 * @param {Roo.LayoutRegion} this
38372 "collapsed" : true,
38375 * Fires when this region is expanded.
38376 * @param {Roo.LayoutRegion} this
38381 * Fires when this region is slid into view.
38382 * @param {Roo.LayoutRegion} this
38384 "slideshow" : true,
38387 * Fires when this region slides out of view.
38388 * @param {Roo.LayoutRegion} this
38390 "slidehide" : true,
38392 * @event panelactivated
38393 * Fires when a panel is activated.
38394 * @param {Roo.LayoutRegion} this
38395 * @param {Roo.ContentPanel} panel The activated panel
38397 "panelactivated" : true,
38400 * Fires when the user resizes this region.
38401 * @param {Roo.LayoutRegion} this
38402 * @param {Number} newSize The new size (width for east/west, height for north/south)
38406 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38407 this.panels = new Roo.util.MixedCollection();
38408 this.panels.getKey = this.getPanelId.createDelegate(this);
38410 this.activePanel = null;
38411 // ensure listeners are added...
38413 if (config.listeners || config.events) {
38414 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38415 listeners : config.listeners || {},
38416 events : config.events || {}
38420 if(skipConfig !== true){
38421 this.applyConfig(config);
38425 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38427 getPanelId : function(p){
38431 applyConfig : function(config){
38432 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38433 this.config = config;
38438 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38439 * the width, for horizontal (north, south) the height.
38440 * @param {Number} newSize The new width or height
38442 resizeTo : function(newSize){
38443 var el = this.el ? this.el :
38444 (this.activePanel ? this.activePanel.getEl() : null);
38446 switch(this.position){
38449 el.setWidth(newSize);
38450 this.fireEvent("resized", this, newSize);
38454 el.setHeight(newSize);
38455 this.fireEvent("resized", this, newSize);
38461 getBox : function(){
38462 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38465 getMargins : function(){
38466 return this.margins;
38469 updateBox : function(box){
38471 var el = this.activePanel.getEl();
38472 el.dom.style.left = box.x + "px";
38473 el.dom.style.top = box.y + "px";
38474 this.activePanel.setSize(box.width, box.height);
38478 * Returns the container element for this region.
38479 * @return {Roo.Element}
38481 getEl : function(){
38482 return this.activePanel;
38486 * Returns true if this region is currently visible.
38487 * @return {Boolean}
38489 isVisible : function(){
38490 return this.activePanel ? true : false;
38493 setActivePanel : function(panel){
38494 panel = this.getPanel(panel);
38495 if(this.activePanel && this.activePanel != panel){
38496 this.activePanel.setActiveState(false);
38497 this.activePanel.getEl().setLeftTop(-10000,-10000);
38499 this.activePanel = panel;
38500 panel.setActiveState(true);
38502 panel.setSize(this.box.width, this.box.height);
38504 this.fireEvent("panelactivated", this, panel);
38505 this.fireEvent("invalidated");
38509 * Show the specified panel.
38510 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38511 * @return {Roo.ContentPanel} The shown panel or null
38513 showPanel : function(panel){
38514 panel = this.getPanel(panel);
38516 this.setActivePanel(panel);
38522 * Get the active panel for this region.
38523 * @return {Roo.ContentPanel} The active panel or null
38525 getActivePanel : function(){
38526 return this.activePanel;
38530 * Add the passed ContentPanel(s)
38531 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38532 * @return {Roo.ContentPanel} The panel added (if only one was added)
38534 add : function(panel){
38535 if(arguments.length > 1){
38536 for(var i = 0, len = arguments.length; i < len; i++) {
38537 this.add(arguments[i]);
38541 if(this.hasPanel(panel)){
38542 this.showPanel(panel);
38545 var el = panel.getEl();
38546 if(el.dom.parentNode != this.mgr.el.dom){
38547 this.mgr.el.dom.appendChild(el.dom);
38549 if(panel.setRegion){
38550 panel.setRegion(this);
38552 this.panels.add(panel);
38553 el.setStyle("position", "absolute");
38554 if(!panel.background){
38555 this.setActivePanel(panel);
38556 if(this.config.initialSize && this.panels.getCount()==1){
38557 this.resizeTo(this.config.initialSize);
38560 this.fireEvent("paneladded", this, panel);
38565 * Returns true if the panel is in this region.
38566 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38567 * @return {Boolean}
38569 hasPanel : function(panel){
38570 if(typeof panel == "object"){ // must be panel obj
38571 panel = panel.getId();
38573 return this.getPanel(panel) ? true : false;
38577 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38578 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38579 * @param {Boolean} preservePanel Overrides the config preservePanel option
38580 * @return {Roo.ContentPanel} The panel that was removed
38582 remove : function(panel, preservePanel){
38583 panel = this.getPanel(panel);
38588 this.fireEvent("beforeremove", this, panel, e);
38589 if(e.cancel === true){
38592 var panelId = panel.getId();
38593 this.panels.removeKey(panelId);
38598 * Returns the panel specified or null if it's not in this region.
38599 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38600 * @return {Roo.ContentPanel}
38602 getPanel : function(id){
38603 if(typeof id == "object"){ // must be panel obj
38606 return this.panels.get(id);
38610 * Returns this regions position (north/south/east/west/center).
38613 getPosition: function(){
38614 return this.position;
38618 * Ext JS Library 1.1.1
38619 * Copyright(c) 2006-2007, Ext JS, LLC.
38621 * Originally Released Under LGPL - original licence link has changed is not relivant.
38624 * <script type="text/javascript">
38628 * @class Roo.bootstrap.layout.Region
38629 * @extends Roo.bootstrap.layout.Basic
38630 * This class represents a region in a layout manager.
38632 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38633 * @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})
38634 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38635 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38636 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38637 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38638 * @cfg {String} title The title for the region (overrides panel titles)
38639 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38640 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38641 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38642 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38643 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38644 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38645 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38646 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38647 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38648 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38650 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38651 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38652 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38653 * @cfg {Number} width For East/West panels
38654 * @cfg {Number} height For North/South panels
38655 * @cfg {Boolean} split To show the splitter
38656 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38658 * @cfg {string} cls Extra CSS classes to add to region
38660 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38661 * @cfg {string} region the region that it inhabits..
38664 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38665 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38667 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38668 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38669 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38671 Roo.bootstrap.layout.Region = function(config)
38673 this.applyConfig(config);
38675 var mgr = config.mgr;
38676 var pos = config.region;
38677 config.skipConfig = true;
38678 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38681 this.onRender(mgr.el);
38684 this.visible = true;
38685 this.collapsed = false;
38686 this.unrendered_panels = [];
38689 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38691 position: '', // set by wrapper (eg. north/south etc..)
38692 unrendered_panels : null, // unrendered panels.
38694 tabPosition : false,
38696 mgr: false, // points to 'Border'
38699 createBody : function(){
38700 /** This region's body element
38701 * @type Roo.Element */
38702 this.bodyEl = this.el.createChild({
38704 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38708 onRender: function(ctr, pos)
38710 var dh = Roo.DomHelper;
38711 /** This region's container element
38712 * @type Roo.Element */
38713 this.el = dh.append(ctr.dom, {
38715 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38717 /** This region's title element
38718 * @type Roo.Element */
38720 this.titleEl = dh.append(this.el.dom, {
38722 unselectable: "on",
38723 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38725 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38726 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38730 this.titleEl.enableDisplayMode();
38731 /** This region's title text element
38732 * @type HTMLElement */
38733 this.titleTextEl = this.titleEl.dom.firstChild;
38734 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38736 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38737 this.closeBtn.enableDisplayMode();
38738 this.closeBtn.on("click", this.closeClicked, this);
38739 this.closeBtn.hide();
38741 this.createBody(this.config);
38742 if(this.config.hideWhenEmpty){
38744 this.on("paneladded", this.validateVisibility, this);
38745 this.on("panelremoved", this.validateVisibility, this);
38747 if(this.autoScroll){
38748 this.bodyEl.setStyle("overflow", "auto");
38750 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38752 //if(c.titlebar !== false){
38753 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38754 this.titleEl.hide();
38756 this.titleEl.show();
38757 if(this.config.title){
38758 this.titleTextEl.innerHTML = this.config.title;
38762 if(this.config.collapsed){
38763 this.collapse(true);
38765 if(this.config.hidden){
38769 if (this.unrendered_panels && this.unrendered_panels.length) {
38770 for (var i =0;i< this.unrendered_panels.length; i++) {
38771 this.add(this.unrendered_panels[i]);
38773 this.unrendered_panels = null;
38779 applyConfig : function(c)
38782 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38783 var dh = Roo.DomHelper;
38784 if(c.titlebar !== false){
38785 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38786 this.collapseBtn.on("click", this.collapse, this);
38787 this.collapseBtn.enableDisplayMode();
38789 if(c.showPin === true || this.showPin){
38790 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38791 this.stickBtn.enableDisplayMode();
38792 this.stickBtn.on("click", this.expand, this);
38793 this.stickBtn.hide();
38798 /** This region's collapsed element
38799 * @type Roo.Element */
38802 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38803 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38806 if(c.floatable !== false){
38807 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38808 this.collapsedEl.on("click", this.collapseClick, this);
38811 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38812 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38813 id: "message", unselectable: "on", style:{"float":"left"}});
38814 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38816 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38817 this.expandBtn.on("click", this.expand, this);
38821 if(this.collapseBtn){
38822 this.collapseBtn.setVisible(c.collapsible == true);
38825 this.cmargins = c.cmargins || this.cmargins ||
38826 (this.position == "west" || this.position == "east" ?
38827 {top: 0, left: 2, right:2, bottom: 0} :
38828 {top: 2, left: 0, right:0, bottom: 2});
38830 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38833 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38835 this.autoScroll = c.autoScroll || false;
38840 this.duration = c.duration || .30;
38841 this.slideDuration = c.slideDuration || .45;
38846 * Returns true if this region is currently visible.
38847 * @return {Boolean}
38849 isVisible : function(){
38850 return this.visible;
38854 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38855 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38857 //setCollapsedTitle : function(title){
38858 // title = title || " ";
38859 // if(this.collapsedTitleTextEl){
38860 // this.collapsedTitleTextEl.innerHTML = title;
38864 getBox : function(){
38866 // if(!this.collapsed){
38867 b = this.el.getBox(false, true);
38869 // b = this.collapsedEl.getBox(false, true);
38874 getMargins : function(){
38875 return this.margins;
38876 //return this.collapsed ? this.cmargins : this.margins;
38879 highlight : function(){
38880 this.el.addClass("x-layout-panel-dragover");
38883 unhighlight : function(){
38884 this.el.removeClass("x-layout-panel-dragover");
38887 updateBox : function(box)
38889 if (!this.bodyEl) {
38890 return; // not rendered yet..
38894 if(!this.collapsed){
38895 this.el.dom.style.left = box.x + "px";
38896 this.el.dom.style.top = box.y + "px";
38897 this.updateBody(box.width, box.height);
38899 this.collapsedEl.dom.style.left = box.x + "px";
38900 this.collapsedEl.dom.style.top = box.y + "px";
38901 this.collapsedEl.setSize(box.width, box.height);
38904 this.tabs.autoSizeTabs();
38908 updateBody : function(w, h)
38911 this.el.setWidth(w);
38912 w -= this.el.getBorderWidth("rl");
38913 if(this.config.adjustments){
38914 w += this.config.adjustments[0];
38917 if(h !== null && h > 0){
38918 this.el.setHeight(h);
38919 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38920 h -= this.el.getBorderWidth("tb");
38921 if(this.config.adjustments){
38922 h += this.config.adjustments[1];
38924 this.bodyEl.setHeight(h);
38926 h = this.tabs.syncHeight(h);
38929 if(this.panelSize){
38930 w = w !== null ? w : this.panelSize.width;
38931 h = h !== null ? h : this.panelSize.height;
38933 if(this.activePanel){
38934 var el = this.activePanel.getEl();
38935 w = w !== null ? w : el.getWidth();
38936 h = h !== null ? h : el.getHeight();
38937 this.panelSize = {width: w, height: h};
38938 this.activePanel.setSize(w, h);
38940 if(Roo.isIE && this.tabs){
38941 this.tabs.el.repaint();
38946 * Returns the container element for this region.
38947 * @return {Roo.Element}
38949 getEl : function(){
38954 * Hides this region.
38957 //if(!this.collapsed){
38958 this.el.dom.style.left = "-2000px";
38961 // this.collapsedEl.dom.style.left = "-2000px";
38962 // this.collapsedEl.hide();
38964 this.visible = false;
38965 this.fireEvent("visibilitychange", this, false);
38969 * Shows this region if it was previously hidden.
38972 //if(!this.collapsed){
38975 // this.collapsedEl.show();
38977 this.visible = true;
38978 this.fireEvent("visibilitychange", this, true);
38981 closeClicked : function(){
38982 if(this.activePanel){
38983 this.remove(this.activePanel);
38987 collapseClick : function(e){
38989 e.stopPropagation();
38992 e.stopPropagation();
38998 * Collapses this region.
38999 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39002 collapse : function(skipAnim, skipCheck = false){
39003 if(this.collapsed) {
39007 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39009 this.collapsed = true;
39011 this.split.el.hide();
39013 if(this.config.animate && skipAnim !== true){
39014 this.fireEvent("invalidated", this);
39015 this.animateCollapse();
39017 this.el.setLocation(-20000,-20000);
39019 this.collapsedEl.show();
39020 this.fireEvent("collapsed", this);
39021 this.fireEvent("invalidated", this);
39027 animateCollapse : function(){
39032 * Expands this region if it was previously collapsed.
39033 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39034 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39037 expand : function(e, skipAnim){
39039 e.stopPropagation();
39041 if(!this.collapsed || this.el.hasActiveFx()) {
39045 this.afterSlideIn();
39048 this.collapsed = false;
39049 if(this.config.animate && skipAnim !== true){
39050 this.animateExpand();
39054 this.split.el.show();
39056 this.collapsedEl.setLocation(-2000,-2000);
39057 this.collapsedEl.hide();
39058 this.fireEvent("invalidated", this);
39059 this.fireEvent("expanded", this);
39063 animateExpand : function(){
39067 initTabs : function()
39069 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39071 var ts = new Roo.bootstrap.panel.Tabs({
39072 el: this.bodyEl.dom,
39074 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39075 disableTooltips: this.config.disableTabTips,
39076 toolbar : this.config.toolbar
39079 if(this.config.hideTabs){
39080 ts.stripWrap.setDisplayed(false);
39083 ts.resizeTabs = this.config.resizeTabs === true;
39084 ts.minTabWidth = this.config.minTabWidth || 40;
39085 ts.maxTabWidth = this.config.maxTabWidth || 250;
39086 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39087 ts.monitorResize = false;
39088 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39089 ts.bodyEl.addClass('roo-layout-tabs-body');
39090 this.panels.each(this.initPanelAsTab, this);
39093 initPanelAsTab : function(panel){
39094 var ti = this.tabs.addTab(
39098 this.config.closeOnTab && panel.isClosable(),
39101 if(panel.tabTip !== undefined){
39102 ti.setTooltip(panel.tabTip);
39104 ti.on("activate", function(){
39105 this.setActivePanel(panel);
39108 if(this.config.closeOnTab){
39109 ti.on("beforeclose", function(t, e){
39111 this.remove(panel);
39115 panel.tabItem = ti;
39120 updatePanelTitle : function(panel, title)
39122 if(this.activePanel == panel){
39123 this.updateTitle(title);
39126 var ti = this.tabs.getTab(panel.getEl().id);
39128 if(panel.tabTip !== undefined){
39129 ti.setTooltip(panel.tabTip);
39134 updateTitle : function(title){
39135 if(this.titleTextEl && !this.config.title){
39136 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39140 setActivePanel : function(panel)
39142 panel = this.getPanel(panel);
39143 if(this.activePanel && this.activePanel != panel){
39144 if(this.activePanel.setActiveState(false) === false){
39148 this.activePanel = panel;
39149 panel.setActiveState(true);
39150 if(this.panelSize){
39151 panel.setSize(this.panelSize.width, this.panelSize.height);
39154 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39156 this.updateTitle(panel.getTitle());
39158 this.fireEvent("invalidated", this);
39160 this.fireEvent("panelactivated", this, panel);
39164 * Shows the specified panel.
39165 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39166 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39168 showPanel : function(panel)
39170 panel = this.getPanel(panel);
39173 var tab = this.tabs.getTab(panel.getEl().id);
39174 if(tab.isHidden()){
39175 this.tabs.unhideTab(tab.id);
39179 this.setActivePanel(panel);
39186 * Get the active panel for this region.
39187 * @return {Roo.ContentPanel} The active panel or null
39189 getActivePanel : function(){
39190 return this.activePanel;
39193 validateVisibility : function(){
39194 if(this.panels.getCount() < 1){
39195 this.updateTitle(" ");
39196 this.closeBtn.hide();
39199 if(!this.isVisible()){
39206 * Adds the passed ContentPanel(s) to this region.
39207 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39208 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39210 add : function(panel)
39212 if(arguments.length > 1){
39213 for(var i = 0, len = arguments.length; i < len; i++) {
39214 this.add(arguments[i]);
39219 // if we have not been rendered yet, then we can not really do much of this..
39220 if (!this.bodyEl) {
39221 this.unrendered_panels.push(panel);
39228 if(this.hasPanel(panel)){
39229 this.showPanel(panel);
39232 panel.setRegion(this);
39233 this.panels.add(panel);
39234 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39235 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39236 // and hide them... ???
39237 this.bodyEl.dom.appendChild(panel.getEl().dom);
39238 if(panel.background !== true){
39239 this.setActivePanel(panel);
39241 this.fireEvent("paneladded", this, panel);
39248 this.initPanelAsTab(panel);
39252 if(panel.background !== true){
39253 this.tabs.activate(panel.getEl().id);
39255 this.fireEvent("paneladded", this, panel);
39260 * Hides the tab for the specified panel.
39261 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39263 hidePanel : function(panel){
39264 if(this.tabs && (panel = this.getPanel(panel))){
39265 this.tabs.hideTab(panel.getEl().id);
39270 * Unhides the tab for a previously hidden panel.
39271 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39273 unhidePanel : function(panel){
39274 if(this.tabs && (panel = this.getPanel(panel))){
39275 this.tabs.unhideTab(panel.getEl().id);
39279 clearPanels : function(){
39280 while(this.panels.getCount() > 0){
39281 this.remove(this.panels.first());
39286 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39287 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39288 * @param {Boolean} preservePanel Overrides the config preservePanel option
39289 * @return {Roo.ContentPanel} The panel that was removed
39291 remove : function(panel, preservePanel)
39293 panel = this.getPanel(panel);
39298 this.fireEvent("beforeremove", this, panel, e);
39299 if(e.cancel === true){
39302 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39303 var panelId = panel.getId();
39304 this.panels.removeKey(panelId);
39306 document.body.appendChild(panel.getEl().dom);
39309 this.tabs.removeTab(panel.getEl().id);
39310 }else if (!preservePanel){
39311 this.bodyEl.dom.removeChild(panel.getEl().dom);
39313 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39314 var p = this.panels.first();
39315 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39316 tempEl.appendChild(p.getEl().dom);
39317 this.bodyEl.update("");
39318 this.bodyEl.dom.appendChild(p.getEl().dom);
39320 this.updateTitle(p.getTitle());
39322 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39323 this.setActivePanel(p);
39325 panel.setRegion(null);
39326 if(this.activePanel == panel){
39327 this.activePanel = null;
39329 if(this.config.autoDestroy !== false && preservePanel !== true){
39330 try{panel.destroy();}catch(e){}
39332 this.fireEvent("panelremoved", this, panel);
39337 * Returns the TabPanel component used by this region
39338 * @return {Roo.TabPanel}
39340 getTabs : function(){
39344 createTool : function(parentEl, className){
39345 var btn = Roo.DomHelper.append(parentEl, {
39347 cls: "x-layout-tools-button",
39350 cls: "roo-layout-tools-button-inner " + className,
39354 btn.addClassOnOver("roo-layout-tools-button-over");
39359 * Ext JS Library 1.1.1
39360 * Copyright(c) 2006-2007, Ext JS, LLC.
39362 * Originally Released Under LGPL - original licence link has changed is not relivant.
39365 * <script type="text/javascript">
39371 * @class Roo.SplitLayoutRegion
39372 * @extends Roo.LayoutRegion
39373 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39375 Roo.bootstrap.layout.Split = function(config){
39376 this.cursor = config.cursor;
39377 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39380 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39382 splitTip : "Drag to resize.",
39383 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39384 useSplitTips : false,
39386 applyConfig : function(config){
39387 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39390 onRender : function(ctr,pos) {
39392 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39393 if(!this.config.split){
39398 var splitEl = Roo.DomHelper.append(ctr.dom, {
39400 id: this.el.id + "-split",
39401 cls: "roo-layout-split roo-layout-split-"+this.position,
39404 /** The SplitBar for this region
39405 * @type Roo.SplitBar */
39406 // does not exist yet...
39407 Roo.log([this.position, this.orientation]);
39409 this.split = new Roo.bootstrap.SplitBar({
39410 dragElement : splitEl,
39411 resizingElement: this.el,
39412 orientation : this.orientation
39415 this.split.on("moved", this.onSplitMove, this);
39416 this.split.useShim = this.config.useShim === true;
39417 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39418 if(this.useSplitTips){
39419 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39421 //if(config.collapsible){
39422 // this.split.el.on("dblclick", this.collapse, this);
39425 if(typeof this.config.minSize != "undefined"){
39426 this.split.minSize = this.config.minSize;
39428 if(typeof this.config.maxSize != "undefined"){
39429 this.split.maxSize = this.config.maxSize;
39431 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39432 this.hideSplitter();
39437 getHMaxSize : function(){
39438 var cmax = this.config.maxSize || 10000;
39439 var center = this.mgr.getRegion("center");
39440 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39443 getVMaxSize : function(){
39444 var cmax = this.config.maxSize || 10000;
39445 var center = this.mgr.getRegion("center");
39446 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39449 onSplitMove : function(split, newSize){
39450 this.fireEvent("resized", this, newSize);
39454 * Returns the {@link Roo.SplitBar} for this region.
39455 * @return {Roo.SplitBar}
39457 getSplitBar : function(){
39462 this.hideSplitter();
39463 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39466 hideSplitter : function(){
39468 this.split.el.setLocation(-2000,-2000);
39469 this.split.el.hide();
39475 this.split.el.show();
39477 Roo.bootstrap.layout.Split.superclass.show.call(this);
39480 beforeSlide: function(){
39481 if(Roo.isGecko){// firefox overflow auto bug workaround
39482 this.bodyEl.clip();
39484 this.tabs.bodyEl.clip();
39486 if(this.activePanel){
39487 this.activePanel.getEl().clip();
39489 if(this.activePanel.beforeSlide){
39490 this.activePanel.beforeSlide();
39496 afterSlide : function(){
39497 if(Roo.isGecko){// firefox overflow auto bug workaround
39498 this.bodyEl.unclip();
39500 this.tabs.bodyEl.unclip();
39502 if(this.activePanel){
39503 this.activePanel.getEl().unclip();
39504 if(this.activePanel.afterSlide){
39505 this.activePanel.afterSlide();
39511 initAutoHide : function(){
39512 if(this.autoHide !== false){
39513 if(!this.autoHideHd){
39514 var st = new Roo.util.DelayedTask(this.slideIn, this);
39515 this.autoHideHd = {
39516 "mouseout": function(e){
39517 if(!e.within(this.el, true)){
39521 "mouseover" : function(e){
39527 this.el.on(this.autoHideHd);
39531 clearAutoHide : function(){
39532 if(this.autoHide !== false){
39533 this.el.un("mouseout", this.autoHideHd.mouseout);
39534 this.el.un("mouseover", this.autoHideHd.mouseover);
39538 clearMonitor : function(){
39539 Roo.get(document).un("click", this.slideInIf, this);
39542 // these names are backwards but not changed for compat
39543 slideOut : function(){
39544 if(this.isSlid || this.el.hasActiveFx()){
39547 this.isSlid = true;
39548 if(this.collapseBtn){
39549 this.collapseBtn.hide();
39551 this.closeBtnState = this.closeBtn.getStyle('display');
39552 this.closeBtn.hide();
39554 this.stickBtn.show();
39557 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39558 this.beforeSlide();
39559 this.el.setStyle("z-index", 10001);
39560 this.el.slideIn(this.getSlideAnchor(), {
39561 callback: function(){
39563 this.initAutoHide();
39564 Roo.get(document).on("click", this.slideInIf, this);
39565 this.fireEvent("slideshow", this);
39572 afterSlideIn : function(){
39573 this.clearAutoHide();
39574 this.isSlid = false;
39575 this.clearMonitor();
39576 this.el.setStyle("z-index", "");
39577 if(this.collapseBtn){
39578 this.collapseBtn.show();
39580 this.closeBtn.setStyle('display', this.closeBtnState);
39582 this.stickBtn.hide();
39584 this.fireEvent("slidehide", this);
39587 slideIn : function(cb){
39588 if(!this.isSlid || this.el.hasActiveFx()){
39592 this.isSlid = false;
39593 this.beforeSlide();
39594 this.el.slideOut(this.getSlideAnchor(), {
39595 callback: function(){
39596 this.el.setLeftTop(-10000, -10000);
39598 this.afterSlideIn();
39606 slideInIf : function(e){
39607 if(!e.within(this.el)){
39612 animateCollapse : function(){
39613 this.beforeSlide();
39614 this.el.setStyle("z-index", 20000);
39615 var anchor = this.getSlideAnchor();
39616 this.el.slideOut(anchor, {
39617 callback : function(){
39618 this.el.setStyle("z-index", "");
39619 this.collapsedEl.slideIn(anchor, {duration:.3});
39621 this.el.setLocation(-10000,-10000);
39623 this.fireEvent("collapsed", this);
39630 animateExpand : function(){
39631 this.beforeSlide();
39632 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39633 this.el.setStyle("z-index", 20000);
39634 this.collapsedEl.hide({
39637 this.el.slideIn(this.getSlideAnchor(), {
39638 callback : function(){
39639 this.el.setStyle("z-index", "");
39642 this.split.el.show();
39644 this.fireEvent("invalidated", this);
39645 this.fireEvent("expanded", this);
39673 getAnchor : function(){
39674 return this.anchors[this.position];
39677 getCollapseAnchor : function(){
39678 return this.canchors[this.position];
39681 getSlideAnchor : function(){
39682 return this.sanchors[this.position];
39685 getAlignAdj : function(){
39686 var cm = this.cmargins;
39687 switch(this.position){
39703 getExpandAdj : function(){
39704 var c = this.collapsedEl, cm = this.cmargins;
39705 switch(this.position){
39707 return [-(cm.right+c.getWidth()+cm.left), 0];
39710 return [cm.right+c.getWidth()+cm.left, 0];
39713 return [0, -(cm.top+cm.bottom+c.getHeight())];
39716 return [0, cm.top+cm.bottom+c.getHeight()];
39722 * Ext JS Library 1.1.1
39723 * Copyright(c) 2006-2007, Ext JS, LLC.
39725 * Originally Released Under LGPL - original licence link has changed is not relivant.
39728 * <script type="text/javascript">
39731 * These classes are private internal classes
39733 Roo.bootstrap.layout.Center = function(config){
39734 config.region = "center";
39735 Roo.bootstrap.layout.Region.call(this, config);
39736 this.visible = true;
39737 this.minWidth = config.minWidth || 20;
39738 this.minHeight = config.minHeight || 20;
39741 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39743 // center panel can't be hidden
39747 // center panel can't be hidden
39750 getMinWidth: function(){
39751 return this.minWidth;
39754 getMinHeight: function(){
39755 return this.minHeight;
39769 Roo.bootstrap.layout.North = function(config)
39771 config.region = 'north';
39772 config.cursor = 'n-resize';
39774 Roo.bootstrap.layout.Split.call(this, config);
39778 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39779 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39780 this.split.el.addClass("roo-layout-split-v");
39782 //var size = config.initialSize || config.height;
39783 //if(this.el && typeof size != "undefined"){
39784 // this.el.setHeight(size);
39787 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39789 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39792 onRender : function(ctr, pos)
39794 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39795 var size = this.config.initialSize || this.config.height;
39796 if(this.el && typeof size != "undefined"){
39797 this.el.setHeight(size);
39802 getBox : function(){
39803 if(this.collapsed){
39804 return this.collapsedEl.getBox();
39806 var box = this.el.getBox();
39808 box.height += this.split.el.getHeight();
39813 updateBox : function(box){
39814 if(this.split && !this.collapsed){
39815 box.height -= this.split.el.getHeight();
39816 this.split.el.setLeft(box.x);
39817 this.split.el.setTop(box.y+box.height);
39818 this.split.el.setWidth(box.width);
39820 if(this.collapsed){
39821 this.updateBody(box.width, null);
39823 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39831 Roo.bootstrap.layout.South = function(config){
39832 config.region = 'south';
39833 config.cursor = 's-resize';
39834 Roo.bootstrap.layout.Split.call(this, config);
39836 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39837 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39838 this.split.el.addClass("roo-layout-split-v");
39843 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39844 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39846 onRender : function(ctr, pos)
39848 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39849 var size = this.config.initialSize || this.config.height;
39850 if(this.el && typeof size != "undefined"){
39851 this.el.setHeight(size);
39856 getBox : function(){
39857 if(this.collapsed){
39858 return this.collapsedEl.getBox();
39860 var box = this.el.getBox();
39862 var sh = this.split.el.getHeight();
39869 updateBox : function(box){
39870 if(this.split && !this.collapsed){
39871 var sh = this.split.el.getHeight();
39874 this.split.el.setLeft(box.x);
39875 this.split.el.setTop(box.y-sh);
39876 this.split.el.setWidth(box.width);
39878 if(this.collapsed){
39879 this.updateBody(box.width, null);
39881 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39885 Roo.bootstrap.layout.East = function(config){
39886 config.region = "east";
39887 config.cursor = "e-resize";
39888 Roo.bootstrap.layout.Split.call(this, config);
39890 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39891 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39892 this.split.el.addClass("roo-layout-split-h");
39896 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39897 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39899 onRender : function(ctr, pos)
39901 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39902 var size = this.config.initialSize || this.config.width;
39903 if(this.el && typeof size != "undefined"){
39904 this.el.setWidth(size);
39909 getBox : function(){
39910 if(this.collapsed){
39911 return this.collapsedEl.getBox();
39913 var box = this.el.getBox();
39915 var sw = this.split.el.getWidth();
39922 updateBox : function(box){
39923 if(this.split && !this.collapsed){
39924 var sw = this.split.el.getWidth();
39926 this.split.el.setLeft(box.x);
39927 this.split.el.setTop(box.y);
39928 this.split.el.setHeight(box.height);
39931 if(this.collapsed){
39932 this.updateBody(null, box.height);
39934 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39938 Roo.bootstrap.layout.West = function(config){
39939 config.region = "west";
39940 config.cursor = "w-resize";
39942 Roo.bootstrap.layout.Split.call(this, config);
39944 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39945 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39946 this.split.el.addClass("roo-layout-split-h");
39950 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39951 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39953 onRender: function(ctr, pos)
39955 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39956 var size = this.config.initialSize || this.config.width;
39957 if(typeof size != "undefined"){
39958 this.el.setWidth(size);
39962 getBox : function(){
39963 if(this.collapsed){
39964 return this.collapsedEl.getBox();
39966 var box = this.el.getBox();
39967 if (box.width == 0) {
39968 box.width = this.config.width; // kludge?
39971 box.width += this.split.el.getWidth();
39976 updateBox : function(box){
39977 if(this.split && !this.collapsed){
39978 var sw = this.split.el.getWidth();
39980 this.split.el.setLeft(box.x+box.width);
39981 this.split.el.setTop(box.y);
39982 this.split.el.setHeight(box.height);
39984 if(this.collapsed){
39985 this.updateBody(null, box.height);
39987 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39989 });Roo.namespace("Roo.bootstrap.panel");/*
39991 * Ext JS Library 1.1.1
39992 * Copyright(c) 2006-2007, Ext JS, LLC.
39994 * Originally Released Under LGPL - original licence link has changed is not relivant.
39997 * <script type="text/javascript">
40000 * @class Roo.ContentPanel
40001 * @extends Roo.util.Observable
40002 * A basic ContentPanel element.
40003 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40004 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40005 * @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
40006 * @cfg {Boolean} closable True if the panel can be closed/removed
40007 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40008 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40009 * @cfg {Toolbar} toolbar A toolbar for this panel
40010 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40011 * @cfg {String} title The title for this panel
40012 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40013 * @cfg {String} url Calls {@link #setUrl} with this value
40014 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40015 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40016 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40017 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40018 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40019 * @cfg {Boolean} badges render the badges
40020 * @cfg {String} cls extra classes to use
40021 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40024 * Create a new ContentPanel.
40025 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40026 * @param {String/Object} config A string to set only the title or a config object
40027 * @param {String} content (optional) Set the HTML content for this panel
40028 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40030 Roo.bootstrap.panel.Content = function( config){
40032 this.tpl = config.tpl || false;
40034 var el = config.el;
40035 var content = config.content;
40037 if(config.autoCreate){ // xtype is available if this is called from factory
40040 this.el = Roo.get(el);
40041 if(!this.el && config && config.autoCreate){
40042 if(typeof config.autoCreate == "object"){
40043 if(!config.autoCreate.id){
40044 config.autoCreate.id = config.id||el;
40046 this.el = Roo.DomHelper.append(document.body,
40047 config.autoCreate, true);
40051 cls: (config.cls || '') +
40052 (config.background ? ' bg-' + config.background : '') +
40053 " roo-layout-inactive-content",
40056 if (config.iframe) {
40060 style : 'border: 0px',
40061 src : 'about:blank'
40067 elcfg.html = config.html;
40071 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40072 if (config.iframe) {
40073 this.iframeEl = this.el.select('iframe',true).first();
40078 this.closable = false;
40079 this.loaded = false;
40080 this.active = false;
40083 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40085 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40087 this.wrapEl = this.el; //this.el.wrap();
40089 if (config.toolbar.items) {
40090 ti = config.toolbar.items ;
40091 delete config.toolbar.items ;
40095 this.toolbar.render(this.wrapEl, 'before');
40096 for(var i =0;i < ti.length;i++) {
40097 // Roo.log(['add child', items[i]]);
40098 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40100 this.toolbar.items = nitems;
40101 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40102 delete config.toolbar;
40106 // xtype created footer. - not sure if will work as we normally have to render first..
40107 if (this.footer && !this.footer.el && this.footer.xtype) {
40108 if (!this.wrapEl) {
40109 this.wrapEl = this.el.wrap();
40112 this.footer.container = this.wrapEl.createChild();
40114 this.footer = Roo.factory(this.footer, Roo);
40119 if(typeof config == "string"){
40120 this.title = config;
40122 Roo.apply(this, config);
40126 this.resizeEl = Roo.get(this.resizeEl, true);
40128 this.resizeEl = this.el;
40130 // handle view.xtype
40138 * Fires when this panel is activated.
40139 * @param {Roo.ContentPanel} this
40143 * @event deactivate
40144 * Fires when this panel is activated.
40145 * @param {Roo.ContentPanel} this
40147 "deactivate" : true,
40151 * Fires when this panel is resized if fitToFrame is true.
40152 * @param {Roo.ContentPanel} this
40153 * @param {Number} width The width after any component adjustments
40154 * @param {Number} height The height after any component adjustments
40160 * Fires when this tab is created
40161 * @param {Roo.ContentPanel} this
40172 if(this.autoScroll && !this.iframe){
40173 this.resizeEl.setStyle("overflow", "auto");
40175 // fix randome scrolling
40176 //this.el.on('scroll', function() {
40177 // Roo.log('fix random scolling');
40178 // this.scrollTo('top',0);
40181 content = content || this.content;
40183 this.setContent(content);
40185 if(config && config.url){
40186 this.setUrl(this.url, this.params, this.loadOnce);
40191 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40193 if (this.view && typeof(this.view.xtype) != 'undefined') {
40194 this.view.el = this.el.appendChild(document.createElement("div"));
40195 this.view = Roo.factory(this.view);
40196 this.view.render && this.view.render(false, '');
40200 this.fireEvent('render', this);
40203 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40213 setRegion : function(region){
40214 this.region = region;
40215 this.setActiveClass(region && !this.background);
40219 setActiveClass: function(state)
40222 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40223 this.el.setStyle('position','relative');
40225 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40226 this.el.setStyle('position', 'absolute');
40231 * Returns the toolbar for this Panel if one was configured.
40232 * @return {Roo.Toolbar}
40234 getToolbar : function(){
40235 return this.toolbar;
40238 setActiveState : function(active)
40240 this.active = active;
40241 this.setActiveClass(active);
40243 if(this.fireEvent("deactivate", this) === false){
40248 this.fireEvent("activate", this);
40252 * Updates this panel's element (not for iframe)
40253 * @param {String} content The new content
40254 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40256 setContent : function(content, loadScripts){
40261 this.el.update(content, loadScripts);
40264 ignoreResize : function(w, h){
40265 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40268 this.lastSize = {width: w, height: h};
40273 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40274 * @return {Roo.UpdateManager} The UpdateManager
40276 getUpdateManager : function(){
40280 return this.el.getUpdateManager();
40283 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40284 * Does not work with IFRAME contents
40285 * @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:
40288 url: "your-url.php",
40289 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40290 callback: yourFunction,
40291 scope: yourObject, //(optional scope)
40294 text: "Loading...",
40300 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40301 * 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.
40302 * @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}
40303 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40304 * @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.
40305 * @return {Roo.ContentPanel} this
40313 var um = this.el.getUpdateManager();
40314 um.update.apply(um, arguments);
40320 * 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.
40321 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40322 * @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)
40323 * @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)
40324 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40326 setUrl : function(url, params, loadOnce){
40328 this.iframeEl.dom.src = url;
40332 if(this.refreshDelegate){
40333 this.removeListener("activate", this.refreshDelegate);
40335 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40336 this.on("activate", this.refreshDelegate);
40337 return this.el.getUpdateManager();
40340 _handleRefresh : function(url, params, loadOnce){
40341 if(!loadOnce || !this.loaded){
40342 var updater = this.el.getUpdateManager();
40343 updater.update(url, params, this._setLoaded.createDelegate(this));
40347 _setLoaded : function(){
40348 this.loaded = true;
40352 * Returns this panel's id
40355 getId : function(){
40360 * Returns this panel's element - used by regiosn to add.
40361 * @return {Roo.Element}
40363 getEl : function(){
40364 return this.wrapEl || this.el;
40369 adjustForComponents : function(width, height)
40371 //Roo.log('adjustForComponents ');
40372 if(this.resizeEl != this.el){
40373 width -= this.el.getFrameWidth('lr');
40374 height -= this.el.getFrameWidth('tb');
40377 var te = this.toolbar.getEl();
40378 te.setWidth(width);
40379 height -= te.getHeight();
40382 var te = this.footer.getEl();
40383 te.setWidth(width);
40384 height -= te.getHeight();
40388 if(this.adjustments){
40389 width += this.adjustments[0];
40390 height += this.adjustments[1];
40392 return {"width": width, "height": height};
40395 setSize : function(width, height){
40396 if(this.fitToFrame && !this.ignoreResize(width, height)){
40397 if(this.fitContainer && this.resizeEl != this.el){
40398 this.el.setSize(width, height);
40400 var size = this.adjustForComponents(width, height);
40402 this.iframeEl.setSize(width,height);
40405 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40406 this.fireEvent('resize', this, size.width, size.height);
40413 * Returns this panel's title
40416 getTitle : function(){
40418 if (typeof(this.title) != 'object') {
40423 for (var k in this.title) {
40424 if (!this.title.hasOwnProperty(k)) {
40428 if (k.indexOf('-') >= 0) {
40429 var s = k.split('-');
40430 for (var i = 0; i<s.length; i++) {
40431 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40434 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40441 * Set this panel's title
40442 * @param {String} title
40444 setTitle : function(title){
40445 this.title = title;
40447 this.region.updatePanelTitle(this, title);
40452 * Returns true is this panel was configured to be closable
40453 * @return {Boolean}
40455 isClosable : function(){
40456 return this.closable;
40459 beforeSlide : function(){
40461 this.resizeEl.clip();
40464 afterSlide : function(){
40466 this.resizeEl.unclip();
40470 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40471 * Will fail silently if the {@link #setUrl} method has not been called.
40472 * This does not activate the panel, just updates its content.
40474 refresh : function(){
40475 if(this.refreshDelegate){
40476 this.loaded = false;
40477 this.refreshDelegate();
40482 * Destroys this panel
40484 destroy : function(){
40485 this.el.removeAllListeners();
40486 var tempEl = document.createElement("span");
40487 tempEl.appendChild(this.el.dom);
40488 tempEl.innerHTML = "";
40494 * form - if the content panel contains a form - this is a reference to it.
40495 * @type {Roo.form.Form}
40499 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40500 * This contains a reference to it.
40506 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40516 * @param {Object} cfg Xtype definition of item to add.
40520 getChildContainer: function () {
40521 return this.getEl();
40526 var ret = new Roo.factory(cfg);
40531 if (cfg.xtype.match(/^Form$/)) {
40534 //if (this.footer) {
40535 // el = this.footer.container.insertSibling(false, 'before');
40537 el = this.el.createChild();
40540 this.form = new Roo.form.Form(cfg);
40543 if ( this.form.allItems.length) {
40544 this.form.render(el.dom);
40548 // should only have one of theses..
40549 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40550 // views.. should not be just added - used named prop 'view''
40552 cfg.el = this.el.appendChild(document.createElement("div"));
40555 var ret = new Roo.factory(cfg);
40557 ret.render && ret.render(false, ''); // render blank..
40567 * @class Roo.bootstrap.panel.Grid
40568 * @extends Roo.bootstrap.panel.Content
40570 * Create a new GridPanel.
40571 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40572 * @param {Object} config A the config object
40578 Roo.bootstrap.panel.Grid = function(config)
40582 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40583 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40585 config.el = this.wrapper;
40586 //this.el = this.wrapper;
40588 if (config.container) {
40589 // ctor'ed from a Border/panel.grid
40592 this.wrapper.setStyle("overflow", "hidden");
40593 this.wrapper.addClass('roo-grid-container');
40598 if(config.toolbar){
40599 var tool_el = this.wrapper.createChild();
40600 this.toolbar = Roo.factory(config.toolbar);
40602 if (config.toolbar.items) {
40603 ti = config.toolbar.items ;
40604 delete config.toolbar.items ;
40608 this.toolbar.render(tool_el);
40609 for(var i =0;i < ti.length;i++) {
40610 // Roo.log(['add child', items[i]]);
40611 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40613 this.toolbar.items = nitems;
40615 delete config.toolbar;
40618 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40619 config.grid.scrollBody = true;;
40620 config.grid.monitorWindowResize = false; // turn off autosizing
40621 config.grid.autoHeight = false;
40622 config.grid.autoWidth = false;
40624 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40626 if (config.background) {
40627 // render grid on panel activation (if panel background)
40628 this.on('activate', function(gp) {
40629 if (!gp.grid.rendered) {
40630 gp.grid.render(this.wrapper);
40631 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40636 this.grid.render(this.wrapper);
40637 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40640 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40641 // ??? needed ??? config.el = this.wrapper;
40646 // xtype created footer. - not sure if will work as we normally have to render first..
40647 if (this.footer && !this.footer.el && this.footer.xtype) {
40649 var ctr = this.grid.getView().getFooterPanel(true);
40650 this.footer.dataSource = this.grid.dataSource;
40651 this.footer = Roo.factory(this.footer, Roo);
40652 this.footer.render(ctr);
40662 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40663 getId : function(){
40664 return this.grid.id;
40668 * Returns the grid for this panel
40669 * @return {Roo.bootstrap.Table}
40671 getGrid : function(){
40675 setSize : function(width, height){
40676 if(!this.ignoreResize(width, height)){
40677 var grid = this.grid;
40678 var size = this.adjustForComponents(width, height);
40679 // tfoot is not a footer?
40682 var gridel = grid.getGridEl();
40683 gridel.setSize(size.width, size.height);
40685 var tbd = grid.getGridEl().select('tbody', true).first();
40686 var thd = grid.getGridEl().select('thead',true).first();
40687 var tbf= grid.getGridEl().select('tfoot', true).first();
40690 size.height -= tbf.getHeight();
40693 size.height -= thd.getHeight();
40696 tbd.setSize(size.width, size.height );
40697 // this is for the account management tab -seems to work there.
40698 var thd = grid.getGridEl().select('thead',true).first();
40700 // tbd.setSize(size.width, size.height - thd.getHeight());
40709 beforeSlide : function(){
40710 this.grid.getView().scroller.clip();
40713 afterSlide : function(){
40714 this.grid.getView().scroller.unclip();
40717 destroy : function(){
40718 this.grid.destroy();
40720 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40725 * @class Roo.bootstrap.panel.Nest
40726 * @extends Roo.bootstrap.panel.Content
40728 * Create a new Panel, that can contain a layout.Border.
40731 * @param {Roo.BorderLayout} layout The layout for this panel
40732 * @param {String/Object} config A string to set only the title or a config object
40734 Roo.bootstrap.panel.Nest = function(config)
40736 // construct with only one argument..
40737 /* FIXME - implement nicer consturctors
40738 if (layout.layout) {
40740 layout = config.layout;
40741 delete config.layout;
40743 if (layout.xtype && !layout.getEl) {
40744 // then layout needs constructing..
40745 layout = Roo.factory(layout, Roo);
40749 config.el = config.layout.getEl();
40751 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40753 config.layout.monitorWindowResize = false; // turn off autosizing
40754 this.layout = config.layout;
40755 this.layout.getEl().addClass("roo-layout-nested-layout");
40756 this.layout.parent = this;
40763 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40765 setSize : function(width, height){
40766 if(!this.ignoreResize(width, height)){
40767 var size = this.adjustForComponents(width, height);
40768 var el = this.layout.getEl();
40769 if (size.height < 1) {
40770 el.setWidth(size.width);
40772 el.setSize(size.width, size.height);
40774 var touch = el.dom.offsetWidth;
40775 this.layout.layout();
40776 // ie requires a double layout on the first pass
40777 if(Roo.isIE && !this.initialized){
40778 this.initialized = true;
40779 this.layout.layout();
40784 // activate all subpanels if not currently active..
40786 setActiveState : function(active){
40787 this.active = active;
40788 this.setActiveClass(active);
40791 this.fireEvent("deactivate", this);
40795 this.fireEvent("activate", this);
40796 // not sure if this should happen before or after..
40797 if (!this.layout) {
40798 return; // should not happen..
40801 for (var r in this.layout.regions) {
40802 reg = this.layout.getRegion(r);
40803 if (reg.getActivePanel()) {
40804 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40805 reg.setActivePanel(reg.getActivePanel());
40808 if (!reg.panels.length) {
40811 reg.showPanel(reg.getPanel(0));
40820 * Returns the nested BorderLayout for this panel
40821 * @return {Roo.BorderLayout}
40823 getLayout : function(){
40824 return this.layout;
40828 * Adds a xtype elements to the layout of the nested panel
40832 xtype : 'ContentPanel',
40839 xtype : 'NestedLayoutPanel',
40845 items : [ ... list of content panels or nested layout panels.. ]
40849 * @param {Object} cfg Xtype definition of item to add.
40851 addxtype : function(cfg) {
40852 return this.layout.addxtype(cfg);
40857 * Ext JS Library 1.1.1
40858 * Copyright(c) 2006-2007, Ext JS, LLC.
40860 * Originally Released Under LGPL - original licence link has changed is not relivant.
40863 * <script type="text/javascript">
40866 * @class Roo.TabPanel
40867 * @extends Roo.util.Observable
40868 * A lightweight tab container.
40872 // basic tabs 1, built from existing content
40873 var tabs = new Roo.TabPanel("tabs1");
40874 tabs.addTab("script", "View Script");
40875 tabs.addTab("markup", "View Markup");
40876 tabs.activate("script");
40878 // more advanced tabs, built from javascript
40879 var jtabs = new Roo.TabPanel("jtabs");
40880 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40882 // set up the UpdateManager
40883 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40884 var updater = tab2.getUpdateManager();
40885 updater.setDefaultUrl("ajax1.htm");
40886 tab2.on('activate', updater.refresh, updater, true);
40888 // Use setUrl for Ajax loading
40889 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40890 tab3.setUrl("ajax2.htm", null, true);
40893 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40896 jtabs.activate("jtabs-1");
40899 * Create a new TabPanel.
40900 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40901 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40903 Roo.bootstrap.panel.Tabs = function(config){
40905 * The container element for this TabPanel.
40906 * @type Roo.Element
40908 this.el = Roo.get(config.el);
40911 if(typeof config == "boolean"){
40912 this.tabPosition = config ? "bottom" : "top";
40914 Roo.apply(this, config);
40918 if(this.tabPosition == "bottom"){
40919 // if tabs are at the bottom = create the body first.
40920 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40921 this.el.addClass("roo-tabs-bottom");
40923 // next create the tabs holders
40925 if (this.tabPosition == "west"){
40927 var reg = this.region; // fake it..
40929 if (!reg.mgr.parent) {
40932 reg = reg.mgr.parent.region;
40934 Roo.log("got nest?");
40936 if (reg.mgr.getRegion('west')) {
40937 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40938 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40939 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40940 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40941 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40949 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40950 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40951 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40952 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40957 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40960 // finally - if tabs are at the top, then create the body last..
40961 if(this.tabPosition != "bottom"){
40962 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40963 * @type Roo.Element
40965 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40966 this.el.addClass("roo-tabs-top");
40970 this.bodyEl.setStyle("position", "relative");
40972 this.active = null;
40973 this.activateDelegate = this.activate.createDelegate(this);
40978 * Fires when the active tab changes
40979 * @param {Roo.TabPanel} this
40980 * @param {Roo.TabPanelItem} activePanel The new active tab
40984 * @event beforetabchange
40985 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40986 * @param {Roo.TabPanel} this
40987 * @param {Object} e Set cancel to true on this object to cancel the tab change
40988 * @param {Roo.TabPanelItem} tab The tab being changed to
40990 "beforetabchange" : true
40993 Roo.EventManager.onWindowResize(this.onResize, this);
40994 this.cpad = this.el.getPadding("lr");
40995 this.hiddenCount = 0;
40998 // toolbar on the tabbar support...
40999 if (this.toolbar) {
41000 alert("no toolbar support yet");
41001 this.toolbar = false;
41003 var tcfg = this.toolbar;
41004 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41005 this.toolbar = new Roo.Toolbar(tcfg);
41006 if (Roo.isSafari) {
41007 var tbl = tcfg.container.child('table', true);
41008 tbl.setAttribute('width', '100%');
41016 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41019 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41021 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41023 tabPosition : "top",
41025 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41027 currentTabWidth : 0,
41029 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41033 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41037 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41039 preferredTabWidth : 175,
41041 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41043 resizeTabs : false,
41045 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41047 monitorResize : true,
41049 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41051 toolbar : false, // set by caller..
41053 region : false, /// set by caller
41055 disableTooltips : true, // not used yet...
41058 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41059 * @param {String} id The id of the div to use <b>or create</b>
41060 * @param {String} text The text for the tab
41061 * @param {String} content (optional) Content to put in the TabPanelItem body
41062 * @param {Boolean} closable (optional) True to create a close icon on the tab
41063 * @return {Roo.TabPanelItem} The created TabPanelItem
41065 addTab : function(id, text, content, closable, tpl)
41067 var item = new Roo.bootstrap.panel.TabItem({
41071 closable : closable,
41074 this.addTabItem(item);
41076 item.setContent(content);
41082 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41083 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41084 * @return {Roo.TabPanelItem}
41086 getTab : function(id){
41087 return this.items[id];
41091 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41092 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41094 hideTab : function(id){
41095 var t = this.items[id];
41098 this.hiddenCount++;
41099 this.autoSizeTabs();
41104 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41105 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41107 unhideTab : function(id){
41108 var t = this.items[id];
41110 t.setHidden(false);
41111 this.hiddenCount--;
41112 this.autoSizeTabs();
41117 * Adds an existing {@link Roo.TabPanelItem}.
41118 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41120 addTabItem : function(item)
41122 this.items[item.id] = item;
41123 this.items.push(item);
41124 this.autoSizeTabs();
41125 // if(this.resizeTabs){
41126 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41127 // this.autoSizeTabs();
41129 // item.autoSize();
41134 * Removes a {@link Roo.TabPanelItem}.
41135 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41137 removeTab : function(id){
41138 var items = this.items;
41139 var tab = items[id];
41140 if(!tab) { return; }
41141 var index = items.indexOf(tab);
41142 if(this.active == tab && items.length > 1){
41143 var newTab = this.getNextAvailable(index);
41148 this.stripEl.dom.removeChild(tab.pnode.dom);
41149 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41150 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41152 items.splice(index, 1);
41153 delete this.items[tab.id];
41154 tab.fireEvent("close", tab);
41155 tab.purgeListeners();
41156 this.autoSizeTabs();
41159 getNextAvailable : function(start){
41160 var items = this.items;
41162 // look for a next tab that will slide over to
41163 // replace the one being removed
41164 while(index < items.length){
41165 var item = items[++index];
41166 if(item && !item.isHidden()){
41170 // if one isn't found select the previous tab (on the left)
41173 var item = items[--index];
41174 if(item && !item.isHidden()){
41182 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41183 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41185 disableTab : function(id){
41186 var tab = this.items[id];
41187 if(tab && this.active != tab){
41193 * Enables a {@link Roo.TabPanelItem} that is disabled.
41194 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41196 enableTab : function(id){
41197 var tab = this.items[id];
41202 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41203 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41204 * @return {Roo.TabPanelItem} The TabPanelItem.
41206 activate : function(id)
41208 //Roo.log('activite:' + id);
41210 var tab = this.items[id];
41214 if(tab == this.active || tab.disabled){
41218 this.fireEvent("beforetabchange", this, e, tab);
41219 if(e.cancel !== true && !tab.disabled){
41221 this.active.hide();
41223 this.active = this.items[id];
41224 this.active.show();
41225 this.fireEvent("tabchange", this, this.active);
41231 * Gets the active {@link Roo.TabPanelItem}.
41232 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41234 getActiveTab : function(){
41235 return this.active;
41239 * Updates the tab body element to fit the height of the container element
41240 * for overflow scrolling
41241 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41243 syncHeight : function(targetHeight){
41244 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41245 var bm = this.bodyEl.getMargins();
41246 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41247 this.bodyEl.setHeight(newHeight);
41251 onResize : function(){
41252 if(this.monitorResize){
41253 this.autoSizeTabs();
41258 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41260 beginUpdate : function(){
41261 this.updating = true;
41265 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41267 endUpdate : function(){
41268 this.updating = false;
41269 this.autoSizeTabs();
41273 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41275 autoSizeTabs : function()
41277 var count = this.items.length;
41278 var vcount = count - this.hiddenCount;
41281 this.stripEl.hide();
41283 this.stripEl.show();
41286 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41291 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41292 var availWidth = Math.floor(w / vcount);
41293 var b = this.stripBody;
41294 if(b.getWidth() > w){
41295 var tabs = this.items;
41296 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41297 if(availWidth < this.minTabWidth){
41298 /*if(!this.sleft){ // incomplete scrolling code
41299 this.createScrollButtons();
41302 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41305 if(this.currentTabWidth < this.preferredTabWidth){
41306 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41312 * Returns the number of tabs in this TabPanel.
41315 getCount : function(){
41316 return this.items.length;
41320 * Resizes all the tabs to the passed width
41321 * @param {Number} The new width
41323 setTabWidth : function(width){
41324 this.currentTabWidth = width;
41325 for(var i = 0, len = this.items.length; i < len; i++) {
41326 if(!this.items[i].isHidden()) {
41327 this.items[i].setWidth(width);
41333 * Destroys this TabPanel
41334 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41336 destroy : function(removeEl){
41337 Roo.EventManager.removeResizeListener(this.onResize, this);
41338 for(var i = 0, len = this.items.length; i < len; i++){
41339 this.items[i].purgeListeners();
41341 if(removeEl === true){
41342 this.el.update("");
41347 createStrip : function(container)
41349 var strip = document.createElement("nav");
41350 strip.className = Roo.bootstrap.version == 4 ?
41351 "navbar-light bg-light" :
41352 "navbar navbar-default"; //"x-tabs-wrap";
41353 container.appendChild(strip);
41357 createStripList : function(strip)
41359 // div wrapper for retard IE
41360 // returns the "tr" element.
41361 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41362 //'<div class="x-tabs-strip-wrap">'+
41363 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41364 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41365 return strip.firstChild; //.firstChild.firstChild.firstChild;
41367 createBody : function(container)
41369 var body = document.createElement("div");
41370 Roo.id(body, "tab-body");
41371 //Roo.fly(body).addClass("x-tabs-body");
41372 Roo.fly(body).addClass("tab-content");
41373 container.appendChild(body);
41376 createItemBody :function(bodyEl, id){
41377 var body = Roo.getDom(id);
41379 body = document.createElement("div");
41382 //Roo.fly(body).addClass("x-tabs-item-body");
41383 Roo.fly(body).addClass("tab-pane");
41384 bodyEl.insertBefore(body, bodyEl.firstChild);
41388 createStripElements : function(stripEl, text, closable, tpl)
41390 var td = document.createElement("li"); // was td..
41391 td.className = 'nav-item';
41393 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41396 stripEl.appendChild(td);
41398 td.className = "x-tabs-closable";
41399 if(!this.closeTpl){
41400 this.closeTpl = new Roo.Template(
41401 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41402 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41403 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41406 var el = this.closeTpl.overwrite(td, {"text": text});
41407 var close = el.getElementsByTagName("div")[0];
41408 var inner = el.getElementsByTagName("em")[0];
41409 return {"el": el, "close": close, "inner": inner};
41412 // not sure what this is..
41413 // if(!this.tabTpl){
41414 //this.tabTpl = new Roo.Template(
41415 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41416 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41418 // this.tabTpl = new Roo.Template(
41419 // '<a href="#">' +
41420 // '<span unselectable="on"' +
41421 // (this.disableTooltips ? '' : ' title="{text}"') +
41422 // ' >{text}</span></a>'
41428 var template = tpl || this.tabTpl || false;
41431 template = new Roo.Template(
41432 Roo.bootstrap.version == 4 ?
41434 '<a class="nav-link" href="#" unselectable="on"' +
41435 (this.disableTooltips ? '' : ' title="{text}"') +
41438 '<a class="nav-link" href="#">' +
41439 '<span unselectable="on"' +
41440 (this.disableTooltips ? '' : ' title="{text}"') +
41441 ' >{text}</span></a>'
41446 switch (typeof(template)) {
41450 template = new Roo.Template(template);
41456 var el = template.overwrite(td, {"text": text});
41458 var inner = el.getElementsByTagName("span")[0];
41460 return {"el": el, "inner": inner};
41468 * @class Roo.TabPanelItem
41469 * @extends Roo.util.Observable
41470 * Represents an individual item (tab plus body) in a TabPanel.
41471 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41472 * @param {String} id The id of this TabPanelItem
41473 * @param {String} text The text for the tab of this TabPanelItem
41474 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41476 Roo.bootstrap.panel.TabItem = function(config){
41478 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41479 * @type Roo.TabPanel
41481 this.tabPanel = config.panel;
41483 * The id for this TabPanelItem
41486 this.id = config.id;
41488 this.disabled = false;
41490 this.text = config.text;
41492 this.loaded = false;
41493 this.closable = config.closable;
41496 * The body element for this TabPanelItem.
41497 * @type Roo.Element
41499 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41500 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41501 this.bodyEl.setStyle("display", "block");
41502 this.bodyEl.setStyle("zoom", "1");
41503 //this.hideAction();
41505 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41507 this.el = Roo.get(els.el);
41508 this.inner = Roo.get(els.inner, true);
41509 this.textEl = Roo.bootstrap.version == 4 ?
41510 this.el : Roo.get(this.el.dom.firstChild, true);
41512 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41513 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41516 // this.el.on("mousedown", this.onTabMouseDown, this);
41517 this.el.on("click", this.onTabClick, this);
41519 if(config.closable){
41520 var c = Roo.get(els.close, true);
41521 c.dom.title = this.closeText;
41522 c.addClassOnOver("close-over");
41523 c.on("click", this.closeClick, this);
41529 * Fires when this tab becomes the active tab.
41530 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41531 * @param {Roo.TabPanelItem} this
41535 * @event beforeclose
41536 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41537 * @param {Roo.TabPanelItem} this
41538 * @param {Object} e Set cancel to true on this object to cancel the close.
41540 "beforeclose": true,
41543 * Fires when this tab is closed.
41544 * @param {Roo.TabPanelItem} this
41548 * @event deactivate
41549 * Fires when this tab is no longer the active tab.
41550 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41551 * @param {Roo.TabPanelItem} this
41553 "deactivate" : true
41555 this.hidden = false;
41557 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41560 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41562 purgeListeners : function(){
41563 Roo.util.Observable.prototype.purgeListeners.call(this);
41564 this.el.removeAllListeners();
41567 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41570 this.status_node.addClass("active");
41573 this.tabPanel.stripWrap.repaint();
41575 this.fireEvent("activate", this.tabPanel, this);
41579 * Returns true if this tab is the active tab.
41580 * @return {Boolean}
41582 isActive : function(){
41583 return this.tabPanel.getActiveTab() == this;
41587 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41590 this.status_node.removeClass("active");
41592 this.fireEvent("deactivate", this.tabPanel, this);
41595 hideAction : function(){
41596 this.bodyEl.hide();
41597 this.bodyEl.setStyle("position", "absolute");
41598 this.bodyEl.setLeft("-20000px");
41599 this.bodyEl.setTop("-20000px");
41602 showAction : function(){
41603 this.bodyEl.setStyle("position", "relative");
41604 this.bodyEl.setTop("");
41605 this.bodyEl.setLeft("");
41606 this.bodyEl.show();
41610 * Set the tooltip for the tab.
41611 * @param {String} tooltip The tab's tooltip
41613 setTooltip : function(text){
41614 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41615 this.textEl.dom.qtip = text;
41616 this.textEl.dom.removeAttribute('title');
41618 this.textEl.dom.title = text;
41622 onTabClick : function(e){
41623 e.preventDefault();
41624 this.tabPanel.activate(this.id);
41627 onTabMouseDown : function(e){
41628 e.preventDefault();
41629 this.tabPanel.activate(this.id);
41632 getWidth : function(){
41633 return this.inner.getWidth();
41636 setWidth : function(width){
41637 var iwidth = width - this.linode.getPadding("lr");
41638 this.inner.setWidth(iwidth);
41639 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41640 this.linode.setWidth(width);
41644 * Show or hide the tab
41645 * @param {Boolean} hidden True to hide or false to show.
41647 setHidden : function(hidden){
41648 this.hidden = hidden;
41649 this.linode.setStyle("display", hidden ? "none" : "");
41653 * Returns true if this tab is "hidden"
41654 * @return {Boolean}
41656 isHidden : function(){
41657 return this.hidden;
41661 * Returns the text for this tab
41664 getText : function(){
41668 autoSize : function(){
41669 //this.el.beginMeasure();
41670 this.textEl.setWidth(1);
41672 * #2804 [new] Tabs in Roojs
41673 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41675 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41676 //this.el.endMeasure();
41680 * Sets the text for the tab (Note: this also sets the tooltip text)
41681 * @param {String} text The tab's text and tooltip
41683 setText : function(text){
41685 this.textEl.update(text);
41686 this.setTooltip(text);
41687 //if(!this.tabPanel.resizeTabs){
41688 // this.autoSize();
41692 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41694 activate : function(){
41695 this.tabPanel.activate(this.id);
41699 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41701 disable : function(){
41702 if(this.tabPanel.active != this){
41703 this.disabled = true;
41704 this.status_node.addClass("disabled");
41709 * Enables this TabPanelItem if it was previously disabled.
41711 enable : function(){
41712 this.disabled = false;
41713 this.status_node.removeClass("disabled");
41717 * Sets the content for this TabPanelItem.
41718 * @param {String} content The content
41719 * @param {Boolean} loadScripts true to look for and load scripts
41721 setContent : function(content, loadScripts){
41722 this.bodyEl.update(content, loadScripts);
41726 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41727 * @return {Roo.UpdateManager} The UpdateManager
41729 getUpdateManager : function(){
41730 return this.bodyEl.getUpdateManager();
41734 * Set a URL to be used to load the content for this TabPanelItem.
41735 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41736 * @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)
41737 * @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)
41738 * @return {Roo.UpdateManager} The UpdateManager
41740 setUrl : function(url, params, loadOnce){
41741 if(this.refreshDelegate){
41742 this.un('activate', this.refreshDelegate);
41744 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41745 this.on("activate", this.refreshDelegate);
41746 return this.bodyEl.getUpdateManager();
41750 _handleRefresh : function(url, params, loadOnce){
41751 if(!loadOnce || !this.loaded){
41752 var updater = this.bodyEl.getUpdateManager();
41753 updater.update(url, params, this._setLoaded.createDelegate(this));
41758 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41759 * Will fail silently if the setUrl method has not been called.
41760 * This does not activate the panel, just updates its content.
41762 refresh : function(){
41763 if(this.refreshDelegate){
41764 this.loaded = false;
41765 this.refreshDelegate();
41770 _setLoaded : function(){
41771 this.loaded = true;
41775 closeClick : function(e){
41778 this.fireEvent("beforeclose", this, o);
41779 if(o.cancel !== true){
41780 this.tabPanel.removeTab(this.id);
41784 * The text displayed in the tooltip for the close icon.
41787 closeText : "Close this tab"
41790 * This script refer to:
41791 * Title: International Telephone Input
41792 * Author: Jack O'Connor
41793 * Code version: v12.1.12
41794 * Availability: https://github.com/jackocnr/intl-tel-input.git
41797 Roo.bootstrap.PhoneInputData = function() {
41800 "Afghanistan (افغانستان)",
41805 "Albania (Shqipëri)",
41810 "Algeria (الجزائر)",
41835 "Antigua and Barbuda",
41845 "Armenia (Հայաստան)",
41861 "Austria (Österreich)",
41866 "Azerbaijan (Azərbaycan)",
41876 "Bahrain (البحرين)",
41881 "Bangladesh (বাংলাদেশ)",
41891 "Belarus (Беларусь)",
41896 "Belgium (België)",
41926 "Bosnia and Herzegovina (Босна и Херцеговина)",
41941 "British Indian Ocean Territory",
41946 "British Virgin Islands",
41956 "Bulgaria (България)",
41966 "Burundi (Uburundi)",
41971 "Cambodia (កម្ពុជា)",
41976 "Cameroon (Cameroun)",
41985 ["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"]
41988 "Cape Verde (Kabu Verdi)",
41993 "Caribbean Netherlands",
42004 "Central African Republic (République centrafricaine)",
42024 "Christmas Island",
42030 "Cocos (Keeling) Islands",
42041 "Comoros (جزر القمر)",
42046 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42051 "Congo (Republic) (Congo-Brazzaville)",
42071 "Croatia (Hrvatska)",
42092 "Czech Republic (Česká republika)",
42097 "Denmark (Danmark)",
42112 "Dominican Republic (República Dominicana)",
42116 ["809", "829", "849"]
42134 "Equatorial Guinea (Guinea Ecuatorial)",
42154 "Falkland Islands (Islas Malvinas)",
42159 "Faroe Islands (Føroyar)",
42180 "French Guiana (Guyane française)",
42185 "French Polynesia (Polynésie française)",
42200 "Georgia (საქართველო)",
42205 "Germany (Deutschland)",
42225 "Greenland (Kalaallit Nunaat)",
42262 "Guinea-Bissau (Guiné Bissau)",
42287 "Hungary (Magyarország)",
42292 "Iceland (Ísland)",
42312 "Iraq (العراق)",
42328 "Israel (ישראל)",
42355 "Jordan (الأردن)",
42360 "Kazakhstan (Казахстан)",
42381 "Kuwait (الكويت)",
42386 "Kyrgyzstan (Кыргызстан)",
42396 "Latvia (Latvija)",
42401 "Lebanon (لبنان)",
42416 "Libya (ليبيا)",
42426 "Lithuania (Lietuva)",
42441 "Macedonia (FYROM) (Македонија)",
42446 "Madagascar (Madagasikara)",
42476 "Marshall Islands",
42486 "Mauritania (موريتانيا)",
42491 "Mauritius (Moris)",
42512 "Moldova (Republica Moldova)",
42522 "Mongolia (Монгол)",
42527 "Montenegro (Crna Gora)",
42537 "Morocco (المغرب)",
42543 "Mozambique (Moçambique)",
42548 "Myanmar (Burma) (မြန်မာ)",
42553 "Namibia (Namibië)",
42568 "Netherlands (Nederland)",
42573 "New Caledonia (Nouvelle-Calédonie)",
42608 "North Korea (조선 민주주의 인민 공화국)",
42613 "Northern Mariana Islands",
42629 "Pakistan (پاکستان)",
42639 "Palestine (فلسطين)",
42649 "Papua New Guinea",
42691 "Réunion (La Réunion)",
42697 "Romania (România)",
42713 "Saint Barthélemy",
42724 "Saint Kitts and Nevis",
42734 "Saint Martin (Saint-Martin (partie française))",
42740 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42745 "Saint Vincent and the Grenadines",
42760 "São Tomé and Príncipe (São Tomé e Príncipe)",
42765 "Saudi Arabia (المملكة العربية السعودية)",
42770 "Senegal (Sénégal)",
42800 "Slovakia (Slovensko)",
42805 "Slovenia (Slovenija)",
42815 "Somalia (Soomaaliya)",
42825 "South Korea (대한민국)",
42830 "South Sudan (جنوب السودان)",
42840 "Sri Lanka (ශ්රී ලංකාව)",
42845 "Sudan (السودان)",
42855 "Svalbard and Jan Mayen",
42866 "Sweden (Sverige)",
42871 "Switzerland (Schweiz)",
42876 "Syria (سوريا)",
42921 "Trinidad and Tobago",
42926 "Tunisia (تونس)",
42931 "Turkey (Türkiye)",
42941 "Turks and Caicos Islands",
42951 "U.S. Virgin Islands",
42961 "Ukraine (Україна)",
42966 "United Arab Emirates (الإمارات العربية المتحدة)",
42988 "Uzbekistan (Oʻzbekiston)",
42998 "Vatican City (Città del Vaticano)",
43009 "Vietnam (Việt Nam)",
43014 "Wallis and Futuna (Wallis-et-Futuna)",
43019 "Western Sahara (الصحراء الغربية)",
43025 "Yemen (اليمن)",
43049 * This script refer to:
43050 * Title: International Telephone Input
43051 * Author: Jack O'Connor
43052 * Code version: v12.1.12
43053 * Availability: https://github.com/jackocnr/intl-tel-input.git
43057 * @class Roo.bootstrap.PhoneInput
43058 * @extends Roo.bootstrap.TriggerField
43059 * An input with International dial-code selection
43061 * @cfg {String} defaultDialCode default '+852'
43062 * @cfg {Array} preferedCountries default []
43065 * Create a new PhoneInput.
43066 * @param {Object} config Configuration options
43069 Roo.bootstrap.PhoneInput = function(config) {
43070 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43073 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43075 listWidth: undefined,
43077 selectedClass: 'active',
43079 invalidClass : "has-warning",
43081 validClass: 'has-success',
43083 allowed: '0123456789',
43088 * @cfg {String} defaultDialCode The default dial code when initializing the input
43090 defaultDialCode: '+852',
43093 * @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
43095 preferedCountries: false,
43097 getAutoCreate : function()
43099 var data = Roo.bootstrap.PhoneInputData();
43100 var align = this.labelAlign || this.parentLabelAlign();
43103 this.allCountries = [];
43104 this.dialCodeMapping = [];
43106 for (var i = 0; i < data.length; i++) {
43108 this.allCountries[i] = {
43112 priority: c[3] || 0,
43113 areaCodes: c[4] || null
43115 this.dialCodeMapping[c[2]] = {
43118 priority: c[3] || 0,
43119 areaCodes: c[4] || null
43131 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43132 maxlength: this.max_length,
43133 cls : 'form-control tel-input',
43134 autocomplete: 'new-password'
43137 var hiddenInput = {
43140 cls: 'hidden-tel-input'
43144 hiddenInput.name = this.name;
43147 if (this.disabled) {
43148 input.disabled = true;
43151 var flag_container = {
43168 cls: this.hasFeedback ? 'has-feedback' : '',
43174 cls: 'dial-code-holder',
43181 cls: 'roo-select2-container input-group',
43188 if (this.fieldLabel.length) {
43191 tooltip: 'This field is required'
43197 cls: 'control-label',
43203 html: this.fieldLabel
43206 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43212 if(this.indicatorpos == 'right') {
43213 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43220 if(align == 'left') {
43228 if(this.labelWidth > 12){
43229 label.style = "width: " + this.labelWidth + 'px';
43231 if(this.labelWidth < 13 && this.labelmd == 0){
43232 this.labelmd = this.labelWidth;
43234 if(this.labellg > 0){
43235 label.cls += ' col-lg-' + this.labellg;
43236 input.cls += ' col-lg-' + (12 - this.labellg);
43238 if(this.labelmd > 0){
43239 label.cls += ' col-md-' + this.labelmd;
43240 container.cls += ' col-md-' + (12 - this.labelmd);
43242 if(this.labelsm > 0){
43243 label.cls += ' col-sm-' + this.labelsm;
43244 container.cls += ' col-sm-' + (12 - this.labelsm);
43246 if(this.labelxs > 0){
43247 label.cls += ' col-xs-' + this.labelxs;
43248 container.cls += ' col-xs-' + (12 - this.labelxs);
43258 var settings = this;
43260 ['xs','sm','md','lg'].map(function(size){
43261 if (settings[size]) {
43262 cfg.cls += ' col-' + size + '-' + settings[size];
43266 this.store = new Roo.data.Store({
43267 proxy : new Roo.data.MemoryProxy({}),
43268 reader : new Roo.data.JsonReader({
43279 'name' : 'dialCode',
43283 'name' : 'priority',
43287 'name' : 'areaCodes',
43294 if(!this.preferedCountries) {
43295 this.preferedCountries = [
43302 var p = this.preferedCountries.reverse();
43305 for (var i = 0; i < p.length; i++) {
43306 for (var j = 0; j < this.allCountries.length; j++) {
43307 if(this.allCountries[j].iso2 == p[i]) {
43308 var t = this.allCountries[j];
43309 this.allCountries.splice(j,1);
43310 this.allCountries.unshift(t);
43316 this.store.proxy.data = {
43318 data: this.allCountries
43324 initEvents : function()
43327 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43329 this.indicator = this.indicatorEl();
43330 this.flag = this.flagEl();
43331 this.dialCodeHolder = this.dialCodeHolderEl();
43333 this.trigger = this.el.select('div.flag-box',true).first();
43334 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43339 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43340 _this.list.setWidth(lw);
43343 this.list.on('mouseover', this.onViewOver, this);
43344 this.list.on('mousemove', this.onViewMove, this);
43345 this.inputEl().on("keyup", this.onKeyUp, this);
43346 this.inputEl().on("keypress", this.onKeyPress, this);
43348 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43350 this.view = new Roo.View(this.list, this.tpl, {
43351 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43354 this.view.on('click', this.onViewClick, this);
43355 this.setValue(this.defaultDialCode);
43358 onTriggerClick : function(e)
43360 Roo.log('trigger click');
43365 if(this.isExpanded()){
43367 this.hasFocus = false;
43369 this.store.load({});
43370 this.hasFocus = true;
43375 isExpanded : function()
43377 return this.list.isVisible();
43380 collapse : function()
43382 if(!this.isExpanded()){
43386 Roo.get(document).un('mousedown', this.collapseIf, this);
43387 Roo.get(document).un('mousewheel', this.collapseIf, this);
43388 this.fireEvent('collapse', this);
43392 expand : function()
43396 if(this.isExpanded() || !this.hasFocus){
43400 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43401 this.list.setWidth(lw);
43404 this.restrictHeight();
43406 Roo.get(document).on('mousedown', this.collapseIf, this);
43407 Roo.get(document).on('mousewheel', this.collapseIf, this);
43409 this.fireEvent('expand', this);
43412 restrictHeight : function()
43414 this.list.alignTo(this.inputEl(), this.listAlign);
43415 this.list.alignTo(this.inputEl(), this.listAlign);
43418 onViewOver : function(e, t)
43420 if(this.inKeyMode){
43423 var item = this.view.findItemFromChild(t);
43426 var index = this.view.indexOf(item);
43427 this.select(index, false);
43432 onViewClick : function(view, doFocus, el, e)
43434 var index = this.view.getSelectedIndexes()[0];
43436 var r = this.store.getAt(index);
43439 this.onSelect(r, index);
43441 if(doFocus !== false && !this.blockFocus){
43442 this.inputEl().focus();
43446 onViewMove : function(e, t)
43448 this.inKeyMode = false;
43451 select : function(index, scrollIntoView)
43453 this.selectedIndex = index;
43454 this.view.select(index);
43455 if(scrollIntoView !== false){
43456 var el = this.view.getNode(index);
43458 this.list.scrollChildIntoView(el, false);
43463 createList : function()
43465 this.list = Roo.get(document.body).createChild({
43467 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43468 style: 'display:none'
43471 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43474 collapseIf : function(e)
43476 var in_combo = e.within(this.el);
43477 var in_list = e.within(this.list);
43478 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43480 if (in_combo || in_list || is_list) {
43486 onSelect : function(record, index)
43488 if(this.fireEvent('beforeselect', this, record, index) !== false){
43490 this.setFlagClass(record.data.iso2);
43491 this.setDialCode(record.data.dialCode);
43492 this.hasFocus = false;
43494 this.fireEvent('select', this, record, index);
43498 flagEl : function()
43500 var flag = this.el.select('div.flag',true).first();
43507 dialCodeHolderEl : function()
43509 var d = this.el.select('input.dial-code-holder',true).first();
43516 setDialCode : function(v)
43518 this.dialCodeHolder.dom.value = '+'+v;
43521 setFlagClass : function(n)
43523 this.flag.dom.className = 'flag '+n;
43526 getValue : function()
43528 var v = this.inputEl().getValue();
43529 if(this.dialCodeHolder) {
43530 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43535 setValue : function(v)
43537 var d = this.getDialCode(v);
43539 //invalid dial code
43540 if(v.length == 0 || !d || d.length == 0) {
43542 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43543 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43549 this.setFlagClass(this.dialCodeMapping[d].iso2);
43550 this.setDialCode(d);
43551 this.inputEl().dom.value = v.replace('+'+d,'');
43552 this.hiddenEl().dom.value = this.getValue();
43557 getDialCode : function(v)
43561 if (v.length == 0) {
43562 return this.dialCodeHolder.dom.value;
43566 if (v.charAt(0) != "+") {
43569 var numericChars = "";
43570 for (var i = 1; i < v.length; i++) {
43571 var c = v.charAt(i);
43574 if (this.dialCodeMapping[numericChars]) {
43575 dialCode = v.substr(1, i);
43577 if (numericChars.length == 4) {
43587 this.setValue(this.defaultDialCode);
43591 hiddenEl : function()
43593 return this.el.select('input.hidden-tel-input',true).first();
43596 // after setting val
43597 onKeyUp : function(e){
43598 this.setValue(this.getValue());
43601 onKeyPress : function(e){
43602 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43609 * @class Roo.bootstrap.MoneyField
43610 * @extends Roo.bootstrap.ComboBox
43611 * Bootstrap MoneyField class
43614 * Create a new MoneyField.
43615 * @param {Object} config Configuration options
43618 Roo.bootstrap.MoneyField = function(config) {
43620 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43624 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43627 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43629 allowDecimals : true,
43631 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43633 decimalSeparator : ".",
43635 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43637 decimalPrecision : 0,
43639 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43641 allowNegative : true,
43643 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43647 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43649 minValue : Number.NEGATIVE_INFINITY,
43651 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43653 maxValue : Number.MAX_VALUE,
43655 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43657 minText : "The minimum value for this field is {0}",
43659 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43661 maxText : "The maximum value for this field is {0}",
43663 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43664 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43666 nanText : "{0} is not a valid number",
43668 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43672 * @cfg {String} defaults currency of the MoneyField
43673 * value should be in lkey
43675 defaultCurrency : false,
43677 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43679 thousandsDelimiter : false,
43681 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43692 getAutoCreate : function()
43694 var align = this.labelAlign || this.parentLabelAlign();
43706 cls : 'form-control roo-money-amount-input',
43707 autocomplete: 'new-password'
43710 var hiddenInput = {
43714 cls: 'hidden-number-input'
43717 if(this.max_length) {
43718 input.maxlength = this.max_length;
43722 hiddenInput.name = this.name;
43725 if (this.disabled) {
43726 input.disabled = true;
43729 var clg = 12 - this.inputlg;
43730 var cmd = 12 - this.inputmd;
43731 var csm = 12 - this.inputsm;
43732 var cxs = 12 - this.inputxs;
43736 cls : 'row roo-money-field',
43740 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43744 cls: 'roo-select2-container input-group',
43748 cls : 'form-control roo-money-currency-input',
43749 autocomplete: 'new-password',
43751 name : this.currencyName
43755 cls : 'input-group-addon',
43769 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43773 cls: this.hasFeedback ? 'has-feedback' : '',
43784 if (this.fieldLabel.length) {
43787 tooltip: 'This field is required'
43793 cls: 'control-label',
43799 html: this.fieldLabel
43802 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43808 if(this.indicatorpos == 'right') {
43809 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43816 if(align == 'left') {
43824 if(this.labelWidth > 12){
43825 label.style = "width: " + this.labelWidth + 'px';
43827 if(this.labelWidth < 13 && this.labelmd == 0){
43828 this.labelmd = this.labelWidth;
43830 if(this.labellg > 0){
43831 label.cls += ' col-lg-' + this.labellg;
43832 input.cls += ' col-lg-' + (12 - this.labellg);
43834 if(this.labelmd > 0){
43835 label.cls += ' col-md-' + this.labelmd;
43836 container.cls += ' col-md-' + (12 - this.labelmd);
43838 if(this.labelsm > 0){
43839 label.cls += ' col-sm-' + this.labelsm;
43840 container.cls += ' col-sm-' + (12 - this.labelsm);
43842 if(this.labelxs > 0){
43843 label.cls += ' col-xs-' + this.labelxs;
43844 container.cls += ' col-xs-' + (12 - this.labelxs);
43855 var settings = this;
43857 ['xs','sm','md','lg'].map(function(size){
43858 if (settings[size]) {
43859 cfg.cls += ' col-' + size + '-' + settings[size];
43866 initEvents : function()
43868 this.indicator = this.indicatorEl();
43870 this.initCurrencyEvent();
43872 this.initNumberEvent();
43875 initCurrencyEvent : function()
43878 throw "can not find store for combo";
43881 this.store = Roo.factory(this.store, Roo.data);
43882 this.store.parent = this;
43886 this.triggerEl = this.el.select('.input-group-addon', true).first();
43888 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43893 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43894 _this.list.setWidth(lw);
43897 this.list.on('mouseover', this.onViewOver, this);
43898 this.list.on('mousemove', this.onViewMove, this);
43899 this.list.on('scroll', this.onViewScroll, this);
43902 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43905 this.view = new Roo.View(this.list, this.tpl, {
43906 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43909 this.view.on('click', this.onViewClick, this);
43911 this.store.on('beforeload', this.onBeforeLoad, this);
43912 this.store.on('load', this.onLoad, this);
43913 this.store.on('loadexception', this.onLoadException, this);
43915 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43916 "up" : function(e){
43917 this.inKeyMode = true;
43921 "down" : function(e){
43922 if(!this.isExpanded()){
43923 this.onTriggerClick();
43925 this.inKeyMode = true;
43930 "enter" : function(e){
43933 if(this.fireEvent("specialkey", this, e)){
43934 this.onViewClick(false);
43940 "esc" : function(e){
43944 "tab" : function(e){
43947 if(this.fireEvent("specialkey", this, e)){
43948 this.onViewClick(false);
43956 doRelay : function(foo, bar, hname){
43957 if(hname == 'down' || this.scope.isExpanded()){
43958 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43966 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43970 initNumberEvent : function(e)
43972 this.inputEl().on("keydown" , this.fireKey, this);
43973 this.inputEl().on("focus", this.onFocus, this);
43974 this.inputEl().on("blur", this.onBlur, this);
43976 this.inputEl().relayEvent('keyup', this);
43978 if(this.indicator){
43979 this.indicator.addClass('invisible');
43982 this.originalValue = this.getValue();
43984 if(this.validationEvent == 'keyup'){
43985 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43986 this.inputEl().on('keyup', this.filterValidation, this);
43988 else if(this.validationEvent !== false){
43989 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43992 if(this.selectOnFocus){
43993 this.on("focus", this.preFocus, this);
43996 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43997 this.inputEl().on("keypress", this.filterKeys, this);
43999 this.inputEl().relayEvent('keypress', this);
44002 var allowed = "0123456789";
44004 if(this.allowDecimals){
44005 allowed += this.decimalSeparator;
44008 if(this.allowNegative){
44012 if(this.thousandsDelimiter) {
44016 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44018 var keyPress = function(e){
44020 var k = e.getKey();
44022 var c = e.getCharCode();
44025 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44026 allowed.indexOf(String.fromCharCode(c)) === -1
44032 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44036 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44041 this.inputEl().on("keypress", keyPress, this);
44045 onTriggerClick : function(e)
44052 this.loadNext = false;
44054 if(this.isExpanded()){
44059 this.hasFocus = true;
44061 if(this.triggerAction == 'all') {
44062 this.doQuery(this.allQuery, true);
44066 this.doQuery(this.getRawValue());
44069 getCurrency : function()
44071 var v = this.currencyEl().getValue();
44076 restrictHeight : function()
44078 this.list.alignTo(this.currencyEl(), this.listAlign);
44079 this.list.alignTo(this.currencyEl(), this.listAlign);
44082 onViewClick : function(view, doFocus, el, e)
44084 var index = this.view.getSelectedIndexes()[0];
44086 var r = this.store.getAt(index);
44089 this.onSelect(r, index);
44093 onSelect : function(record, index){
44095 if(this.fireEvent('beforeselect', this, record, index) !== false){
44097 this.setFromCurrencyData(index > -1 ? record.data : false);
44101 this.fireEvent('select', this, record, index);
44105 setFromCurrencyData : function(o)
44109 this.lastCurrency = o;
44111 if (this.currencyField) {
44112 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44114 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44117 this.lastSelectionText = currency;
44119 //setting default currency
44120 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44121 this.setCurrency(this.defaultCurrency);
44125 this.setCurrency(currency);
44128 setFromData : function(o)
44132 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44134 this.setFromCurrencyData(c);
44139 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44141 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44144 this.setValue(value);
44148 setCurrency : function(v)
44150 this.currencyValue = v;
44153 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44158 setValue : function(v)
44160 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44166 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44168 this.inputEl().dom.value = (v == '') ? '' :
44169 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44171 if(!this.allowZero && v === '0') {
44172 this.hiddenEl().dom.value = '';
44173 this.inputEl().dom.value = '';
44180 getRawValue : function()
44182 var v = this.inputEl().getValue();
44187 getValue : function()
44189 return this.fixPrecision(this.parseValue(this.getRawValue()));
44192 parseValue : function(value)
44194 if(this.thousandsDelimiter) {
44196 r = new RegExp(",", "g");
44197 value = value.replace(r, "");
44200 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44201 return isNaN(value) ? '' : value;
44205 fixPrecision : function(value)
44207 if(this.thousandsDelimiter) {
44209 r = new RegExp(",", "g");
44210 value = value.replace(r, "");
44213 var nan = isNaN(value);
44215 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44216 return nan ? '' : value;
44218 return parseFloat(value).toFixed(this.decimalPrecision);
44221 decimalPrecisionFcn : function(v)
44223 return Math.floor(v);
44226 validateValue : function(value)
44228 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44232 var num = this.parseValue(value);
44235 this.markInvalid(String.format(this.nanText, value));
44239 if(num < this.minValue){
44240 this.markInvalid(String.format(this.minText, this.minValue));
44244 if(num > this.maxValue){
44245 this.markInvalid(String.format(this.maxText, this.maxValue));
44252 validate : function()
44254 if(this.disabled || this.allowBlank){
44259 var currency = this.getCurrency();
44261 if(this.validateValue(this.getRawValue()) && currency.length){
44266 this.markInvalid();
44270 getName: function()
44275 beforeBlur : function()
44281 var v = this.parseValue(this.getRawValue());
44288 onBlur : function()
44292 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44293 //this.el.removeClass(this.focusClass);
44296 this.hasFocus = false;
44298 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44302 var v = this.getValue();
44304 if(String(v) !== String(this.startValue)){
44305 this.fireEvent('change', this, v, this.startValue);
44308 this.fireEvent("blur", this);
44311 inputEl : function()
44313 return this.el.select('.roo-money-amount-input', true).first();
44316 currencyEl : function()
44318 return this.el.select('.roo-money-currency-input', true).first();
44321 hiddenEl : function()
44323 return this.el.select('input.hidden-number-input',true).first();
44327 * @class Roo.bootstrap.BezierSignature
44328 * @extends Roo.bootstrap.Component
44329 * Bootstrap BezierSignature class
44330 * This script refer to:
44331 * Title: Signature Pad
44333 * Availability: https://github.com/szimek/signature_pad
44336 * Create a new BezierSignature
44337 * @param {Object} config The config object
44340 Roo.bootstrap.BezierSignature = function(config){
44341 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44347 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44354 mouse_btn_down: true,
44357 * @cfg {int} canvas height
44359 canvas_height: '200px',
44362 * @cfg {float|function} Radius of a single dot.
44367 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44372 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44377 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44382 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44387 * @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.
44389 bg_color: 'rgba(0, 0, 0, 0)',
44392 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44394 dot_color: 'black',
44397 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44399 velocity_filter_weight: 0.7,
44402 * @cfg {function} Callback when stroke begin.
44407 * @cfg {function} Callback when stroke end.
44411 getAutoCreate : function()
44413 var cls = 'roo-signature column';
44416 cls += ' ' + this.cls;
44426 for(var i = 0; i < col_sizes.length; i++) {
44427 if(this[col_sizes[i]]) {
44428 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44438 cls: 'roo-signature-body',
44442 cls: 'roo-signature-body-canvas',
44443 height: this.canvas_height,
44444 width: this.canvas_width
44451 style: 'display: none'
44459 initEvents: function()
44461 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44463 var canvas = this.canvasEl();
44465 // mouse && touch event swapping...
44466 canvas.dom.style.touchAction = 'none';
44467 canvas.dom.style.msTouchAction = 'none';
44469 this.mouse_btn_down = false;
44470 canvas.on('mousedown', this._handleMouseDown, this);
44471 canvas.on('mousemove', this._handleMouseMove, this);
44472 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44474 if (window.PointerEvent) {
44475 canvas.on('pointerdown', this._handleMouseDown, this);
44476 canvas.on('pointermove', this._handleMouseMove, this);
44477 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44480 if ('ontouchstart' in window) {
44481 canvas.on('touchstart', this._handleTouchStart, this);
44482 canvas.on('touchmove', this._handleTouchMove, this);
44483 canvas.on('touchend', this._handleTouchEnd, this);
44486 Roo.EventManager.onWindowResize(this.resize, this, true);
44488 // file input event
44489 this.fileEl().on('change', this.uploadImage, this);
44496 resize: function(){
44498 var canvas = this.canvasEl().dom;
44499 var ctx = this.canvasElCtx();
44500 var img_data = false;
44502 if(canvas.width > 0) {
44503 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44505 // setting canvas width will clean img data
44508 var style = window.getComputedStyle ?
44509 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44511 var padding_left = parseInt(style.paddingLeft) || 0;
44512 var padding_right = parseInt(style.paddingRight) || 0;
44514 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44517 ctx.putImageData(img_data, 0, 0);
44521 _handleMouseDown: function(e)
44523 if (e.browserEvent.which === 1) {
44524 this.mouse_btn_down = true;
44525 this.strokeBegin(e);
44529 _handleMouseMove: function (e)
44531 if (this.mouse_btn_down) {
44532 this.strokeMoveUpdate(e);
44536 _handleMouseUp: function (e)
44538 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44539 this.mouse_btn_down = false;
44544 _handleTouchStart: function (e) {
44546 e.preventDefault();
44547 if (e.browserEvent.targetTouches.length === 1) {
44548 // var touch = e.browserEvent.changedTouches[0];
44549 // this.strokeBegin(touch);
44551 this.strokeBegin(e); // assume e catching the correct xy...
44555 _handleTouchMove: function (e) {
44556 e.preventDefault();
44557 // var touch = event.targetTouches[0];
44558 // _this._strokeMoveUpdate(touch);
44559 this.strokeMoveUpdate(e);
44562 _handleTouchEnd: function (e) {
44563 var wasCanvasTouched = e.target === this.canvasEl().dom;
44564 if (wasCanvasTouched) {
44565 e.preventDefault();
44566 // var touch = event.changedTouches[0];
44567 // _this._strokeEnd(touch);
44572 reset: function () {
44573 this._lastPoints = [];
44574 this._lastVelocity = 0;
44575 this._lastWidth = (this.min_width + this.max_width) / 2;
44576 this.canvasElCtx().fillStyle = this.dot_color;
44579 strokeMoveUpdate: function(e)
44581 this.strokeUpdate(e);
44583 if (this.throttle) {
44584 this.throttleStroke(this.strokeUpdate, this.throttle);
44587 this.strokeUpdate(e);
44591 strokeBegin: function(e)
44593 var newPointGroup = {
44594 color: this.dot_color,
44598 if (typeof this.onBegin === 'function') {
44602 this.curve_data.push(newPointGroup);
44604 this.strokeUpdate(e);
44607 strokeUpdate: function(e)
44609 var rect = this.canvasEl().dom.getBoundingClientRect();
44610 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44611 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44612 var lastPoints = lastPointGroup.points;
44613 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44614 var isLastPointTooClose = lastPoint
44615 ? point.distanceTo(lastPoint) <= this.min_distance
44617 var color = lastPointGroup.color;
44618 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44619 var curve = this.addPoint(point);
44621 this.drawDot({color: color, point: point});
44624 this.drawCurve({color: color, curve: curve});
44634 strokeEnd: function(e)
44636 this.strokeUpdate(e);
44637 if (typeof this.onEnd === 'function') {
44642 addPoint: function (point) {
44643 var _lastPoints = this._lastPoints;
44644 _lastPoints.push(point);
44645 if (_lastPoints.length > 2) {
44646 if (_lastPoints.length === 3) {
44647 _lastPoints.unshift(_lastPoints[0]);
44649 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44650 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44651 _lastPoints.shift();
44657 calculateCurveWidths: function (startPoint, endPoint) {
44658 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44659 (1 - this.velocity_filter_weight) * this._lastVelocity;
44661 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44664 start: this._lastWidth
44667 this._lastVelocity = velocity;
44668 this._lastWidth = newWidth;
44672 drawDot: function (_a) {
44673 var color = _a.color, point = _a.point;
44674 var ctx = this.canvasElCtx();
44675 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44677 this.drawCurveSegment(point.x, point.y, width);
44679 ctx.fillStyle = color;
44683 drawCurve: function (_a) {
44684 var color = _a.color, curve = _a.curve;
44685 var ctx = this.canvasElCtx();
44686 var widthDelta = curve.endWidth - curve.startWidth;
44687 var drawSteps = Math.floor(curve.length()) * 2;
44689 ctx.fillStyle = color;
44690 for (var i = 0; i < drawSteps; i += 1) {
44691 var t = i / drawSteps;
44697 var x = uuu * curve.startPoint.x;
44698 x += 3 * uu * t * curve.control1.x;
44699 x += 3 * u * tt * curve.control2.x;
44700 x += ttt * curve.endPoint.x;
44701 var y = uuu * curve.startPoint.y;
44702 y += 3 * uu * t * curve.control1.y;
44703 y += 3 * u * tt * curve.control2.y;
44704 y += ttt * curve.endPoint.y;
44705 var width = curve.startWidth + ttt * widthDelta;
44706 this.drawCurveSegment(x, y, width);
44712 drawCurveSegment: function (x, y, width) {
44713 var ctx = this.canvasElCtx();
44715 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44716 this.is_empty = false;
44721 var ctx = this.canvasElCtx();
44722 var canvas = this.canvasEl().dom;
44723 ctx.fillStyle = this.bg_color;
44724 ctx.clearRect(0, 0, canvas.width, canvas.height);
44725 ctx.fillRect(0, 0, canvas.width, canvas.height);
44726 this.curve_data = [];
44728 this.is_empty = true;
44733 return this.el.select('input',true).first();
44736 canvasEl: function()
44738 return this.el.select('canvas',true).first();
44741 canvasElCtx: function()
44743 return this.el.select('canvas',true).first().dom.getContext('2d');
44746 getImage: function(type)
44748 if(this.is_empty) {
44753 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44756 drawFromImage: function(img_src)
44758 var img = new Image();
44760 img.onload = function(){
44761 this.canvasElCtx().drawImage(img, 0, 0);
44766 this.is_empty = false;
44769 selectImage: function()
44771 this.fileEl().dom.click();
44774 uploadImage: function(e)
44776 var reader = new FileReader();
44778 reader.onload = function(e){
44779 var img = new Image();
44780 img.onload = function(){
44782 this.canvasElCtx().drawImage(img, 0, 0);
44784 img.src = e.target.result;
44787 reader.readAsDataURL(e.target.files[0]);
44790 // Bezier Point Constructor
44791 Point: (function () {
44792 function Point(x, y, time) {
44795 this.time = time || Date.now();
44797 Point.prototype.distanceTo = function (start) {
44798 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44800 Point.prototype.equals = function (other) {
44801 return this.x === other.x && this.y === other.y && this.time === other.time;
44803 Point.prototype.velocityFrom = function (start) {
44804 return this.time !== start.time
44805 ? this.distanceTo(start) / (this.time - start.time)
44812 // Bezier Constructor
44813 Bezier: (function () {
44814 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44815 this.startPoint = startPoint;
44816 this.control2 = control2;
44817 this.control1 = control1;
44818 this.endPoint = endPoint;
44819 this.startWidth = startWidth;
44820 this.endWidth = endWidth;
44822 Bezier.fromPoints = function (points, widths, scope) {
44823 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44824 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44825 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44827 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44828 var dx1 = s1.x - s2.x;
44829 var dy1 = s1.y - s2.y;
44830 var dx2 = s2.x - s3.x;
44831 var dy2 = s2.y - s3.y;
44832 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44833 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44834 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44835 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44836 var dxm = m1.x - m2.x;
44837 var dym = m1.y - m2.y;
44838 var k = l2 / (l1 + l2);
44839 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44840 var tx = s2.x - cm.x;
44841 var ty = s2.y - cm.y;
44843 c1: new scope.Point(m1.x + tx, m1.y + ty),
44844 c2: new scope.Point(m2.x + tx, m2.y + ty)
44847 Bezier.prototype.length = function () {
44852 for (var i = 0; i <= steps; i += 1) {
44854 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44855 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44857 var xdiff = cx - px;
44858 var ydiff = cy - py;
44859 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44866 Bezier.prototype.point = function (t, start, c1, c2, end) {
44867 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44868 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44869 + (3.0 * c2 * (1.0 - t) * t * t)
44870 + (end * t * t * t);
44875 throttleStroke: function(fn, wait) {
44876 if (wait === void 0) { wait = 250; }
44878 var timeout = null;
44882 var later = function () {
44883 previous = Date.now();
44885 result = fn.apply(storedContext, storedArgs);
44887 storedContext = null;
44891 return function wrapper() {
44893 for (var _i = 0; _i < arguments.length; _i++) {
44894 args[_i] = arguments[_i];
44896 var now = Date.now();
44897 var remaining = wait - (now - previous);
44898 storedContext = this;
44900 if (remaining <= 0 || remaining > wait) {
44902 clearTimeout(timeout);
44906 result = fn.apply(storedContext, storedArgs);
44908 storedContext = null;
44912 else if (!timeout) {
44913 timeout = window.setTimeout(later, remaining);