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.
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,
2914 getAutoCreate : function()
2921 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2924 multiple : 'multiple',
2926 cls : 'd-none roo-card-upload-selector'
2938 initEvents : function()
2941 Roo.bootstrap.Button.prototype.initEvents.call(this);
2947 this.urlAPI = (window.createObjectURL && window) ||
2948 (window.URL && URL.revokeObjectURL && URL) ||
2949 (window.webkitURL && webkitURL);
2954 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2956 this.selectorEl.on('change', this.onFileSelected, this);
2963 onClick : function(e)
2967 if ( this.fireEvent('beforeselect', this) === false) {
2971 this.selectorEl.dom.click();
2975 onFileSelected : function(e)
2979 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2982 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2983 this.selectorEl.dom.reset();
2985 this.fireEvent('uploaded', this, files );
2993 * addCard - add an Attachment to the uploader
2994 * @param data - the data about the image to upload
2998 title : "Title of file",
2999 is_uploaded : false,
3000 src : "http://.....",
3001 srcfile : { the File upload object },
3002 mimetype : file.type,
3005 .. any other data...
3030 * @class Roo.bootstrap.Img
3031 * @extends Roo.bootstrap.Component
3032 * Bootstrap Img class
3033 * @cfg {Boolean} imgResponsive false | true
3034 * @cfg {String} border rounded | circle | thumbnail
3035 * @cfg {String} src image source
3036 * @cfg {String} alt image alternative text
3037 * @cfg {String} href a tag href
3038 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3039 * @cfg {String} xsUrl xs image source
3040 * @cfg {String} smUrl sm image source
3041 * @cfg {String} mdUrl md image source
3042 * @cfg {String} lgUrl lg image source
3045 * Create a new Input
3046 * @param {Object} config The config object
3049 Roo.bootstrap.Img = function(config){
3050 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056 * The img click event for the img.
3057 * @param {Roo.EventObject} e
3063 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3065 imgResponsive: true,
3075 getAutoCreate : function()
3077 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3078 return this.createSingleImg();
3083 cls: 'roo-image-responsive-group',
3088 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3090 if(!_this[size + 'Url']){
3096 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3097 html: _this.html || cfg.html,
3098 src: _this[size + 'Url']
3101 img.cls += ' roo-image-responsive-' + size;
3103 var s = ['xs', 'sm', 'md', 'lg'];
3105 s.splice(s.indexOf(size), 1);
3107 Roo.each(s, function(ss){
3108 img.cls += ' hidden-' + ss;
3111 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3112 cfg.cls += ' img-' + _this.border;
3116 cfg.alt = _this.alt;
3129 a.target = _this.target;
3133 cfg.cn.push((_this.href) ? a : img);
3140 createSingleImg : function()
3144 cls: (this.imgResponsive) ? 'img-responsive' : '',
3146 src : 'about:blank' // just incase src get's set to undefined?!?
3149 cfg.html = this.html || cfg.html;
3151 cfg.src = this.src || cfg.src;
3153 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3154 cfg.cls += ' img-' + this.border;
3171 a.target = this.target;
3176 return (this.href) ? a : cfg;
3179 initEvents: function()
3182 this.el.on('click', this.onClick, this);
3187 onClick : function(e)
3189 Roo.log('img onclick');
3190 this.fireEvent('click', this, e);
3193 * Sets the url of the image - used to update it
3194 * @param {String} url the url of the image
3197 setSrc : function(url)
3201 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3202 this.el.dom.src = url;
3206 this.el.select('img', true).first().dom.src = url;
3222 * @class Roo.bootstrap.Link
3223 * @extends Roo.bootstrap.Component
3224 * Bootstrap Link Class
3225 * @cfg {String} alt image alternative text
3226 * @cfg {String} href a tag href
3227 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3228 * @cfg {String} html the content of the link.
3229 * @cfg {String} anchor name for the anchor link
3230 * @cfg {String} fa - favicon
3232 * @cfg {Boolean} preventDefault (true | false) default false
3236 * Create a new Input
3237 * @param {Object} config The config object
3240 Roo.bootstrap.Link = function(config){
3241 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3247 * The img click event for the img.
3248 * @param {Roo.EventObject} e
3254 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3258 preventDefault: false,
3264 getAutoCreate : function()
3266 var html = this.html || '';
3268 if (this.fa !== false) {
3269 html = '<i class="fa fa-' + this.fa + '"></i>';
3274 // anchor's do not require html/href...
3275 if (this.anchor === false) {
3277 cfg.href = this.href || '#';
3279 cfg.name = this.anchor;
3280 if (this.html !== false || this.fa !== false) {
3283 if (this.href !== false) {
3284 cfg.href = this.href;
3288 if(this.alt !== false){
3293 if(this.target !== false) {
3294 cfg.target = this.target;
3300 initEvents: function() {
3302 if(!this.href || this.preventDefault){
3303 this.el.on('click', this.onClick, this);
3307 onClick : function(e)
3309 if(this.preventDefault){
3312 //Roo.log('img onclick');
3313 this.fireEvent('click', this, e);
3326 * @class Roo.bootstrap.Header
3327 * @extends Roo.bootstrap.Component
3328 * Bootstrap Header class
3329 * @cfg {String} html content of header
3330 * @cfg {Number} level (1|2|3|4|5|6) default 1
3333 * Create a new Header
3334 * @param {Object} config The config object
3338 Roo.bootstrap.Header = function(config){
3339 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3342 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3350 getAutoCreate : function(){
3355 tag: 'h' + (1 *this.level),
3356 html: this.html || ''
3368 * Ext JS Library 1.1.1
3369 * Copyright(c) 2006-2007, Ext JS, LLC.
3371 * Originally Released Under LGPL - original licence link has changed is not relivant.
3374 * <script type="text/javascript">
3378 * @class Roo.bootstrap.MenuMgr
3379 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3382 Roo.bootstrap.MenuMgr = function(){
3383 var menus, active, groups = {}, attached = false, lastShow = new Date();
3385 // private - called when first menu is created
3388 active = new Roo.util.MixedCollection();
3389 Roo.get(document).addKeyListener(27, function(){
3390 if(active.length > 0){
3398 if(active && active.length > 0){
3399 var c = active.clone();
3409 if(active.length < 1){
3410 Roo.get(document).un("mouseup", onMouseDown);
3418 var last = active.last();
3419 lastShow = new Date();
3422 Roo.get(document).on("mouseup", onMouseDown);
3427 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3428 m.parentMenu.activeChild = m;
3429 }else if(last && last.isVisible()){
3430 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3435 function onBeforeHide(m){
3437 m.activeChild.hide();
3439 if(m.autoHideTimer){
3440 clearTimeout(m.autoHideTimer);
3441 delete m.autoHideTimer;
3446 function onBeforeShow(m){
3447 var pm = m.parentMenu;
3448 if(!pm && !m.allowOtherMenus){
3450 }else if(pm && pm.activeChild && active != m){
3451 pm.activeChild.hide();
3455 // private this should really trigger on mouseup..
3456 function onMouseDown(e){
3457 Roo.log("on Mouse Up");
3459 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3460 Roo.log("MenuManager hideAll");
3469 function onBeforeCheck(mi, state){
3471 var g = groups[mi.group];
3472 for(var i = 0, l = g.length; i < l; i++){
3474 g[i].setChecked(false);
3483 * Hides all menus that are currently visible
3485 hideAll : function(){
3490 register : function(menu){
3494 menus[menu.id] = menu;
3495 menu.on("beforehide", onBeforeHide);
3496 menu.on("hide", onHide);
3497 menu.on("beforeshow", onBeforeShow);
3498 menu.on("show", onShow);
3500 if(g && menu.events["checkchange"]){
3504 groups[g].push(menu);
3505 menu.on("checkchange", onCheck);
3510 * Returns a {@link Roo.menu.Menu} object
3511 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3512 * be used to generate and return a new Menu instance.
3514 get : function(menu){
3515 if(typeof menu == "string"){ // menu id
3517 }else if(menu.events){ // menu instance
3520 /*else if(typeof menu.length == 'number'){ // array of menu items?
3521 return new Roo.bootstrap.Menu({items:menu});
3522 }else{ // otherwise, must be a config
3523 return new Roo.bootstrap.Menu(menu);
3530 unregister : function(menu){
3531 delete menus[menu.id];
3532 menu.un("beforehide", onBeforeHide);
3533 menu.un("hide", onHide);
3534 menu.un("beforeshow", onBeforeShow);
3535 menu.un("show", onShow);
3537 if(g && menu.events["checkchange"]){
3538 groups[g].remove(menu);
3539 menu.un("checkchange", onCheck);
3544 registerCheckable : function(menuItem){
3545 var g = menuItem.group;
3550 groups[g].push(menuItem);
3551 menuItem.on("beforecheckchange", onBeforeCheck);
3556 unregisterCheckable : function(menuItem){
3557 var g = menuItem.group;
3559 groups[g].remove(menuItem);
3560 menuItem.un("beforecheckchange", onBeforeCheck);
3572 * @class Roo.bootstrap.Menu
3573 * @extends Roo.bootstrap.Component
3574 * Bootstrap Menu class - container for MenuItems
3575 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3576 * @cfg {bool} hidden if the menu should be hidden when rendered.
3577 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3578 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3579 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @param {Object} config The config object
3587 Roo.bootstrap.Menu = function(config){
3588 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3589 if (this.registerMenu && this.type != 'treeview') {
3590 Roo.bootstrap.MenuMgr.register(this);
3597 * Fires before this menu is displayed (return false to block)
3598 * @param {Roo.menu.Menu} this
3603 * Fires before this menu is hidden (return false to block)
3604 * @param {Roo.menu.Menu} this
3609 * Fires after this menu is displayed
3610 * @param {Roo.menu.Menu} this
3615 * Fires after this menu is hidden
3616 * @param {Roo.menu.Menu} this
3621 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3622 * @param {Roo.menu.Menu} this
3623 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3624 * @param {Roo.EventObject} e
3629 * Fires when the mouse is hovering over this menu
3630 * @param {Roo.menu.Menu} this
3631 * @param {Roo.EventObject} e
3632 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3637 * Fires when the mouse exits this menu
3638 * @param {Roo.menu.Menu} this
3639 * @param {Roo.EventObject} e
3640 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3645 * Fires when a menu item contained in this menu is clicked
3646 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3647 * @param {Roo.EventObject} e
3651 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3654 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3658 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3661 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3663 registerMenu : true,
3665 menuItems :false, // stores the menu items..
3675 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3677 hideTrigger : false,
3680 getChildContainer : function() {
3684 getAutoCreate : function(){
3686 //if (['right'].indexOf(this.align)!==-1) {
3687 // cfg.cn[1].cls += ' pull-right'
3693 cls : 'dropdown-menu shadow' ,
3694 style : 'z-index:1000'
3698 if (this.type === 'submenu') {
3699 cfg.cls = 'submenu active';
3701 if (this.type === 'treeview') {
3702 cfg.cls = 'treeview-menu';
3707 initEvents : function() {
3709 // Roo.log("ADD event");
3710 // Roo.log(this.triggerEl.dom);
3712 this.triggerEl.on('click', this.onTriggerClick, this);
3714 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3716 if (!this.hideTrigger) {
3717 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3718 // dropdown toggle on the 'a' in BS4?
3719 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3721 this.triggerEl.addClass('dropdown-toggle');
3725 this.el.on('touchstart' , this.onTouch, this);
3727 this.el.on('click' , this.onClick, this);
3729 this.el.on("mouseover", this.onMouseOver, this);
3730 this.el.on("mouseout", this.onMouseOut, this);
3734 findTargetItem : function(e)
3736 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3740 //Roo.log(t); Roo.log(t.id);
3742 //Roo.log(this.menuitems);
3743 return this.menuitems.get(t.id);
3745 //return this.items.get(t.menuItemId);
3751 onTouch : function(e)
3753 Roo.log("menu.onTouch");
3754 //e.stopEvent(); this make the user popdown broken
3758 onClick : function(e)
3760 Roo.log("menu.onClick");
3762 var t = this.findTargetItem(e);
3763 if(!t || t.isContainer){
3768 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3769 if(t == this.activeItem && t.shouldDeactivate(e)){
3770 this.activeItem.deactivate();
3771 delete this.activeItem;
3775 this.setActiveItem(t, true);
3783 Roo.log('pass click event');
3787 this.fireEvent("click", this, t, e);
3791 if(!t.href.length || t.href == '#'){
3792 (function() { _this.hide(); }).defer(100);
3797 onMouseOver : function(e){
3798 var t = this.findTargetItem(e);
3801 // if(t.canActivate && !t.disabled){
3802 // this.setActiveItem(t, true);
3806 this.fireEvent("mouseover", this, e, t);
3808 isVisible : function(){
3809 return !this.hidden;
3811 onMouseOut : function(e){
3812 var t = this.findTargetItem(e);
3815 // if(t == this.activeItem && t.shouldDeactivate(e)){
3816 // this.activeItem.deactivate();
3817 // delete this.activeItem;
3820 this.fireEvent("mouseout", this, e, t);
3825 * Displays this menu relative to another element
3826 * @param {String/HTMLElement/Roo.Element} element The element to align to
3827 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3828 * the element (defaults to this.defaultAlign)
3829 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3831 show : function(el, pos, parentMenu)
3833 if (false === this.fireEvent("beforeshow", this)) {
3834 Roo.log("show canceled");
3837 this.parentMenu = parentMenu;
3842 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3845 * Displays this menu at a specific xy position
3846 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3847 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3849 showAt : function(xy, parentMenu, /* private: */_e){
3850 this.parentMenu = parentMenu;
3855 this.fireEvent("beforeshow", this);
3856 //xy = this.el.adjustForConstraints(xy);
3860 this.hideMenuItems();
3861 this.hidden = false;
3862 this.triggerEl.addClass('open');
3863 this.el.addClass('show');
3865 // reassign x when hitting right
3866 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3867 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3870 // reassign y when hitting bottom
3871 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3872 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3875 // but the list may align on trigger left or trigger top... should it be a properity?
3877 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3882 this.fireEvent("show", this);
3888 this.doFocus.defer(50, this);
3892 doFocus : function(){
3894 this.focusEl.focus();
3899 * Hides this menu and optionally all parent menus
3900 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3902 hide : function(deep)
3904 if (false === this.fireEvent("beforehide", this)) {
3905 Roo.log("hide canceled");
3908 this.hideMenuItems();
3909 if(this.el && this.isVisible()){
3911 if(this.activeItem){
3912 this.activeItem.deactivate();
3913 this.activeItem = null;
3915 this.triggerEl.removeClass('open');;
3916 this.el.removeClass('show');
3918 this.fireEvent("hide", this);
3920 if(deep === true && this.parentMenu){
3921 this.parentMenu.hide(true);
3925 onTriggerClick : function(e)
3927 Roo.log('trigger click');
3929 var target = e.getTarget();
3931 Roo.log(target.nodeName.toLowerCase());
3933 if(target.nodeName.toLowerCase() === 'i'){
3939 onTriggerPress : function(e)
3941 Roo.log('trigger press');
3942 //Roo.log(e.getTarget());
3943 // Roo.log(this.triggerEl.dom);
3945 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3946 var pel = Roo.get(e.getTarget());
3947 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3948 Roo.log('is treeview or dropdown?');
3952 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3956 if (this.isVisible()) {
3961 this.show(this.triggerEl, '?', false);
3964 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3971 hideMenuItems : function()
3973 Roo.log("hide Menu Items");
3978 this.el.select('.open',true).each(function(aa) {
3980 aa.removeClass('open');
3984 addxtypeChild : function (tree, cntr) {
3985 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3987 this.menuitems.add(comp);
3999 this.getEl().dom.innerHTML = '';
4000 this.menuitems.clear();
4014 * @class Roo.bootstrap.MenuItem
4015 * @extends Roo.bootstrap.Component
4016 * Bootstrap MenuItem class
4017 * @cfg {String} html the menu label
4018 * @cfg {String} href the link
4019 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4020 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4021 * @cfg {Boolean} active used on sidebars to highlight active itesm
4022 * @cfg {String} fa favicon to show on left of menu item.
4023 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4027 * Create a new MenuItem
4028 * @param {Object} config The config object
4032 Roo.bootstrap.MenuItem = function(config){
4033 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4038 * The raw click event for the entire grid.
4039 * @param {Roo.bootstrap.MenuItem} this
4040 * @param {Roo.EventObject} e
4046 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4050 preventDefault: false,
4051 isContainer : false,
4055 getAutoCreate : function(){
4057 if(this.isContainer){
4060 cls: 'dropdown-menu-item '
4070 cls : 'dropdown-item',
4075 if (this.fa !== false) {
4078 cls : 'fa fa-' + this.fa
4087 cls: 'dropdown-menu-item',
4090 if (this.parent().type == 'treeview') {
4091 cfg.cls = 'treeview-menu';
4094 cfg.cls += ' active';
4099 anc.href = this.href || cfg.cn[0].href ;
4100 ctag.html = this.html || cfg.cn[0].html ;
4104 initEvents: function()
4106 if (this.parent().type == 'treeview') {
4107 this.el.select('a').on('click', this.onClick, this);
4111 this.menu.parentType = this.xtype;
4112 this.menu.triggerEl = this.el;
4113 this.menu = this.addxtype(Roo.apply({}, this.menu));
4117 onClick : function(e)
4119 Roo.log('item on click ');
4121 if(this.preventDefault){
4124 //this.parent().hideMenuItems();
4126 this.fireEvent('click', this, e);
4145 * @class Roo.bootstrap.MenuSeparator
4146 * @extends Roo.bootstrap.Component
4147 * Bootstrap MenuSeparator class
4150 * Create a new MenuItem
4151 * @param {Object} config The config object
4155 Roo.bootstrap.MenuSeparator = function(config){
4156 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4159 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4161 getAutoCreate : function(){
4180 * @class Roo.bootstrap.Modal
4181 * @extends Roo.bootstrap.Component
4182 * Bootstrap Modal class
4183 * @cfg {String} title Title of dialog
4184 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4185 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4186 * @cfg {Boolean} specificTitle default false
4187 * @cfg {Array} buttons Array of buttons or standard button set..
4188 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4189 * @cfg {Boolean} animate default true
4190 * @cfg {Boolean} allow_close default true
4191 * @cfg {Boolean} fitwindow default false
4192 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4193 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4194 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4195 * @cfg {String} size (sm|lg|xl) default empty
4196 * @cfg {Number} max_width set the max width of modal
4197 * @cfg {Boolean} editableTitle can the title be edited
4202 * Create a new Modal Dialog
4203 * @param {Object} config The config object
4206 Roo.bootstrap.Modal = function(config){
4207 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4212 * The raw btnclick event for the button
4213 * @param {Roo.EventObject} e
4218 * Fire when dialog resize
4219 * @param {Roo.bootstrap.Modal} this
4220 * @param {Roo.EventObject} e
4224 * @event titlechanged
4225 * Fire when the editable title has been changed
4226 * @param {Roo.bootstrap.Modal} this
4227 * @param {Roo.EventObject} value
4229 "titlechanged" : true
4232 this.buttons = this.buttons || [];
4235 this.tmpl = Roo.factory(this.tmpl);
4240 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4242 title : 'test dialog',
4252 specificTitle: false,
4254 buttonPosition: 'right',
4276 editableTitle : false,
4278 onRender : function(ct, position)
4280 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4283 var cfg = Roo.apply({}, this.getAutoCreate());
4286 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4288 //if (!cfg.name.length) {
4292 cfg.cls += ' ' + this.cls;
4295 cfg.style = this.style;
4297 this.el = Roo.get(document.body).createChild(cfg, position);
4299 //var type = this.el.dom.type;
4302 if(this.tabIndex !== undefined){
4303 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4306 this.dialogEl = this.el.select('.modal-dialog',true).first();
4307 this.bodyEl = this.el.select('.modal-body',true).first();
4308 this.closeEl = this.el.select('.modal-header .close', true).first();
4309 this.headerEl = this.el.select('.modal-header',true).first();
4310 this.titleEl = this.el.select('.modal-title',true).first();
4311 this.footerEl = this.el.select('.modal-footer',true).first();
4313 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4315 //this.el.addClass("x-dlg-modal");
4317 if (this.buttons.length) {
4318 Roo.each(this.buttons, function(bb) {
4319 var b = Roo.apply({}, bb);
4320 b.xns = b.xns || Roo.bootstrap;
4321 b.xtype = b.xtype || 'Button';
4322 if (typeof(b.listeners) == 'undefined') {
4323 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4326 var btn = Roo.factory(b);
4328 btn.render(this.getButtonContainer());
4332 // render the children.
4335 if(typeof(this.items) != 'undefined'){
4336 var items = this.items;
4339 for(var i =0;i < items.length;i++) {
4340 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4344 this.items = nitems;
4346 // where are these used - they used to be body/close/footer
4350 //this.el.addClass([this.fieldClass, this.cls]);
4354 getAutoCreate : function()
4356 // we will default to modal-body-overflow - might need to remove or make optional later.
4358 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4359 html : this.html || ''
4364 cls : 'modal-title',
4368 if(this.specificTitle){ // WTF is this?
4373 if (this.allow_close && Roo.bootstrap.version == 3) {
4383 if (this.editableTitle) {
4385 cls: 'form-control roo-editable-title d-none',
4391 if (this.allow_close && Roo.bootstrap.version == 4) {
4401 if(this.size.length){
4402 size = 'modal-' + this.size;
4405 var footer = Roo.bootstrap.version == 3 ?
4407 cls : 'modal-footer',
4411 cls: 'btn-' + this.buttonPosition
4416 { // BS4 uses mr-auto on left buttons....
4417 cls : 'modal-footer'
4428 cls: "modal-dialog " + size,
4431 cls : "modal-content",
4434 cls : 'modal-header',
4449 modal.cls += ' fade';
4455 getChildContainer : function() {
4460 getButtonContainer : function() {
4462 return Roo.bootstrap.version == 4 ?
4463 this.el.select('.modal-footer',true).first()
4464 : this.el.select('.modal-footer div',true).first();
4467 initEvents : function()
4469 if (this.allow_close) {
4470 this.closeEl.on('click', this.hide, this);
4472 Roo.EventManager.onWindowResize(this.resize, this, true);
4473 if (this.editableTitle) {
4474 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4475 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4476 this.headerEditEl.on('keyup', function(e) {
4477 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4478 this.toggleHeaderInput(false)
4481 this.headerEditEl.on('blur', function(e) {
4482 this.toggleHeaderInput(false)
4491 this.maskEl.setSize(
4492 Roo.lib.Dom.getViewWidth(true),
4493 Roo.lib.Dom.getViewHeight(true)
4496 if (this.fitwindow) {
4498 this.dialogEl.setStyle( { 'max-width' : '100%' });
4500 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4501 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4506 if(this.max_width !== 0) {
4508 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4511 this.setSize(w, this.height);
4515 if(this.max_height) {
4516 this.setSize(w,Math.min(
4518 Roo.lib.Dom.getViewportHeight(true) - 60
4524 if(!this.fit_content) {
4525 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4529 this.setSize(w, Math.min(
4531 this.headerEl.getHeight() +
4532 this.footerEl.getHeight() +
4533 this.getChildHeight(this.bodyEl.dom.childNodes),
4534 Roo.lib.Dom.getViewportHeight(true) - 60)
4540 setSize : function(w,h)
4551 if (!this.rendered) {
4554 this.toggleHeaderInput(false);
4555 //this.el.setStyle('display', 'block');
4556 this.el.removeClass('hideing');
4557 this.el.dom.style.display='block';
4559 Roo.get(document.body).addClass('modal-open');
4561 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4564 this.el.addClass('show');
4565 this.el.addClass('in');
4568 this.el.addClass('show');
4569 this.el.addClass('in');
4572 // not sure how we can show data in here..
4574 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4577 Roo.get(document.body).addClass("x-body-masked");
4579 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4580 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4581 this.maskEl.dom.style.display = 'block';
4582 this.maskEl.addClass('show');
4587 this.fireEvent('show', this);
4589 // set zindex here - otherwise it appears to be ignored...
4590 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4593 this.items.forEach( function(e) {
4594 e.layout ? e.layout() : false;
4602 if(this.fireEvent("beforehide", this) !== false){
4604 this.maskEl.removeClass('show');
4606 this.maskEl.dom.style.display = '';
4607 Roo.get(document.body).removeClass("x-body-masked");
4608 this.el.removeClass('in');
4609 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4611 if(this.animate){ // why
4612 this.el.addClass('hideing');
4613 this.el.removeClass('show');
4615 if (!this.el.hasClass('hideing')) {
4616 return; // it's been shown again...
4619 this.el.dom.style.display='';
4621 Roo.get(document.body).removeClass('modal-open');
4622 this.el.removeClass('hideing');
4626 this.el.removeClass('show');
4627 this.el.dom.style.display='';
4628 Roo.get(document.body).removeClass('modal-open');
4631 this.fireEvent('hide', this);
4634 isVisible : function()
4637 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4641 addButton : function(str, cb)
4645 var b = Roo.apply({}, { html : str } );
4646 b.xns = b.xns || Roo.bootstrap;
4647 b.xtype = b.xtype || 'Button';
4648 if (typeof(b.listeners) == 'undefined') {
4649 b.listeners = { click : cb.createDelegate(this) };
4652 var btn = Roo.factory(b);
4654 btn.render(this.getButtonContainer());
4660 setDefaultButton : function(btn)
4662 //this.el.select('.modal-footer').()
4665 resizeTo: function(w,h)
4667 this.dialogEl.setWidth(w);
4669 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4671 this.bodyEl.setHeight(h - diff);
4673 this.fireEvent('resize', this);
4676 setContentSize : function(w, h)
4680 onButtonClick: function(btn,e)
4683 this.fireEvent('btnclick', btn.name, e);
4686 * Set the title of the Dialog
4687 * @param {String} str new Title
4689 setTitle: function(str) {
4690 this.titleEl.dom.innerHTML = str;
4694 * Set the body of the Dialog
4695 * @param {String} str new Title
4697 setBody: function(str) {
4698 this.bodyEl.dom.innerHTML = str;
4701 * Set the body of the Dialog using the template
4702 * @param {Obj} data - apply this data to the template and replace the body contents.
4704 applyBody: function(obj)
4707 Roo.log("Error - using apply Body without a template");
4710 this.tmpl.overwrite(this.bodyEl, obj);
4713 getChildHeight : function(child_nodes)
4717 child_nodes.length == 0
4722 var child_height = 0;
4724 for(var i = 0; i < child_nodes.length; i++) {
4727 * for modal with tabs...
4728 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4730 var layout_childs = child_nodes[i].childNodes;
4732 for(var j = 0; j < layout_childs.length; j++) {
4734 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4736 var layout_body_childs = layout_childs[j].childNodes;
4738 for(var k = 0; k < layout_body_childs.length; k++) {
4740 if(layout_body_childs[k].classList.contains('navbar')) {
4741 child_height += layout_body_childs[k].offsetHeight;
4745 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4747 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4749 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4751 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4752 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4767 child_height += child_nodes[i].offsetHeight;
4768 // Roo.log(child_nodes[i].offsetHeight);
4771 return child_height;
4773 toggleHeaderInput : function(is_edit)
4775 if (!this.editableTitle) {
4776 return; // not editable.
4778 if (is_edit && this.is_header_editing) {
4779 return; // already editing..
4783 this.headerEditEl.dom.value = this.title;
4784 this.headerEditEl.removeClass('d-none');
4785 this.headerEditEl.dom.focus();
4786 this.titleEl.addClass('d-none');
4788 this.is_header_editing = true;
4791 // flip back to not editing.
4792 this.title = this.headerEditEl.dom.value;
4793 this.headerEditEl.addClass('d-none');
4794 this.titleEl.removeClass('d-none');
4795 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4796 this.is_header_editing = false;
4797 this.fireEvent('titlechanged', this, this.title);
4806 Roo.apply(Roo.bootstrap.Modal, {
4808 * Button config that displays a single OK button
4817 * Button config that displays Yes and No buttons
4833 * Button config that displays OK and Cancel buttons
4848 * Button config that displays Yes, No and Cancel buttons
4873 * messagebox - can be used as a replace
4877 * @class Roo.MessageBox
4878 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4882 Roo.Msg.alert('Status', 'Changes saved successfully.');
4884 // Prompt for user data:
4885 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4887 // process text value...
4891 // Show a dialog using config options:
4893 title:'Save Changes?',
4894 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4895 buttons: Roo.Msg.YESNOCANCEL,
4902 Roo.bootstrap.MessageBox = function(){
4903 var dlg, opt, mask, waitTimer;
4904 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4905 var buttons, activeTextEl, bwidth;
4909 var handleButton = function(button){
4911 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4915 var handleHide = function(){
4917 dlg.el.removeClass(opt.cls);
4920 // Roo.TaskMgr.stop(waitTimer);
4921 // waitTimer = null;
4926 var updateButtons = function(b){
4929 buttons["ok"].hide();
4930 buttons["cancel"].hide();
4931 buttons["yes"].hide();
4932 buttons["no"].hide();
4933 dlg.footerEl.hide();
4937 dlg.footerEl.show();
4938 for(var k in buttons){
4939 if(typeof buttons[k] != "function"){
4942 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4943 width += buttons[k].el.getWidth()+15;
4953 var handleEsc = function(d, k, e){
4954 if(opt && opt.closable !== false){
4964 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4965 * @return {Roo.BasicDialog} The BasicDialog element
4967 getDialog : function(){
4969 dlg = new Roo.bootstrap.Modal( {
4972 //constraintoviewport:false,
4974 //collapsible : false,
4979 //buttonAlign:"center",
4980 closeClick : function(){
4981 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4984 handleButton("cancel");
4989 dlg.on("hide", handleHide);
4991 //dlg.addKeyListener(27, handleEsc);
4993 this.buttons = buttons;
4994 var bt = this.buttonText;
4995 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4996 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4997 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4998 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5000 bodyEl = dlg.bodyEl.createChild({
5002 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5003 '<textarea class="roo-mb-textarea"></textarea>' +
5004 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5006 msgEl = bodyEl.dom.firstChild;
5007 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5008 textboxEl.enableDisplayMode();
5009 textboxEl.addKeyListener([10,13], function(){
5010 if(dlg.isVisible() && opt && opt.buttons){
5013 }else if(opt.buttons.yes){
5014 handleButton("yes");
5018 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5019 textareaEl.enableDisplayMode();
5020 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5021 progressEl.enableDisplayMode();
5023 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5024 var pf = progressEl.dom.firstChild;
5026 pp = Roo.get(pf.firstChild);
5027 pp.setHeight(pf.offsetHeight);
5035 * Updates the message box body text
5036 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5037 * the XHTML-compliant non-breaking space character '&#160;')
5038 * @return {Roo.MessageBox} This message box
5040 updateText : function(text)
5042 if(!dlg.isVisible() && !opt.width){
5043 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5044 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5046 msgEl.innerHTML = text || ' ';
5048 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5049 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5051 Math.min(opt.width || cw , this.maxWidth),
5052 Math.max(opt.minWidth || this.minWidth, bwidth)
5055 activeTextEl.setWidth(w);
5057 if(dlg.isVisible()){
5058 dlg.fixedcenter = false;
5060 // to big, make it scroll. = But as usual stupid IE does not support
5063 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5064 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5065 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5067 bodyEl.dom.style.height = '';
5068 bodyEl.dom.style.overflowY = '';
5071 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5073 bodyEl.dom.style.overflowX = '';
5076 dlg.setContentSize(w, bodyEl.getHeight());
5077 if(dlg.isVisible()){
5078 dlg.fixedcenter = true;
5084 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5085 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5086 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5087 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5088 * @return {Roo.MessageBox} This message box
5090 updateProgress : function(value, text){
5092 this.updateText(text);
5095 if (pp) { // weird bug on my firefox - for some reason this is not defined
5096 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5097 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5103 * Returns true if the message box is currently displayed
5104 * @return {Boolean} True if the message box is visible, else false
5106 isVisible : function(){
5107 return dlg && dlg.isVisible();
5111 * Hides the message box if it is displayed
5114 if(this.isVisible()){
5120 * Displays a new message box, or reinitializes an existing message box, based on the config options
5121 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5122 * The following config object properties are supported:
5124 Property Type Description
5125 ---------- --------------- ------------------------------------------------------------------------------------
5126 animEl String/Element An id or Element from which the message box should animate as it opens and
5127 closes (defaults to undefined)
5128 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5129 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5130 closable Boolean False to hide the top-right close button (defaults to true). Note that
5131 progress and wait dialogs will ignore this property and always hide the
5132 close button as they can only be closed programmatically.
5133 cls String A custom CSS class to apply to the message box element
5134 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5135 displayed (defaults to 75)
5136 fn Function A callback function to execute after closing the dialog. The arguments to the
5137 function will be btn (the name of the button that was clicked, if applicable,
5138 e.g. "ok"), and text (the value of the active text field, if applicable).
5139 Progress and wait dialogs will ignore this option since they do not respond to
5140 user actions and can only be closed programmatically, so any required function
5141 should be called by the same code after it closes the dialog.
5142 icon String A CSS class that provides a background image to be used as an icon for
5143 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5144 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5145 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5146 modal Boolean False to allow user interaction with the page while the message box is
5147 displayed (defaults to true)
5148 msg String A string that will replace the existing message box body text (defaults
5149 to the XHTML-compliant non-breaking space character ' ')
5150 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5151 progress Boolean True to display a progress bar (defaults to false)
5152 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5153 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5154 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5155 title String The title text
5156 value String The string value to set into the active textbox element if displayed
5157 wait Boolean True to display a progress bar (defaults to false)
5158 width Number The width of the dialog in pixels
5165 msg: 'Please enter your address:',
5167 buttons: Roo.MessageBox.OKCANCEL,
5170 animEl: 'addAddressBtn'
5173 * @param {Object} config Configuration options
5174 * @return {Roo.MessageBox} This message box
5176 show : function(options)
5179 // this causes nightmares if you show one dialog after another
5180 // especially on callbacks..
5182 if(this.isVisible()){
5185 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5186 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5187 Roo.log("New Dialog Message:" + options.msg )
5188 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5189 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5192 var d = this.getDialog();
5194 d.setTitle(opt.title || " ");
5195 d.closeEl.setDisplayed(opt.closable !== false);
5196 activeTextEl = textboxEl;
5197 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5202 textareaEl.setHeight(typeof opt.multiline == "number" ?
5203 opt.multiline : this.defaultTextHeight);
5204 activeTextEl = textareaEl;
5213 progressEl.setDisplayed(opt.progress === true);
5215 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5217 this.updateProgress(0);
5218 activeTextEl.dom.value = opt.value || "";
5220 dlg.setDefaultButton(activeTextEl);
5222 var bs = opt.buttons;
5226 }else if(bs && bs.yes){
5227 db = buttons["yes"];
5229 dlg.setDefaultButton(db);
5231 bwidth = updateButtons(opt.buttons);
5232 this.updateText(opt.msg);
5234 d.el.addClass(opt.cls);
5236 d.proxyDrag = opt.proxyDrag === true;
5237 d.modal = opt.modal !== false;
5238 d.mask = opt.modal !== false ? mask : false;
5240 // force it to the end of the z-index stack so it gets a cursor in FF
5241 document.body.appendChild(dlg.el.dom);
5242 d.animateTarget = null;
5243 d.show(options.animEl);
5249 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5250 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5251 * and closing the message box when the process is complete.
5252 * @param {String} title The title bar text
5253 * @param {String} msg The message box body text
5254 * @return {Roo.MessageBox} This message box
5256 progress : function(title, msg){
5263 minWidth: this.minProgressWidth,
5270 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5271 * If a callback function is passed it will be called after the user clicks the button, and the
5272 * id of the button that was clicked will be passed as the only parameter to the callback
5273 * (could also be the top-right close button).
5274 * @param {String} title The title bar text
5275 * @param {String} msg The message box body text
5276 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5277 * @param {Object} scope (optional) The scope of the callback function
5278 * @return {Roo.MessageBox} This message box
5280 alert : function(title, msg, fn, scope)
5295 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5296 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5297 * You are responsible for closing the message box when the process is complete.
5298 * @param {String} msg The message box body text
5299 * @param {String} title (optional) The title bar text
5300 * @return {Roo.MessageBox} This message box
5302 wait : function(msg, title){
5313 waitTimer = Roo.TaskMgr.start({
5315 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5323 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5324 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5325 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5326 * @param {String} title The title bar text
5327 * @param {String} msg The message box body text
5328 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5329 * @param {Object} scope (optional) The scope of the callback function
5330 * @return {Roo.MessageBox} This message box
5332 confirm : function(title, msg, fn, scope){
5336 buttons: this.YESNO,
5345 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5346 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5347 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5348 * (could also be the top-right close button) and the text that was entered will be passed as the two
5349 * parameters to the callback.
5350 * @param {String} title The title bar text
5351 * @param {String} msg The message box body text
5352 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5353 * @param {Object} scope (optional) The scope of the callback function
5354 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5355 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5356 * @return {Roo.MessageBox} This message box
5358 prompt : function(title, msg, fn, scope, multiline){
5362 buttons: this.OKCANCEL,
5367 multiline: multiline,
5374 * Button config that displays a single OK button
5379 * Button config that displays Yes and No buttons
5382 YESNO : {yes:true, no:true},
5384 * Button config that displays OK and Cancel buttons
5387 OKCANCEL : {ok:true, cancel:true},
5389 * Button config that displays Yes, No and Cancel buttons
5392 YESNOCANCEL : {yes:true, no:true, cancel:true},
5395 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5398 defaultTextHeight : 75,
5400 * The maximum width in pixels of the message box (defaults to 600)
5405 * The minimum width in pixels of the message box (defaults to 100)
5410 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5411 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5414 minProgressWidth : 250,
5416 * An object containing the default button text strings that can be overriden for localized language support.
5417 * Supported properties are: ok, cancel, yes and no.
5418 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5431 * Shorthand for {@link Roo.MessageBox}
5433 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5434 Roo.Msg = Roo.Msg || Roo.MessageBox;
5443 * @class Roo.bootstrap.Navbar
5444 * @extends Roo.bootstrap.Component
5445 * Bootstrap Navbar class
5448 * Create a new Navbar
5449 * @param {Object} config The config object
5453 Roo.bootstrap.Navbar = function(config){
5454 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5458 * @event beforetoggle
5459 * Fire before toggle the menu
5460 * @param {Roo.EventObject} e
5462 "beforetoggle" : true
5466 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5475 getAutoCreate : function(){
5478 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5482 initEvents :function ()
5484 //Roo.log(this.el.select('.navbar-toggle',true));
5485 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5492 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5494 var size = this.el.getSize();
5495 this.maskEl.setSize(size.width, size.height);
5496 this.maskEl.enableDisplayMode("block");
5505 getChildContainer : function()
5507 if (this.el && this.el.select('.collapse').getCount()) {
5508 return this.el.select('.collapse',true).first();
5523 onToggle : function()
5526 if(this.fireEvent('beforetoggle', this) === false){
5529 var ce = this.el.select('.navbar-collapse',true).first();
5531 if (!ce.hasClass('show')) {
5541 * Expand the navbar pulldown
5543 expand : function ()
5546 var ce = this.el.select('.navbar-collapse',true).first();
5547 if (ce.hasClass('collapsing')) {
5550 ce.dom.style.height = '';
5552 ce.addClass('in'); // old...
5553 ce.removeClass('collapse');
5554 ce.addClass('show');
5555 var h = ce.getHeight();
5557 ce.removeClass('show');
5558 // at this point we should be able to see it..
5559 ce.addClass('collapsing');
5561 ce.setHeight(0); // resize it ...
5562 ce.on('transitionend', function() {
5563 //Roo.log('done transition');
5564 ce.removeClass('collapsing');
5565 ce.addClass('show');
5566 ce.removeClass('collapse');
5568 ce.dom.style.height = '';
5569 }, this, { single: true} );
5571 ce.dom.scrollTop = 0;
5574 * Collapse the navbar pulldown
5576 collapse : function()
5578 var ce = this.el.select('.navbar-collapse',true).first();
5580 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5581 // it's collapsed or collapsing..
5584 ce.removeClass('in'); // old...
5585 ce.setHeight(ce.getHeight());
5586 ce.removeClass('show');
5587 ce.addClass('collapsing');
5589 ce.on('transitionend', function() {
5590 ce.dom.style.height = '';
5591 ce.removeClass('collapsing');
5592 ce.addClass('collapse');
5593 }, this, { single: true} );
5613 * @class Roo.bootstrap.NavSimplebar
5614 * @extends Roo.bootstrap.Navbar
5615 * Bootstrap Sidebar class
5617 * @cfg {Boolean} inverse is inverted color
5619 * @cfg {String} type (nav | pills | tabs)
5620 * @cfg {Boolean} arrangement stacked | justified
5621 * @cfg {String} align (left | right) alignment
5623 * @cfg {Boolean} main (true|false) main nav bar? default false
5624 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5626 * @cfg {String} tag (header|footer|nav|div) default is nav
5628 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5632 * Create a new Sidebar
5633 * @param {Object} config The config object
5637 Roo.bootstrap.NavSimplebar = function(config){
5638 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5641 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5657 getAutoCreate : function(){
5661 tag : this.tag || 'div',
5662 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5664 if (['light','white'].indexOf(this.weight) > -1) {
5665 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5667 cfg.cls += ' bg-' + this.weight;
5670 cfg.cls += ' navbar-inverse';
5674 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5676 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5685 cls: 'nav nav-' + this.xtype,
5691 this.type = this.type || 'nav';
5692 if (['tabs','pills'].indexOf(this.type) != -1) {
5693 cfg.cn[0].cls += ' nav-' + this.type
5697 if (this.type!=='nav') {
5698 Roo.log('nav type must be nav/tabs/pills')
5700 cfg.cn[0].cls += ' navbar-nav'
5706 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5707 cfg.cn[0].cls += ' nav-' + this.arrangement;
5711 if (this.align === 'right') {
5712 cfg.cn[0].cls += ' navbar-right';
5737 * navbar-expand-md fixed-top
5741 * @class Roo.bootstrap.NavHeaderbar
5742 * @extends Roo.bootstrap.NavSimplebar
5743 * Bootstrap Sidebar class
5745 * @cfg {String} brand what is brand
5746 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5747 * @cfg {String} brand_href href of the brand
5748 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5749 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5750 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5751 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5754 * Create a new Sidebar
5755 * @param {Object} config The config object
5759 Roo.bootstrap.NavHeaderbar = function(config){
5760 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5764 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5771 desktopCenter : false,
5774 getAutoCreate : function(){
5777 tag: this.nav || 'nav',
5778 cls: 'navbar navbar-expand-md',
5784 if (this.desktopCenter) {
5785 cn.push({cls : 'container', cn : []});
5793 cls: 'navbar-toggle navbar-toggler',
5794 'data-toggle': 'collapse',
5799 html: 'Toggle navigation'
5803 cls: 'icon-bar navbar-toggler-icon'
5816 cn.push( Roo.bootstrap.version == 4 ? btn : {
5818 cls: 'navbar-header',
5827 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5831 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5833 if (['light','white'].indexOf(this.weight) > -1) {
5834 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5836 cfg.cls += ' bg-' + this.weight;
5839 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5840 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5842 // tag can override this..
5844 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5847 if (this.brand !== '') {
5848 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5849 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5851 href: this.brand_href ? this.brand_href : '#',
5852 cls: 'navbar-brand',
5860 cfg.cls += ' main-nav';
5868 getHeaderChildContainer : function()
5870 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5871 return this.el.select('.navbar-header',true).first();
5874 return this.getChildContainer();
5877 getChildContainer : function()
5880 return this.el.select('.roo-navbar-collapse',true).first();
5885 initEvents : function()
5887 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5889 if (this.autohide) {
5894 Roo.get(document).on('scroll',function(e) {
5895 var ns = Roo.get(document).getScroll().top;
5896 var os = prevScroll;
5900 ft.removeClass('slideDown');
5901 ft.addClass('slideUp');
5904 ft.removeClass('slideUp');
5905 ft.addClass('slideDown');
5926 * @class Roo.bootstrap.NavSidebar
5927 * @extends Roo.bootstrap.Navbar
5928 * Bootstrap Sidebar class
5931 * Create a new Sidebar
5932 * @param {Object} config The config object
5936 Roo.bootstrap.NavSidebar = function(config){
5937 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5940 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5942 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5944 getAutoCreate : function(){
5949 cls: 'sidebar sidebar-nav'
5971 * @class Roo.bootstrap.NavGroup
5972 * @extends Roo.bootstrap.Component
5973 * Bootstrap NavGroup class
5974 * @cfg {String} align (left|right)
5975 * @cfg {Boolean} inverse
5976 * @cfg {String} type (nav|pills|tab) default nav
5977 * @cfg {String} navId - reference Id for navbar.
5978 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5981 * Create a new nav group
5982 * @param {Object} config The config object
5985 Roo.bootstrap.NavGroup = function(config){
5986 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5989 Roo.bootstrap.NavGroup.register(this);
5993 * Fires when the active item changes
5994 * @param {Roo.bootstrap.NavGroup} this
5995 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5996 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6003 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6015 getAutoCreate : function()
6017 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6023 if (Roo.bootstrap.version == 4) {
6024 if (['tabs','pills'].indexOf(this.type) != -1) {
6025 cfg.cls += ' nav-' + this.type;
6027 // trying to remove so header bar can right align top?
6028 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6029 // do not use on header bar...
6030 cfg.cls += ' navbar-nav';
6035 if (['tabs','pills'].indexOf(this.type) != -1) {
6036 cfg.cls += ' nav-' + this.type
6038 if (this.type !== 'nav') {
6039 Roo.log('nav type must be nav/tabs/pills')
6041 cfg.cls += ' navbar-nav'
6045 if (this.parent() && this.parent().sidebar) {
6048 cls: 'dashboard-menu sidebar-menu'
6054 if (this.form === true) {
6057 cls: 'navbar-form form-inline'
6059 //nav navbar-right ml-md-auto
6060 if (this.align === 'right') {
6061 cfg.cls += ' navbar-right ml-md-auto';
6063 cfg.cls += ' navbar-left';
6067 if (this.align === 'right') {
6068 cfg.cls += ' navbar-right ml-md-auto';
6070 cfg.cls += ' mr-auto';
6074 cfg.cls += ' navbar-inverse';
6082 * sets the active Navigation item
6083 * @param {Roo.bootstrap.NavItem} the new current navitem
6085 setActiveItem : function(item)
6088 Roo.each(this.navItems, function(v){
6093 v.setActive(false, true);
6100 item.setActive(true, true);
6101 this.fireEvent('changed', this, item, prev);
6106 * gets the active Navigation item
6107 * @return {Roo.bootstrap.NavItem} the current navitem
6109 getActive : function()
6113 Roo.each(this.navItems, function(v){
6124 indexOfNav : function()
6128 Roo.each(this.navItems, function(v,i){
6139 * adds a Navigation item
6140 * @param {Roo.bootstrap.NavItem} the navitem to add
6142 addItem : function(cfg)
6144 if (this.form && Roo.bootstrap.version == 4) {
6147 var cn = new Roo.bootstrap.NavItem(cfg);
6149 cn.parentId = this.id;
6150 cn.onRender(this.el, null);
6154 * register a Navigation item
6155 * @param {Roo.bootstrap.NavItem} the navitem to add
6157 register : function(item)
6159 this.navItems.push( item);
6160 item.navId = this.navId;
6165 * clear all the Navigation item
6168 clearAll : function()
6171 this.el.dom.innerHTML = '';
6174 getNavItem: function(tabId)
6177 Roo.each(this.navItems, function(e) {
6178 if (e.tabId == tabId) {
6188 setActiveNext : function()
6190 var i = this.indexOfNav(this.getActive());
6191 if (i > this.navItems.length) {
6194 this.setActiveItem(this.navItems[i+1]);
6196 setActivePrev : function()
6198 var i = this.indexOfNav(this.getActive());
6202 this.setActiveItem(this.navItems[i-1]);
6204 clearWasActive : function(except) {
6205 Roo.each(this.navItems, function(e) {
6206 if (e.tabId != except.tabId && e.was_active) {
6207 e.was_active = false;
6214 getWasActive : function ()
6217 Roo.each(this.navItems, function(e) {
6232 Roo.apply(Roo.bootstrap.NavGroup, {
6236 * register a Navigation Group
6237 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6239 register : function(navgrp)
6241 this.groups[navgrp.navId] = navgrp;
6245 * fetch a Navigation Group based on the navigation ID
6246 * @param {string} the navgroup to add
6247 * @returns {Roo.bootstrap.NavGroup} the navgroup
6249 get: function(navId) {
6250 if (typeof(this.groups[navId]) == 'undefined') {
6252 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6254 return this.groups[navId] ;
6269 * @class Roo.bootstrap.NavItem
6270 * @extends Roo.bootstrap.Component
6271 * Bootstrap Navbar.NavItem class
6272 * @cfg {String} href link to
6273 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6274 * @cfg {Boolean} button_outline show and outlined button
6275 * @cfg {String} html content of button
6276 * @cfg {String} badge text inside badge
6277 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6278 * @cfg {String} glyphicon DEPRICATED - use fa
6279 * @cfg {String} icon DEPRICATED - use fa
6280 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6281 * @cfg {Boolean} active Is item active
6282 * @cfg {Boolean} disabled Is item disabled
6283 * @cfg {String} linkcls Link Class
6284 * @cfg {Boolean} preventDefault (true | false) default false
6285 * @cfg {String} tabId the tab that this item activates.
6286 * @cfg {String} tagtype (a|span) render as a href or span?
6287 * @cfg {Boolean} animateRef (true|false) link to element default false
6290 * Create a new Navbar Item
6291 * @param {Object} config The config object
6293 Roo.bootstrap.NavItem = function(config){
6294 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6299 * The raw click event for the entire grid.
6300 * @param {Roo.EventObject} e
6305 * Fires when the active item active state changes
6306 * @param {Roo.bootstrap.NavItem} this
6307 * @param {boolean} state the new state
6313 * Fires when scroll to element
6314 * @param {Roo.bootstrap.NavItem} this
6315 * @param {Object} options
6316 * @param {Roo.EventObject} e
6324 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6333 preventDefault : false,
6341 button_outline : false,
6345 getAutoCreate : function(){
6352 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6355 cfg.cls += ' active' ;
6357 if (this.disabled) {
6358 cfg.cls += ' disabled';
6362 if (this.button_weight.length) {
6363 cfg.tag = this.href ? 'a' : 'button';
6364 cfg.html = this.html || '';
6365 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6367 cfg.href = this.href;
6370 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6372 cfg.cls += " nav-html";
6375 // menu .. should add dropdown-menu class - so no need for carat..
6377 if (this.badge !== '') {
6379 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6384 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6388 href : this.href || "#",
6389 html: this.html || '',
6393 if (this.tagtype == 'a') {
6394 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6398 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6399 } else if (this.fa) {
6400 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6401 } else if(this.glyphicon) {
6402 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6404 cfg.cn[0].cls += " nav-html";
6408 cfg.cn[0].html += " <span class='caret'></span>";
6412 if (this.badge !== '') {
6413 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421 onRender : function(ct, position)
6423 // Roo.log("Call onRender: " + this.xtype);
6424 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6428 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6429 this.navLink = this.el.select('.nav-link',true).first();
6430 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6435 initEvents: function()
6437 if (typeof (this.menu) != 'undefined') {
6438 this.menu.parentType = this.xtype;
6439 this.menu.triggerEl = this.el;
6440 this.menu = this.addxtype(Roo.apply({}, this.menu));
6443 this.el.on('click', this.onClick, this);
6445 //if(this.tagtype == 'span'){
6446 // this.el.select('span',true).on('click', this.onClick, this);
6449 // at this point parent should be available..
6450 this.parent().register(this);
6453 onClick : function(e)
6455 if (e.getTarget('.dropdown-menu-item')) {
6456 // did you click on a menu itemm.... - then don't trigger onclick..
6461 this.preventDefault ||
6464 Roo.log("NavItem - prevent Default?");
6468 if (this.disabled) {
6472 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6473 if (tg && tg.transition) {
6474 Roo.log("waiting for the transitionend");
6480 //Roo.log("fire event clicked");
6481 if(this.fireEvent('click', this, e) === false){
6485 if(this.tagtype == 'span'){
6489 //Roo.log(this.href);
6490 var ael = this.el.select('a',true).first();
6493 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6494 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6495 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6496 return; // ignore... - it's a 'hash' to another page.
6498 Roo.log("NavItem - prevent Default?");
6500 this.scrollToElement(e);
6504 var p = this.parent();
6506 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6507 if (typeof(p.setActiveItem) !== 'undefined') {
6508 p.setActiveItem(this);
6512 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6513 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6514 // remove the collapsed menu expand...
6515 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6519 isActive: function () {
6522 setActive : function(state, fire, is_was_active)
6524 if (this.active && !state && this.navId) {
6525 this.was_active = true;
6526 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6528 nv.clearWasActive(this);
6532 this.active = state;
6535 this.el.removeClass('active');
6536 this.navLink ? this.navLink.removeClass('active') : false;
6537 } else if (!this.el.hasClass('active')) {
6539 this.el.addClass('active');
6540 if (Roo.bootstrap.version == 4 && this.navLink ) {
6541 this.navLink.addClass('active');
6546 this.fireEvent('changed', this, state);
6549 // show a panel if it's registered and related..
6551 if (!this.navId || !this.tabId || !state || is_was_active) {
6555 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559 var pan = tg.getPanelByName(this.tabId);
6563 // if we can not flip to new panel - go back to old nav highlight..
6564 if (false == tg.showPanel(pan)) {
6565 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6567 var onav = nv.getWasActive();
6569 onav.setActive(true, false, true);
6578 // this should not be here...
6579 setDisabled : function(state)
6581 this.disabled = state;
6583 this.el.removeClass('disabled');
6584 } else if (!this.el.hasClass('disabled')) {
6585 this.el.addClass('disabled');
6591 * Fetch the element to display the tooltip on.
6592 * @return {Roo.Element} defaults to this.el
6594 tooltipEl : function()
6596 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6599 scrollToElement : function(e)
6601 var c = document.body;
6604 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6606 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6607 c = document.documentElement;
6610 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6616 var o = target.calcOffsetsTo(c);
6623 this.fireEvent('scrollto', this, options, e);
6625 Roo.get(c).scrollTo('top', options.value, true);
6630 * Set the HTML (text content) of the item
6631 * @param {string} html content for the nav item
6633 setHtml : function(html)
6636 this.htmlEl.dom.innerHTML = html;
6648 * <span> icon </span>
6649 * <span> text </span>
6650 * <span>badge </span>
6654 * @class Roo.bootstrap.NavSidebarItem
6655 * @extends Roo.bootstrap.NavItem
6656 * Bootstrap Navbar.NavSidebarItem class
6657 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6658 * {Boolean} open is the menu open
6659 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6660 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6661 * {String} buttonSize (sm|md|lg)the extra classes for the button
6662 * {Boolean} showArrow show arrow next to the text (default true)
6664 * Create a new Navbar Button
6665 * @param {Object} config The config object
6667 Roo.bootstrap.NavSidebarItem = function(config){
6668 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6673 * The raw click event for the entire grid.
6674 * @param {Roo.EventObject} e
6679 * Fires when the active item active state changes
6680 * @param {Roo.bootstrap.NavSidebarItem} this
6681 * @param {boolean} state the new state
6689 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6691 badgeWeight : 'default',
6697 buttonWeight : 'default',
6703 getAutoCreate : function(){
6708 href : this.href || '#',
6714 if(this.buttonView){
6717 href : this.href || '#',
6718 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6731 cfg.cls += ' active';
6734 if (this.disabled) {
6735 cfg.cls += ' disabled';
6738 cfg.cls += ' open x-open';
6741 if (this.glyphicon || this.icon) {
6742 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6743 a.cn.push({ tag : 'i', cls : c }) ;
6746 if(!this.buttonView){
6749 html : this.html || ''
6756 if (this.badge !== '') {
6757 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6763 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6766 a.cls += ' dropdown-toggle treeview' ;
6772 initEvents : function()
6774 if (typeof (this.menu) != 'undefined') {
6775 this.menu.parentType = this.xtype;
6776 this.menu.triggerEl = this.el;
6777 this.menu = this.addxtype(Roo.apply({}, this.menu));
6780 this.el.on('click', this.onClick, this);
6782 if(this.badge !== ''){
6783 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6788 onClick : function(e)
6795 if(this.preventDefault){
6799 this.fireEvent('click', this, e);
6802 disable : function()
6804 this.setDisabled(true);
6809 this.setDisabled(false);
6812 setDisabled : function(state)
6814 if(this.disabled == state){
6818 this.disabled = state;
6821 this.el.addClass('disabled');
6825 this.el.removeClass('disabled');
6830 setActive : function(state)
6832 if(this.active == state){
6836 this.active = state;
6839 this.el.addClass('active');
6843 this.el.removeClass('active');
6848 isActive: function ()
6853 setBadge : function(str)
6859 this.badgeEl.dom.innerHTML = str;
6874 Roo.namespace('Roo.bootstrap.breadcrumb');
6878 * @class Roo.bootstrap.breadcrumb.Nav
6879 * @extends Roo.bootstrap.Component
6880 * Bootstrap Breadcrumb Nav Class
6882 * @children Roo.bootstrap.breadcrumb.Item
6885 * Create a new breadcrumb.Nav
6886 * @param {Object} config The config object
6890 Roo.bootstrap.breadcrumb.Nav = function(config){
6891 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6896 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6898 getAutoCreate : function()
6915 initEvents: function()
6917 this.olEl = this.el.select('ol',true).first();
6919 getChildContainer : function()
6935 * @class Roo.bootstrap.breadcrumb.Nav
6936 * @extends Roo.bootstrap.Component
6937 * Bootstrap Breadcrumb Nav Class
6939 * @children Roo.bootstrap.breadcrumb.Component
6940 * @cfg {String} html the content of the link.
6941 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6942 * @cfg {Boolean} active is it active
6946 * Create a new breadcrumb.Nav
6947 * @param {Object} config The config object
6950 Roo.bootstrap.breadcrumb.Item = function(config){
6951 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6956 * The img click event for the img.
6957 * @param {Roo.EventObject} e
6964 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6969 getAutoCreate : function()
6974 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6976 if (this.href !== false) {
6983 cfg.html = this.html;
6989 initEvents: function()
6992 this.el.select('a', true).first().on('click',this.onClick, this)
6996 onClick : function(e)
6999 this.fireEvent('click',this, e);
7012 * @class Roo.bootstrap.Row
7013 * @extends Roo.bootstrap.Component
7014 * Bootstrap Row class (contains columns...)
7018 * @param {Object} config The config object
7021 Roo.bootstrap.Row = function(config){
7022 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7025 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7027 getAutoCreate : function(){
7046 * @class Roo.bootstrap.Pagination
7047 * @extends Roo.bootstrap.Component
7048 * Bootstrap Pagination class
7049 * @cfg {String} size xs | sm | md | lg
7050 * @cfg {Boolean} inverse false | true
7053 * Create a new Pagination
7054 * @param {Object} config The config object
7057 Roo.bootstrap.Pagination = function(config){
7058 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7061 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7067 getAutoCreate : function(){
7073 cfg.cls += ' inverse';
7079 cfg.cls += " " + this.cls;
7097 * @class Roo.bootstrap.PaginationItem
7098 * @extends Roo.bootstrap.Component
7099 * Bootstrap PaginationItem class
7100 * @cfg {String} html text
7101 * @cfg {String} href the link
7102 * @cfg {Boolean} preventDefault (true | false) default true
7103 * @cfg {Boolean} active (true | false) default false
7104 * @cfg {Boolean} disabled default false
7108 * Create a new PaginationItem
7109 * @param {Object} config The config object
7113 Roo.bootstrap.PaginationItem = function(config){
7114 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7119 * The raw click event for the entire grid.
7120 * @param {Roo.EventObject} e
7126 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7130 preventDefault: true,
7135 getAutoCreate : function(){
7141 href : this.href ? this.href : '#',
7142 html : this.html ? this.html : ''
7152 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7156 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7162 initEvents: function() {
7164 this.el.on('click', this.onClick, this);
7167 onClick : function(e)
7169 Roo.log('PaginationItem on click ');
7170 if(this.preventDefault){
7178 this.fireEvent('click', this, e);
7194 * @class Roo.bootstrap.Slider
7195 * @extends Roo.bootstrap.Component
7196 * Bootstrap Slider class
7199 * Create a new Slider
7200 * @param {Object} config The config object
7203 Roo.bootstrap.Slider = function(config){
7204 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7209 getAutoCreate : function(){
7213 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7217 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7229 * Ext JS Library 1.1.1
7230 * Copyright(c) 2006-2007, Ext JS, LLC.
7232 * Originally Released Under LGPL - original licence link has changed is not relivant.
7235 * <script type="text/javascript">
7240 * @class Roo.grid.ColumnModel
7241 * @extends Roo.util.Observable
7242 * This is the default implementation of a ColumnModel used by the Grid. It defines
7243 * the columns in the grid.
7246 var colModel = new Roo.grid.ColumnModel([
7247 {header: "Ticker", width: 60, sortable: true, locked: true},
7248 {header: "Company Name", width: 150, sortable: true},
7249 {header: "Market Cap.", width: 100, sortable: true},
7250 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7251 {header: "Employees", width: 100, sortable: true, resizable: false}
7256 * The config options listed for this class are options which may appear in each
7257 * individual column definition.
7258 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7260 * @param {Object} config An Array of column config objects. See this class's
7261 * config objects for details.
7263 Roo.grid.ColumnModel = function(config){
7265 * The config passed into the constructor
7267 this.config = config;
7270 // if no id, create one
7271 // if the column does not have a dataIndex mapping,
7272 // map it to the order it is in the config
7273 for(var i = 0, len = config.length; i < len; i++){
7275 if(typeof c.dataIndex == "undefined"){
7278 if(typeof c.renderer == "string"){
7279 c.renderer = Roo.util.Format[c.renderer];
7281 if(typeof c.id == "undefined"){
7284 if(c.editor && c.editor.xtype){
7285 c.editor = Roo.factory(c.editor, Roo.grid);
7287 if(c.editor && c.editor.isFormField){
7288 c.editor = new Roo.grid.GridEditor(c.editor);
7290 this.lookup[c.id] = c;
7294 * The width of columns which have no width specified (defaults to 100)
7297 this.defaultWidth = 100;
7300 * Default sortable of columns which have no sortable specified (defaults to false)
7303 this.defaultSortable = false;
7307 * @event widthchange
7308 * Fires when the width of a column changes.
7309 * @param {ColumnModel} this
7310 * @param {Number} columnIndex The column index
7311 * @param {Number} newWidth The new width
7313 "widthchange": true,
7315 * @event headerchange
7316 * Fires when the text of a header changes.
7317 * @param {ColumnModel} this
7318 * @param {Number} columnIndex The column index
7319 * @param {Number} newText The new header text
7321 "headerchange": true,
7323 * @event hiddenchange
7324 * Fires when a column is hidden or "unhidden".
7325 * @param {ColumnModel} this
7326 * @param {Number} columnIndex The column index
7327 * @param {Boolean} hidden true if hidden, false otherwise
7329 "hiddenchange": true,
7331 * @event columnmoved
7332 * Fires when a column is moved.
7333 * @param {ColumnModel} this
7334 * @param {Number} oldIndex
7335 * @param {Number} newIndex
7337 "columnmoved" : true,
7339 * @event columlockchange
7340 * Fires when a column's locked state is changed
7341 * @param {ColumnModel} this
7342 * @param {Number} colIndex
7343 * @param {Boolean} locked true if locked
7345 "columnlockchange" : true
7347 Roo.grid.ColumnModel.superclass.constructor.call(this);
7349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7351 * @cfg {String} header The header text to display in the Grid view.
7354 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7355 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7356 * specified, the column's index is used as an index into the Record's data Array.
7359 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7360 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7363 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7364 * Defaults to the value of the {@link #defaultSortable} property.
7365 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7368 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7371 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7374 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7377 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7380 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7381 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7382 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7383 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7386 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7389 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7392 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7395 * @cfg {String} cursor (Optional)
7398 * @cfg {String} tooltip (Optional)
7401 * @cfg {Number} xs (Optional)
7404 * @cfg {Number} sm (Optional)
7407 * @cfg {Number} md (Optional)
7410 * @cfg {Number} lg (Optional)
7413 * Returns the id of the column at the specified index.
7414 * @param {Number} index The column index
7415 * @return {String} the id
7417 getColumnId : function(index){
7418 return this.config[index].id;
7422 * Returns the column for a specified id.
7423 * @param {String} id The column id
7424 * @return {Object} the column
7426 getColumnById : function(id){
7427 return this.lookup[id];
7432 * Returns the column for a specified dataIndex.
7433 * @param {String} dataIndex The column dataIndex
7434 * @return {Object|Boolean} the column or false if not found
7436 getColumnByDataIndex: function(dataIndex){
7437 var index = this.findColumnIndex(dataIndex);
7438 return index > -1 ? this.config[index] : false;
7442 * Returns the index for a specified column id.
7443 * @param {String} id The column id
7444 * @return {Number} the index, or -1 if not found
7446 getIndexById : function(id){
7447 for(var i = 0, len = this.config.length; i < len; i++){
7448 if(this.config[i].id == id){
7456 * Returns the index for a specified column dataIndex.
7457 * @param {String} dataIndex The column dataIndex
7458 * @return {Number} the index, or -1 if not found
7461 findColumnIndex : function(dataIndex){
7462 for(var i = 0, len = this.config.length; i < len; i++){
7463 if(this.config[i].dataIndex == dataIndex){
7471 moveColumn : function(oldIndex, newIndex){
7472 var c = this.config[oldIndex];
7473 this.config.splice(oldIndex, 1);
7474 this.config.splice(newIndex, 0, c);
7475 this.dataMap = null;
7476 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7479 isLocked : function(colIndex){
7480 return this.config[colIndex].locked === true;
7483 setLocked : function(colIndex, value, suppressEvent){
7484 if(this.isLocked(colIndex) == value){
7487 this.config[colIndex].locked = value;
7489 this.fireEvent("columnlockchange", this, colIndex, value);
7493 getTotalLockedWidth : function(){
7495 for(var i = 0; i < this.config.length; i++){
7496 if(this.isLocked(i) && !this.isHidden(i)){
7497 this.totalWidth += this.getColumnWidth(i);
7503 getLockedCount : function(){
7504 for(var i = 0, len = this.config.length; i < len; i++){
7505 if(!this.isLocked(i)){
7510 return this.config.length;
7514 * Returns the number of columns.
7517 getColumnCount : function(visibleOnly){
7518 if(visibleOnly === true){
7520 for(var i = 0, len = this.config.length; i < len; i++){
7521 if(!this.isHidden(i)){
7527 return this.config.length;
7531 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7532 * @param {Function} fn
7533 * @param {Object} scope (optional)
7534 * @return {Array} result
7536 getColumnsBy : function(fn, scope){
7538 for(var i = 0, len = this.config.length; i < len; i++){
7539 var c = this.config[i];
7540 if(fn.call(scope||this, c, i) === true){
7548 * Returns true if the specified column is sortable.
7549 * @param {Number} col The column index
7552 isSortable : function(col){
7553 if(typeof this.config[col].sortable == "undefined"){
7554 return this.defaultSortable;
7556 return this.config[col].sortable;
7560 * Returns the rendering (formatting) function defined for the column.
7561 * @param {Number} col The column index.
7562 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7564 getRenderer : function(col){
7565 if(!this.config[col].renderer){
7566 return Roo.grid.ColumnModel.defaultRenderer;
7568 return this.config[col].renderer;
7572 * Sets the rendering (formatting) function for a column.
7573 * @param {Number} col The column index
7574 * @param {Function} fn The function to use to process the cell's raw data
7575 * to return HTML markup for the grid view. The render function is called with
7576 * the following parameters:<ul>
7577 * <li>Data value.</li>
7578 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7579 * <li>css A CSS style string to apply to the table cell.</li>
7580 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7581 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7582 * <li>Row index</li>
7583 * <li>Column index</li>
7584 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7586 setRenderer : function(col, fn){
7587 this.config[col].renderer = fn;
7591 * Returns the width for the specified column.
7592 * @param {Number} col The column index
7595 getColumnWidth : function(col){
7596 return this.config[col].width * 1 || this.defaultWidth;
7600 * Sets the width for a column.
7601 * @param {Number} col The column index
7602 * @param {Number} width The new width
7604 setColumnWidth : function(col, width, suppressEvent){
7605 this.config[col].width = width;
7606 this.totalWidth = null;
7608 this.fireEvent("widthchange", this, col, width);
7613 * Returns the total width of all columns.
7614 * @param {Boolean} includeHidden True to include hidden column widths
7617 getTotalWidth : function(includeHidden){
7618 if(!this.totalWidth){
7619 this.totalWidth = 0;
7620 for(var i = 0, len = this.config.length; i < len; i++){
7621 if(includeHidden || !this.isHidden(i)){
7622 this.totalWidth += this.getColumnWidth(i);
7626 return this.totalWidth;
7630 * Returns the header for the specified column.
7631 * @param {Number} col The column index
7634 getColumnHeader : function(col){
7635 return this.config[col].header;
7639 * Sets the header for a column.
7640 * @param {Number} col The column index
7641 * @param {String} header The new header
7643 setColumnHeader : function(col, header){
7644 this.config[col].header = header;
7645 this.fireEvent("headerchange", this, col, header);
7649 * Returns the tooltip for the specified column.
7650 * @param {Number} col The column index
7653 getColumnTooltip : function(col){
7654 return this.config[col].tooltip;
7657 * Sets the tooltip for a column.
7658 * @param {Number} col The column index
7659 * @param {String} tooltip The new tooltip
7661 setColumnTooltip : function(col, tooltip){
7662 this.config[col].tooltip = tooltip;
7666 * Returns the dataIndex for the specified column.
7667 * @param {Number} col The column index
7670 getDataIndex : function(col){
7671 return this.config[col].dataIndex;
7675 * Sets the dataIndex for a column.
7676 * @param {Number} col The column index
7677 * @param {Number} dataIndex The new dataIndex
7679 setDataIndex : function(col, dataIndex){
7680 this.config[col].dataIndex = dataIndex;
7686 * Returns true if the cell is editable.
7687 * @param {Number} colIndex The column index
7688 * @param {Number} rowIndex The row index - this is nto actually used..?
7691 isCellEditable : function(colIndex, rowIndex){
7692 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7696 * Returns the editor defined for the cell/column.
7697 * return false or null to disable editing.
7698 * @param {Number} colIndex The column index
7699 * @param {Number} rowIndex The row index
7702 getCellEditor : function(colIndex, rowIndex){
7703 return this.config[colIndex].editor;
7707 * Sets if a column is editable.
7708 * @param {Number} col The column index
7709 * @param {Boolean} editable True if the column is editable
7711 setEditable : function(col, editable){
7712 this.config[col].editable = editable;
7717 * Returns true if the column is hidden.
7718 * @param {Number} colIndex The column index
7721 isHidden : function(colIndex){
7722 return this.config[colIndex].hidden;
7727 * Returns true if the column width cannot be changed
7729 isFixed : function(colIndex){
7730 return this.config[colIndex].fixed;
7734 * Returns true if the column can be resized
7737 isResizable : function(colIndex){
7738 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7741 * Sets if a column is hidden.
7742 * @param {Number} colIndex The column index
7743 * @param {Boolean} hidden True if the column is hidden
7745 setHidden : function(colIndex, hidden){
7746 this.config[colIndex].hidden = hidden;
7747 this.totalWidth = null;
7748 this.fireEvent("hiddenchange", this, colIndex, hidden);
7752 * Sets the editor for a column.
7753 * @param {Number} col The column index
7754 * @param {Object} editor The editor object
7756 setEditor : function(col, editor){
7757 this.config[col].editor = editor;
7761 Roo.grid.ColumnModel.defaultRenderer = function(value)
7763 if(typeof value == "object") {
7766 if(typeof value == "string" && value.length < 1){
7770 return String.format("{0}", value);
7773 // Alias for backwards compatibility
7774 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7777 * Ext JS Library 1.1.1
7778 * Copyright(c) 2006-2007, Ext JS, LLC.
7780 * Originally Released Under LGPL - original licence link has changed is not relivant.
7783 * <script type="text/javascript">
7787 * @class Roo.LoadMask
7788 * A simple utility class for generically masking elements while loading data. If the element being masked has
7789 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7790 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7791 * element's UpdateManager load indicator and will be destroyed after the initial load.
7793 * Create a new LoadMask
7794 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7795 * @param {Object} config The config object
7797 Roo.LoadMask = function(el, config){
7798 this.el = Roo.get(el);
7799 Roo.apply(this, config);
7801 this.store.on('beforeload', this.onBeforeLoad, this);
7802 this.store.on('load', this.onLoad, this);
7803 this.store.on('loadexception', this.onLoadException, this);
7804 this.removeMask = false;
7806 var um = this.el.getUpdateManager();
7807 um.showLoadIndicator = false; // disable the default indicator
7808 um.on('beforeupdate', this.onBeforeLoad, this);
7809 um.on('update', this.onLoad, this);
7810 um.on('failure', this.onLoad, this);
7811 this.removeMask = true;
7815 Roo.LoadMask.prototype = {
7817 * @cfg {Boolean} removeMask
7818 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7819 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7823 * The text to display in a centered loading message box (defaults to 'Loading...')
7827 * @cfg {String} msgCls
7828 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7830 msgCls : 'x-mask-loading',
7833 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7839 * Disables the mask to prevent it from being displayed
7841 disable : function(){
7842 this.disabled = true;
7846 * Enables the mask so that it can be displayed
7848 enable : function(){
7849 this.disabled = false;
7852 onLoadException : function()
7856 if (typeof(arguments[3]) != 'undefined') {
7857 Roo.MessageBox.alert("Error loading",arguments[3]);
7861 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7862 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7869 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7874 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7878 onBeforeLoad : function(){
7880 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7885 destroy : function(){
7887 this.store.un('beforeload', this.onBeforeLoad, this);
7888 this.store.un('load', this.onLoad, this);
7889 this.store.un('loadexception', this.onLoadException, this);
7891 var um = this.el.getUpdateManager();
7892 um.un('beforeupdate', this.onBeforeLoad, this);
7893 um.un('update', this.onLoad, this);
7894 um.un('failure', this.onLoad, this);
7905 * @class Roo.bootstrap.Table
7906 * @extends Roo.bootstrap.Component
7907 * Bootstrap Table class
7908 * @cfg {String} cls table class
7909 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7910 * @cfg {String} bgcolor Specifies the background color for a table
7911 * @cfg {Number} border Specifies whether the table cells should have borders or not
7912 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7913 * @cfg {Number} cellspacing Specifies the space between cells
7914 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7915 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7916 * @cfg {String} sortable Specifies that the table should be sortable
7917 * @cfg {String} summary Specifies a summary of the content of a table
7918 * @cfg {Number} width Specifies the width of a table
7919 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7921 * @cfg {boolean} striped Should the rows be alternative striped
7922 * @cfg {boolean} bordered Add borders to the table
7923 * @cfg {boolean} hover Add hover highlighting
7924 * @cfg {boolean} condensed Format condensed
7925 * @cfg {boolean} responsive Format condensed
7926 * @cfg {Boolean} loadMask (true|false) default false
7927 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7928 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7929 * @cfg {Boolean} rowSelection (true|false) default false
7930 * @cfg {Boolean} cellSelection (true|false) default false
7931 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7932 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7933 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7934 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7938 * Create a new Table
7939 * @param {Object} config The config object
7942 Roo.bootstrap.Table = function(config){
7943 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7948 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7949 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7950 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7951 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7953 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7955 this.sm.grid = this;
7956 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7957 this.sm = this.selModel;
7958 this.sm.xmodule = this.xmodule || false;
7961 if (this.cm && typeof(this.cm.config) == 'undefined') {
7962 this.colModel = new Roo.grid.ColumnModel(this.cm);
7963 this.cm = this.colModel;
7964 this.cm.xmodule = this.xmodule || false;
7967 this.store= Roo.factory(this.store, Roo.data);
7968 this.ds = this.store;
7969 this.ds.xmodule = this.xmodule || false;
7972 if (this.footer && this.store) {
7973 this.footer.dataSource = this.ds;
7974 this.footer = Roo.factory(this.footer);
7981 * Fires when a cell is clicked
7982 * @param {Roo.bootstrap.Table} this
7983 * @param {Roo.Element} el
7984 * @param {Number} rowIndex
7985 * @param {Number} columnIndex
7986 * @param {Roo.EventObject} e
7990 * @event celldblclick
7991 * Fires when a cell is double clicked
7992 * @param {Roo.bootstrap.Table} this
7993 * @param {Roo.Element} el
7994 * @param {Number} rowIndex
7995 * @param {Number} columnIndex
7996 * @param {Roo.EventObject} e
7998 "celldblclick" : true,
8001 * Fires when a row is clicked
8002 * @param {Roo.bootstrap.Table} this
8003 * @param {Roo.Element} el
8004 * @param {Number} rowIndex
8005 * @param {Roo.EventObject} e
8009 * @event rowdblclick
8010 * Fires when a row is double clicked
8011 * @param {Roo.bootstrap.Table} this
8012 * @param {Roo.Element} el
8013 * @param {Number} rowIndex
8014 * @param {Roo.EventObject} e
8016 "rowdblclick" : true,
8019 * Fires when a mouseover occur
8020 * @param {Roo.bootstrap.Table} this
8021 * @param {Roo.Element} el
8022 * @param {Number} rowIndex
8023 * @param {Number} columnIndex
8024 * @param {Roo.EventObject} e
8029 * Fires when a mouseout occur
8030 * @param {Roo.bootstrap.Table} this
8031 * @param {Roo.Element} el
8032 * @param {Number} rowIndex
8033 * @param {Number} columnIndex
8034 * @param {Roo.EventObject} e
8039 * Fires when a row is rendered, so you can change add a style to it.
8040 * @param {Roo.bootstrap.Table} this
8041 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8045 * @event rowsrendered
8046 * Fires when all the rows have been rendered
8047 * @param {Roo.bootstrap.Table} this
8049 'rowsrendered' : true,
8051 * @event contextmenu
8052 * The raw contextmenu event for the entire grid.
8053 * @param {Roo.EventObject} e
8055 "contextmenu" : true,
8057 * @event rowcontextmenu
8058 * Fires when a row is right clicked
8059 * @param {Roo.bootstrap.Table} this
8060 * @param {Number} rowIndex
8061 * @param {Roo.EventObject} e
8063 "rowcontextmenu" : true,
8065 * @event cellcontextmenu
8066 * Fires when a cell is right clicked
8067 * @param {Roo.bootstrap.Table} this
8068 * @param {Number} rowIndex
8069 * @param {Number} cellIndex
8070 * @param {Roo.EventObject} e
8072 "cellcontextmenu" : true,
8074 * @event headercontextmenu
8075 * Fires when a header is right clicked
8076 * @param {Roo.bootstrap.Table} this
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8080 "headercontextmenu" : true
8084 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8110 rowSelection : false,
8111 cellSelection : false,
8114 // Roo.Element - the tbody
8116 // Roo.Element - thead element
8119 container: false, // used by gridpanel...
8125 auto_hide_footer : false,
8127 getAutoCreate : function()
8129 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8136 if (this.scrollBody) {
8137 cfg.cls += ' table-body-fixed';
8140 cfg.cls += ' table-striped';
8144 cfg.cls += ' table-hover';
8146 if (this.bordered) {
8147 cfg.cls += ' table-bordered';
8149 if (this.condensed) {
8150 cfg.cls += ' table-condensed';
8152 if (this.responsive) {
8153 cfg.cls += ' table-responsive';
8157 cfg.cls+= ' ' +this.cls;
8160 // this lot should be simplifed...
8173 ].forEach(function(k) {
8181 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8184 if(this.store || this.cm){
8185 if(this.headerShow){
8186 cfg.cn.push(this.renderHeader());
8189 cfg.cn.push(this.renderBody());
8191 if(this.footerShow){
8192 cfg.cn.push(this.renderFooter());
8194 // where does this come from?
8195 //cfg.cls+= ' TableGrid';
8198 return { cn : [ cfg ] };
8201 initEvents : function()
8203 if(!this.store || !this.cm){
8206 if (this.selModel) {
8207 this.selModel.initEvents();
8211 //Roo.log('initEvents with ds!!!!');
8213 this.mainBody = this.el.select('tbody', true).first();
8214 this.mainHead = this.el.select('thead', true).first();
8215 this.mainFoot = this.el.select('tfoot', true).first();
8221 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222 e.on('click', _this.sort, _this);
8225 this.mainBody.on("click", this.onClick, this);
8226 this.mainBody.on("dblclick", this.onDblClick, this);
8228 // why is this done????? = it breaks dialogs??
8229 //this.parent().el.setStyle('position', 'relative');
8233 this.footer.parentId = this.id;
8234 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8237 this.el.select('tfoot tr td').first().addClass('hide');
8242 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8245 this.store.on('load', this.onLoad, this);
8246 this.store.on('beforeload', this.onBeforeLoad, this);
8247 this.store.on('update', this.onUpdate, this);
8248 this.store.on('add', this.onAdd, this);
8249 this.store.on("clear", this.clear, this);
8251 this.el.on("contextmenu", this.onContextMenu, this);
8253 this.mainBody.on('scroll', this.onBodyScroll, this);
8255 this.cm.on("headerchange", this.onHeaderChange, this);
8257 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8261 onContextMenu : function(e, t)
8263 this.processEvent("contextmenu", e);
8266 processEvent : function(name, e)
8268 if (name != 'touchstart' ) {
8269 this.fireEvent(name, e);
8272 var t = e.getTarget();
8274 var cell = Roo.get(t);
8280 if(cell.findParent('tfoot', false, true)){
8284 if(cell.findParent('thead', false, true)){
8286 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8287 cell = Roo.get(t).findParent('th', false, true);
8289 Roo.log("failed to find th in thead?");
8290 Roo.log(e.getTarget());
8295 var cellIndex = cell.dom.cellIndex;
8297 var ename = name == 'touchstart' ? 'click' : name;
8298 this.fireEvent("header" + ename, this, cellIndex, e);
8303 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8304 cell = Roo.get(t).findParent('td', false, true);
8306 Roo.log("failed to find th in tbody?");
8307 Roo.log(e.getTarget());
8312 var row = cell.findParent('tr', false, true);
8313 var cellIndex = cell.dom.cellIndex;
8314 var rowIndex = row.dom.rowIndex - 1;
8318 this.fireEvent("row" + name, this, rowIndex, e);
8322 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8328 onMouseover : function(e, el)
8330 var cell = Roo.get(el);
8336 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8337 cell = cell.findParent('td', false, true);
8340 var row = cell.findParent('tr', false, true);
8341 var cellIndex = cell.dom.cellIndex;
8342 var rowIndex = row.dom.rowIndex - 1; // start from 0
8344 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8348 onMouseout : function(e, el)
8350 var cell = Roo.get(el);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = cell.findParent('td', false, true);
8360 var row = cell.findParent('tr', false, true);
8361 var cellIndex = cell.dom.cellIndex;
8362 var rowIndex = row.dom.rowIndex - 1; // start from 0
8364 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8368 onClick : function(e, el)
8370 var cell = Roo.get(el);
8372 if(!cell || (!this.cellSelection && !this.rowSelection)){
8376 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8377 cell = cell.findParent('td', false, true);
8380 if(!cell || typeof(cell) == 'undefined'){
8384 var row = cell.findParent('tr', false, true);
8386 if(!row || typeof(row) == 'undefined'){
8390 var cellIndex = cell.dom.cellIndex;
8391 var rowIndex = this.getRowIndex(row);
8393 // why??? - should these not be based on SelectionModel?
8394 if(this.cellSelection){
8395 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8398 if(this.rowSelection){
8399 this.fireEvent('rowclick', this, row, rowIndex, e);
8405 onDblClick : function(e,el)
8407 var cell = Roo.get(el);
8409 if(!cell || (!this.cellSelection && !this.rowSelection)){
8413 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8414 cell = cell.findParent('td', false, true);
8417 if(!cell || typeof(cell) == 'undefined'){
8421 var row = cell.findParent('tr', false, true);
8423 if(!row || typeof(row) == 'undefined'){
8427 var cellIndex = cell.dom.cellIndex;
8428 var rowIndex = this.getRowIndex(row);
8430 if(this.cellSelection){
8431 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8434 if(this.rowSelection){
8435 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8439 sort : function(e,el)
8441 var col = Roo.get(el);
8443 if(!col.hasClass('sortable')){
8447 var sort = col.attr('sort');
8450 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8454 this.store.sortInfo = {field : sort, direction : dir};
8457 Roo.log("calling footer first");
8458 this.footer.onClick('first');
8461 this.store.load({ params : { start : 0 } });
8465 renderHeader : function()
8473 this.totalWidth = 0;
8475 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8477 var config = cm.config[i];
8481 cls : 'x-hcol-' + i,
8483 html: cm.getColumnHeader(i)
8488 if(typeof(config.sortable) != 'undefined' && config.sortable){
8490 c.html = '<i class="glyphicon"></i>' + c.html;
8493 // could use BS4 hidden-..-down
8495 if(typeof(config.lgHeader) != 'undefined'){
8496 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8499 if(typeof(config.mdHeader) != 'undefined'){
8500 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8503 if(typeof(config.smHeader) != 'undefined'){
8504 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8507 if(typeof(config.xsHeader) != 'undefined'){
8508 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8515 if(typeof(config.tooltip) != 'undefined'){
8516 c.tooltip = config.tooltip;
8519 if(typeof(config.colspan) != 'undefined'){
8520 c.colspan = config.colspan;
8523 if(typeof(config.hidden) != 'undefined' && config.hidden){
8524 c.style += ' display:none;';
8527 if(typeof(config.dataIndex) != 'undefined'){
8528 c.sort = config.dataIndex;
8533 if(typeof(config.align) != 'undefined' && config.align.length){
8534 c.style += ' text-align:' + config.align + ';';
8537 if(typeof(config.width) != 'undefined'){
8538 c.style += ' width:' + config.width + 'px;';
8539 this.totalWidth += config.width;
8541 this.totalWidth += 100; // assume minimum of 100 per column?
8544 if(typeof(config.cls) != 'undefined'){
8545 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8548 ['xs','sm','md','lg'].map(function(size){
8550 if(typeof(config[size]) == 'undefined'){
8554 if (!config[size]) { // 0 = hidden
8555 // BS 4 '0' is treated as hide that column and below.
8556 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8560 c.cls += ' col-' + size + '-' + config[size] + (
8561 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8573 renderBody : function()
8583 colspan : this.cm.getColumnCount()
8593 renderFooter : function()
8603 colspan : this.cm.getColumnCount()
8617 // Roo.log('ds onload');
8622 var ds = this.store;
8624 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8625 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8626 if (_this.store.sortInfo) {
8628 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8629 e.select('i', true).addClass(['glyphicon-arrow-up']);
8632 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8633 e.select('i', true).addClass(['glyphicon-arrow-down']);
8638 var tbody = this.mainBody;
8640 if(ds.getCount() > 0){
8641 ds.data.each(function(d,rowIndex){
8642 var row = this.renderRow(cm, ds, rowIndex);
8644 tbody.createChild(row);
8648 if(row.cellObjects.length){
8649 Roo.each(row.cellObjects, function(r){
8650 _this.renderCellObject(r);
8657 var tfoot = this.el.select('tfoot', true).first();
8659 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8661 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8663 var total = this.ds.getTotalCount();
8665 if(this.footer.pageSize < total){
8666 this.mainFoot.show();
8670 Roo.each(this.el.select('tbody td', true).elements, function(e){
8671 e.on('mouseover', _this.onMouseover, _this);
8674 Roo.each(this.el.select('tbody td', true).elements, function(e){
8675 e.on('mouseout', _this.onMouseout, _this);
8677 this.fireEvent('rowsrendered', this);
8683 onUpdate : function(ds,record)
8685 this.refreshRow(record);
8689 onRemove : function(ds, record, index, isUpdate){
8690 if(isUpdate !== true){
8691 this.fireEvent("beforerowremoved", this, index, record);
8693 var bt = this.mainBody.dom;
8695 var rows = this.el.select('tbody > tr', true).elements;
8697 if(typeof(rows[index]) != 'undefined'){
8698 bt.removeChild(rows[index].dom);
8701 // if(bt.rows[index]){
8702 // bt.removeChild(bt.rows[index]);
8705 if(isUpdate !== true){
8706 //this.stripeRows(index);
8707 //this.syncRowHeights(index, index);
8709 this.fireEvent("rowremoved", this, index, record);
8713 onAdd : function(ds, records, rowIndex)
8715 //Roo.log('on Add called');
8716 // - note this does not handle multiple adding very well..
8717 var bt = this.mainBody.dom;
8718 for (var i =0 ; i < records.length;i++) {
8719 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8720 //Roo.log(records[i]);
8721 //Roo.log(this.store.getAt(rowIndex+i));
8722 this.insertRow(this.store, rowIndex + i, false);
8729 refreshRow : function(record){
8730 var ds = this.store, index;
8731 if(typeof record == 'number'){
8733 record = ds.getAt(index);
8735 index = ds.indexOf(record);
8737 return; // should not happen - but seems to
8740 this.insertRow(ds, index, true);
8742 this.onRemove(ds, record, index+1, true);
8744 //this.syncRowHeights(index, index);
8746 this.fireEvent("rowupdated", this, index, record);
8749 insertRow : function(dm, rowIndex, isUpdate){
8752 this.fireEvent("beforerowsinserted", this, rowIndex);
8754 //var s = this.getScrollState();
8755 var row = this.renderRow(this.cm, this.store, rowIndex);
8756 // insert before rowIndex..
8757 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8761 if(row.cellObjects.length){
8762 Roo.each(row.cellObjects, function(r){
8763 _this.renderCellObject(r);
8768 this.fireEvent("rowsinserted", this, rowIndex);
8769 //this.syncRowHeights(firstRow, lastRow);
8770 //this.stripeRows(firstRow);
8777 getRowDom : function(rowIndex)
8779 var rows = this.el.select('tbody > tr', true).elements;
8781 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8784 // returns the object tree for a tr..
8787 renderRow : function(cm, ds, rowIndex)
8789 var d = ds.getAt(rowIndex);
8793 cls : 'x-row-' + rowIndex,
8797 var cellObjects = [];
8799 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8800 var config = cm.config[i];
8802 var renderer = cm.getRenderer(i);
8806 if(typeof(renderer) !== 'undefined'){
8807 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8809 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8810 // and are rendered into the cells after the row is rendered - using the id for the element.
8812 if(typeof(value) === 'object'){
8822 rowIndex : rowIndex,
8827 this.fireEvent('rowclass', this, rowcfg);
8831 cls : rowcfg.rowClass + ' x-col-' + i,
8833 html: (typeof(value) === 'object') ? '' : value
8840 if(typeof(config.colspan) != 'undefined'){
8841 td.colspan = config.colspan;
8844 if(typeof(config.hidden) != 'undefined' && config.hidden){
8845 td.style += ' display:none;';
8848 if(typeof(config.align) != 'undefined' && config.align.length){
8849 td.style += ' text-align:' + config.align + ';';
8851 if(typeof(config.valign) != 'undefined' && config.valign.length){
8852 td.style += ' vertical-align:' + config.valign + ';';
8855 if(typeof(config.width) != 'undefined'){
8856 td.style += ' width:' + config.width + 'px;';
8859 if(typeof(config.cursor) != 'undefined'){
8860 td.style += ' cursor:' + config.cursor + ';';
8863 if(typeof(config.cls) != 'undefined'){
8864 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8867 ['xs','sm','md','lg'].map(function(size){
8869 if(typeof(config[size]) == 'undefined'){
8875 if (!config[size]) { // 0 = hidden
8876 // BS 4 '0' is treated as hide that column and below.
8877 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8881 td.cls += ' col-' + size + '-' + config[size] + (
8882 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8892 row.cellObjects = cellObjects;
8900 onBeforeLoad : function()
8909 this.el.select('tbody', true).first().dom.innerHTML = '';
8912 * Show or hide a row.
8913 * @param {Number} rowIndex to show or hide
8914 * @param {Boolean} state hide
8916 setRowVisibility : function(rowIndex, state)
8918 var bt = this.mainBody.dom;
8920 var rows = this.el.select('tbody > tr', true).elements;
8922 if(typeof(rows[rowIndex]) == 'undefined'){
8925 rows[rowIndex].dom.style.display = state ? '' : 'none';
8929 getSelectionModel : function(){
8931 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8933 return this.selModel;
8936 * Render the Roo.bootstrap object from renderder
8938 renderCellObject : function(r)
8942 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8944 var t = r.cfg.render(r.container);
8947 Roo.each(r.cfg.cn, function(c){
8949 container: t.getChildContainer(),
8952 _this.renderCellObject(child);
8957 getRowIndex : function(row)
8961 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8972 * Returns the grid's underlying element = used by panel.Grid
8973 * @return {Element} The element
8975 getGridEl : function(){
8979 * Forces a resize - used by panel.Grid
8980 * @return {Element} The element
8982 autoSize : function()
8984 //var ctr = Roo.get(this.container.dom.parentElement);
8985 var ctr = Roo.get(this.el.dom);
8987 var thd = this.getGridEl().select('thead',true).first();
8988 var tbd = this.getGridEl().select('tbody', true).first();
8989 var tfd = this.getGridEl().select('tfoot', true).first();
8991 var cw = ctr.getWidth();
8992 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8996 tbd.setWidth(ctr.getWidth());
8997 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8998 // this needs fixing for various usage - currently only hydra job advers I think..
9000 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9002 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9005 cw = Math.max(cw, this.totalWidth);
9006 this.getGridEl().select('tbody tr',true).setWidth(cw);
9008 // resize 'expandable coloumn?
9010 return; // we doe not have a view in this design..
9013 onBodyScroll: function()
9015 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9017 this.mainHead.setStyle({
9018 'position' : 'relative',
9019 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9025 var scrollHeight = this.mainBody.dom.scrollHeight;
9027 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9029 var height = this.mainBody.getHeight();
9031 if(scrollHeight - height == scrollTop) {
9033 var total = this.ds.getTotalCount();
9035 if(this.footer.cursor + this.footer.pageSize < total){
9037 this.footer.ds.load({
9039 start : this.footer.cursor + this.footer.pageSize,
9040 limit : this.footer.pageSize
9050 onHeaderChange : function()
9052 var header = this.renderHeader();
9053 var table = this.el.select('table', true).first();
9055 this.mainHead.remove();
9056 this.mainHead = table.createChild(header, this.mainBody, false);
9059 onHiddenChange : function(colModel, colIndex, hidden)
9061 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9062 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9064 this.CSS.updateRule(thSelector, "display", "");
9065 this.CSS.updateRule(tdSelector, "display", "");
9068 this.CSS.updateRule(thSelector, "display", "none");
9069 this.CSS.updateRule(tdSelector, "display", "none");
9072 this.onHeaderChange();
9076 setColumnWidth: function(col_index, width)
9078 // width = "md-2 xs-2..."
9079 if(!this.colModel.config[col_index]) {
9083 var w = width.split(" ");
9085 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9087 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9090 for(var j = 0; j < w.length; j++) {
9096 var size_cls = w[j].split("-");
9098 if(!Number.isInteger(size_cls[1] * 1)) {
9102 if(!this.colModel.config[col_index][size_cls[0]]) {
9106 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9110 h_row[0].classList.replace(
9111 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9112 "col-"+size_cls[0]+"-"+size_cls[1]
9115 for(var i = 0; i < rows.length; i++) {
9117 var size_cls = w[j].split("-");
9119 if(!Number.isInteger(size_cls[1] * 1)) {
9123 if(!this.colModel.config[col_index][size_cls[0]]) {
9127 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9131 rows[i].classList.replace(
9132 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9133 "col-"+size_cls[0]+"-"+size_cls[1]
9137 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9152 * @class Roo.bootstrap.TableCell
9153 * @extends Roo.bootstrap.Component
9154 * Bootstrap TableCell class
9155 * @cfg {String} html cell contain text
9156 * @cfg {String} cls cell class
9157 * @cfg {String} tag cell tag (td|th) default td
9158 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9159 * @cfg {String} align Aligns the content in a cell
9160 * @cfg {String} axis Categorizes cells
9161 * @cfg {String} bgcolor Specifies the background color of a cell
9162 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9163 * @cfg {Number} colspan Specifies the number of columns a cell should span
9164 * @cfg {String} headers Specifies one or more header cells a cell is related to
9165 * @cfg {Number} height Sets the height of a cell
9166 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9167 * @cfg {Number} rowspan Sets the number of rows a cell should span
9168 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9169 * @cfg {String} valign Vertical aligns the content in a cell
9170 * @cfg {Number} width Specifies the width of a cell
9173 * Create a new TableCell
9174 * @param {Object} config The config object
9177 Roo.bootstrap.TableCell = function(config){
9178 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9181 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9201 getAutoCreate : function(){
9202 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9222 cfg.align=this.align
9228 cfg.bgcolor=this.bgcolor
9231 cfg.charoff=this.charoff
9234 cfg.colspan=this.colspan
9237 cfg.headers=this.headers
9240 cfg.height=this.height
9243 cfg.nowrap=this.nowrap
9246 cfg.rowspan=this.rowspan
9249 cfg.scope=this.scope
9252 cfg.valign=this.valign
9255 cfg.width=this.width
9274 * @class Roo.bootstrap.TableRow
9275 * @extends Roo.bootstrap.Component
9276 * Bootstrap TableRow class
9277 * @cfg {String} cls row class
9278 * @cfg {String} align Aligns the content in a table row
9279 * @cfg {String} bgcolor Specifies a background color for a table row
9280 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9281 * @cfg {String} valign Vertical aligns the content in a table row
9284 * Create a new TableRow
9285 * @param {Object} config The config object
9288 Roo.bootstrap.TableRow = function(config){
9289 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9292 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9300 getAutoCreate : function(){
9301 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9311 cfg.align = this.align;
9314 cfg.bgcolor = this.bgcolor;
9317 cfg.charoff = this.charoff;
9320 cfg.valign = this.valign;
9338 * @class Roo.bootstrap.TableBody
9339 * @extends Roo.bootstrap.Component
9340 * Bootstrap TableBody class
9341 * @cfg {String} cls element class
9342 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9343 * @cfg {String} align Aligns the content inside the element
9344 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9345 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9348 * Create a new TableBody
9349 * @param {Object} config The config object
9352 Roo.bootstrap.TableBody = function(config){
9353 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9356 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9364 getAutoCreate : function(){
9365 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9379 cfg.align = this.align;
9382 cfg.charoff = this.charoff;
9385 cfg.valign = this.valign;
9392 // initEvents : function()
9399 // this.store = Roo.factory(this.store, Roo.data);
9400 // this.store.on('load', this.onLoad, this);
9402 // this.store.load();
9406 // onLoad: function ()
9408 // this.fireEvent('load', this);
9418 * Ext JS Library 1.1.1
9419 * Copyright(c) 2006-2007, Ext JS, LLC.
9421 * Originally Released Under LGPL - original licence link has changed is not relivant.
9424 * <script type="text/javascript">
9427 // as we use this in bootstrap.
9428 Roo.namespace('Roo.form');
9430 * @class Roo.form.Action
9431 * Internal Class used to handle form actions
9433 * @param {Roo.form.BasicForm} el The form element or its id
9434 * @param {Object} config Configuration options
9439 // define the action interface
9440 Roo.form.Action = function(form, options){
9442 this.options = options || {};
9445 * Client Validation Failed
9448 Roo.form.Action.CLIENT_INVALID = 'client';
9450 * Server Validation Failed
9453 Roo.form.Action.SERVER_INVALID = 'server';
9455 * Connect to Server Failed
9458 Roo.form.Action.CONNECT_FAILURE = 'connect';
9460 * Reading Data from Server Failed
9463 Roo.form.Action.LOAD_FAILURE = 'load';
9465 Roo.form.Action.prototype = {
9467 failureType : undefined,
9468 response : undefined,
9472 run : function(options){
9477 success : function(response){
9482 handleResponse : function(response){
9486 // default connection failure
9487 failure : function(response){
9489 this.response = response;
9490 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9491 this.form.afterAction(this, false);
9494 processResponse : function(response){
9495 this.response = response;
9496 if(!response.responseText){
9499 this.result = this.handleResponse(response);
9503 // utility functions used internally
9504 getUrl : function(appendParams){
9505 var url = this.options.url || this.form.url || this.form.el.dom.action;
9507 var p = this.getParams();
9509 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9515 getMethod : function(){
9516 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9519 getParams : function(){
9520 var bp = this.form.baseParams;
9521 var p = this.options.params;
9523 if(typeof p == "object"){
9524 p = Roo.urlEncode(Roo.applyIf(p, bp));
9525 }else if(typeof p == 'string' && bp){
9526 p += '&' + Roo.urlEncode(bp);
9529 p = Roo.urlEncode(bp);
9534 createCallback : function(){
9536 success: this.success,
9537 failure: this.failure,
9539 timeout: (this.form.timeout*1000),
9540 upload: this.form.fileUpload ? this.success : undefined
9545 Roo.form.Action.Submit = function(form, options){
9546 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9549 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9552 haveProgress : false,
9553 uploadComplete : false,
9555 // uploadProgress indicator.
9556 uploadProgress : function()
9558 if (!this.form.progressUrl) {
9562 if (!this.haveProgress) {
9563 Roo.MessageBox.progress("Uploading", "Uploading");
9565 if (this.uploadComplete) {
9566 Roo.MessageBox.hide();
9570 this.haveProgress = true;
9572 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9574 var c = new Roo.data.Connection();
9576 url : this.form.progressUrl,
9581 success : function(req){
9582 //console.log(data);
9586 rdata = Roo.decode(req.responseText)
9588 Roo.log("Invalid data from server..");
9592 if (!rdata || !rdata.success) {
9594 Roo.MessageBox.alert(Roo.encode(rdata));
9597 var data = rdata.data;
9599 if (this.uploadComplete) {
9600 Roo.MessageBox.hide();
9605 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9606 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9609 this.uploadProgress.defer(2000,this);
9612 failure: function(data) {
9613 Roo.log('progress url failed ');
9624 // run get Values on the form, so it syncs any secondary forms.
9625 this.form.getValues();
9627 var o = this.options;
9628 var method = this.getMethod();
9629 var isPost = method == 'POST';
9630 if(o.clientValidation === false || this.form.isValid()){
9632 if (this.form.progressUrl) {
9633 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9634 (new Date() * 1) + '' + Math.random());
9639 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9640 form:this.form.el.dom,
9641 url:this.getUrl(!isPost),
9643 params:isPost ? this.getParams() : null,
9644 isUpload: this.form.fileUpload,
9645 formData : this.form.formData
9648 this.uploadProgress();
9650 }else if (o.clientValidation !== false){ // client validation failed
9651 this.failureType = Roo.form.Action.CLIENT_INVALID;
9652 this.form.afterAction(this, false);
9656 success : function(response)
9658 this.uploadComplete= true;
9659 if (this.haveProgress) {
9660 Roo.MessageBox.hide();
9664 var result = this.processResponse(response);
9665 if(result === true || result.success){
9666 this.form.afterAction(this, true);
9670 this.form.markInvalid(result.errors);
9671 this.failureType = Roo.form.Action.SERVER_INVALID;
9673 this.form.afterAction(this, false);
9675 failure : function(response)
9677 this.uploadComplete= true;
9678 if (this.haveProgress) {
9679 Roo.MessageBox.hide();
9682 this.response = response;
9683 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9684 this.form.afterAction(this, false);
9687 handleResponse : function(response){
9688 if(this.form.errorReader){
9689 var rs = this.form.errorReader.read(response);
9692 for(var i = 0, len = rs.records.length; i < len; i++) {
9693 var r = rs.records[i];
9697 if(errors.length < 1){
9701 success : rs.success,
9707 ret = Roo.decode(response.responseText);
9711 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9721 Roo.form.Action.Load = function(form, options){
9722 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9723 this.reader = this.form.reader;
9726 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9731 Roo.Ajax.request(Roo.apply(
9732 this.createCallback(), {
9733 method:this.getMethod(),
9734 url:this.getUrl(false),
9735 params:this.getParams()
9739 success : function(response){
9741 var result = this.processResponse(response);
9742 if(result === true || !result.success || !result.data){
9743 this.failureType = Roo.form.Action.LOAD_FAILURE;
9744 this.form.afterAction(this, false);
9747 this.form.clearInvalid();
9748 this.form.setValues(result.data);
9749 this.form.afterAction(this, true);
9752 handleResponse : function(response){
9753 if(this.form.reader){
9754 var rs = this.form.reader.read(response);
9755 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9757 success : rs.success,
9761 return Roo.decode(response.responseText);
9765 Roo.form.Action.ACTION_TYPES = {
9766 'load' : Roo.form.Action.Load,
9767 'submit' : Roo.form.Action.Submit
9776 * @class Roo.bootstrap.Form
9777 * @extends Roo.bootstrap.Component
9778 * Bootstrap Form class
9779 * @cfg {String} method GET | POST (default POST)
9780 * @cfg {String} labelAlign top | left (default top)
9781 * @cfg {String} align left | right - for navbars
9782 * @cfg {Boolean} loadMask load mask when submit (default true)
9787 * @param {Object} config The config object
9791 Roo.bootstrap.Form = function(config){
9793 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9795 Roo.bootstrap.Form.popover.apply();
9799 * @event clientvalidation
9800 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9801 * @param {Form} this
9802 * @param {Boolean} valid true if the form has passed client-side validation
9804 clientvalidation: true,
9806 * @event beforeaction
9807 * Fires before any action is performed. Return false to cancel the action.
9808 * @param {Form} this
9809 * @param {Action} action The action to be performed
9813 * @event actionfailed
9814 * Fires when an action fails.
9815 * @param {Form} this
9816 * @param {Action} action The action that failed
9818 actionfailed : true,
9820 * @event actioncomplete
9821 * Fires when an action is completed.
9822 * @param {Form} this
9823 * @param {Action} action The action that completed
9825 actioncomplete : true
9829 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9832 * @cfg {String} method
9833 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9838 * The URL to use for form actions if one isn't supplied in the action options.
9841 * @cfg {Boolean} fileUpload
9842 * Set to true if this form is a file upload.
9846 * @cfg {Object} baseParams
9847 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9851 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9855 * @cfg {Sting} align (left|right) for navbar forms
9860 activeAction : null,
9863 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9864 * element by passing it or its id or mask the form itself by passing in true.
9867 waitMsgTarget : false,
9872 * @cfg {Boolean} errorMask (true|false) default false
9877 * @cfg {Number} maskOffset Default 100
9882 * @cfg {Boolean} maskBody
9886 getAutoCreate : function(){
9890 method : this.method || 'POST',
9891 id : this.id || Roo.id(),
9894 if (this.parent().xtype.match(/^Nav/)) {
9895 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9899 if (this.labelAlign == 'left' ) {
9900 cfg.cls += ' form-horizontal';
9906 initEvents : function()
9908 this.el.on('submit', this.onSubmit, this);
9909 // this was added as random key presses on the form where triggering form submit.
9910 this.el.on('keypress', function(e) {
9911 if (e.getCharCode() != 13) {
9914 // we might need to allow it for textareas.. and some other items.
9915 // check e.getTarget().
9917 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9921 Roo.log("keypress blocked");
9929 onSubmit : function(e){
9934 * Returns true if client-side validation on the form is successful.
9937 isValid : function(){
9938 var items = this.getItems();
9942 items.each(function(f){
9948 Roo.log('invalid field: ' + f.name);
9952 if(!target && f.el.isVisible(true)){
9958 if(this.errorMask && !valid){
9959 Roo.bootstrap.Form.popover.mask(this, target);
9966 * Returns true if any fields in this form have changed since their original load.
9969 isDirty : function(){
9971 var items = this.getItems();
9972 items.each(function(f){
9982 * Performs a predefined action (submit or load) or custom actions you define on this form.
9983 * @param {String} actionName The name of the action type
9984 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9985 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9986 * accept other config options):
9988 Property Type Description
9989 ---------------- --------------- ----------------------------------------------------------------------------------
9990 url String The url for the action (defaults to the form's url)
9991 method String The form method to use (defaults to the form's method, or POST if not defined)
9992 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9993 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9994 validate the form on the client (defaults to false)
9996 * @return {BasicForm} this
9998 doAction : function(action, options){
9999 if(typeof action == 'string'){
10000 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10002 if(this.fireEvent('beforeaction', this, action) !== false){
10003 this.beforeAction(action);
10004 action.run.defer(100, action);
10010 beforeAction : function(action){
10011 var o = action.options;
10016 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10018 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10021 // not really supported yet.. ??
10023 //if(this.waitMsgTarget === true){
10024 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10025 //}else if(this.waitMsgTarget){
10026 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10027 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10029 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10035 afterAction : function(action, success){
10036 this.activeAction = null;
10037 var o = action.options;
10042 Roo.get(document.body).unmask();
10048 //if(this.waitMsgTarget === true){
10049 // this.el.unmask();
10050 //}else if(this.waitMsgTarget){
10051 // this.waitMsgTarget.unmask();
10053 // Roo.MessageBox.updateProgress(1);
10054 // Roo.MessageBox.hide();
10061 Roo.callback(o.success, o.scope, [this, action]);
10062 this.fireEvent('actioncomplete', this, action);
10066 // failure condition..
10067 // we have a scenario where updates need confirming.
10068 // eg. if a locking scenario exists..
10069 // we look for { errors : { needs_confirm : true }} in the response.
10071 (typeof(action.result) != 'undefined') &&
10072 (typeof(action.result.errors) != 'undefined') &&
10073 (typeof(action.result.errors.needs_confirm) != 'undefined')
10076 Roo.log("not supported yet");
10079 Roo.MessageBox.confirm(
10080 "Change requires confirmation",
10081 action.result.errorMsg,
10086 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10096 Roo.callback(o.failure, o.scope, [this, action]);
10097 // show an error message if no failed handler is set..
10098 if (!this.hasListener('actionfailed')) {
10099 Roo.log("need to add dialog support");
10101 Roo.MessageBox.alert("Error",
10102 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10103 action.result.errorMsg :
10104 "Saving Failed, please check your entries or try again"
10109 this.fireEvent('actionfailed', this, action);
10114 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10115 * @param {String} id The value to search for
10118 findField : function(id){
10119 var items = this.getItems();
10120 var field = items.get(id);
10122 items.each(function(f){
10123 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10130 return field || null;
10133 * Mark fields in this form invalid in bulk.
10134 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10135 * @return {BasicForm} this
10137 markInvalid : function(errors){
10138 if(errors instanceof Array){
10139 for(var i = 0, len = errors.length; i < len; i++){
10140 var fieldError = errors[i];
10141 var f = this.findField(fieldError.id);
10143 f.markInvalid(fieldError.msg);
10149 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10150 field.markInvalid(errors[id]);
10154 //Roo.each(this.childForms || [], function (f) {
10155 // f.markInvalid(errors);
10162 * Set values for fields in this form in bulk.
10163 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10164 * @return {BasicForm} this
10166 setValues : function(values){
10167 if(values instanceof Array){ // array of objects
10168 for(var i = 0, len = values.length; i < len; i++){
10170 var f = this.findField(v.id);
10172 f.setValue(v.value);
10173 if(this.trackResetOnLoad){
10174 f.originalValue = f.getValue();
10178 }else{ // object hash
10181 if(typeof values[id] != 'function' && (field = this.findField(id))){
10183 if (field.setFromData &&
10184 field.valueField &&
10185 field.displayField &&
10186 // combos' with local stores can
10187 // be queried via setValue()
10188 // to set their value..
10189 (field.store && !field.store.isLocal)
10193 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10194 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10195 field.setFromData(sd);
10197 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10199 field.setFromData(values);
10202 field.setValue(values[id]);
10206 if(this.trackResetOnLoad){
10207 field.originalValue = field.getValue();
10213 //Roo.each(this.childForms || [], function (f) {
10214 // f.setValues(values);
10221 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10222 * they are returned as an array.
10223 * @param {Boolean} asString
10226 getValues : function(asString){
10227 //if (this.childForms) {
10228 // copy values from the child forms
10229 // Roo.each(this.childForms, function (f) {
10230 // this.setValues(f.getValues());
10236 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10237 if(asString === true){
10240 return Roo.urlDecode(fs);
10244 * Returns the fields in this form as an object with key/value pairs.
10245 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10248 getFieldValues : function(with_hidden)
10250 var items = this.getItems();
10252 items.each(function(f){
10254 if (!f.getName()) {
10258 var v = f.getValue();
10260 if (f.inputType =='radio') {
10261 if (typeof(ret[f.getName()]) == 'undefined') {
10262 ret[f.getName()] = ''; // empty..
10265 if (!f.el.dom.checked) {
10269 v = f.el.dom.value;
10273 if(f.xtype == 'MoneyField'){
10274 ret[f.currencyName] = f.getCurrency();
10277 // not sure if this supported any more..
10278 if ((typeof(v) == 'object') && f.getRawValue) {
10279 v = f.getRawValue() ; // dates..
10281 // combo boxes where name != hiddenName...
10282 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10283 ret[f.name] = f.getRawValue();
10285 ret[f.getName()] = v;
10292 * Clears all invalid messages in this form.
10293 * @return {BasicForm} this
10295 clearInvalid : function(){
10296 var items = this.getItems();
10298 items.each(function(f){
10306 * Resets this form.
10307 * @return {BasicForm} this
10309 reset : function(){
10310 var items = this.getItems();
10311 items.each(function(f){
10315 Roo.each(this.childForms || [], function (f) {
10323 getItems : function()
10325 var r=new Roo.util.MixedCollection(false, function(o){
10326 return o.id || (o.id = Roo.id());
10328 var iter = function(el) {
10335 Roo.each(el.items,function(e) {
10344 hideFields : function(items)
10346 Roo.each(items, function(i){
10348 var f = this.findField(i);
10359 showFields : function(items)
10361 Roo.each(items, function(i){
10363 var f = this.findField(i);
10376 Roo.apply(Roo.bootstrap.Form, {
10392 intervalID : false,
10398 if(this.isApplied){
10403 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10404 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10405 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10406 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10409 this.maskEl.top.enableDisplayMode("block");
10410 this.maskEl.left.enableDisplayMode("block");
10411 this.maskEl.bottom.enableDisplayMode("block");
10412 this.maskEl.right.enableDisplayMode("block");
10414 this.toolTip = new Roo.bootstrap.Tooltip({
10415 cls : 'roo-form-error-popover',
10417 'left' : ['r-l', [-2,0], 'right'],
10418 'right' : ['l-r', [2,0], 'left'],
10419 'bottom' : ['tl-bl', [0,2], 'top'],
10420 'top' : [ 'bl-tl', [0,-2], 'bottom']
10424 this.toolTip.render(Roo.get(document.body));
10426 this.toolTip.el.enableDisplayMode("block");
10428 Roo.get(document.body).on('click', function(){
10432 Roo.get(document.body).on('touchstart', function(){
10436 this.isApplied = true
10439 mask : function(form, target)
10443 this.target = target;
10445 if(!this.form.errorMask || !target.el){
10449 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10451 Roo.log(scrollable);
10453 var ot = this.target.el.calcOffsetsTo(scrollable);
10455 var scrollTo = ot[1] - this.form.maskOffset;
10457 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10459 scrollable.scrollTo('top', scrollTo);
10461 var box = this.target.el.getBox();
10463 var zIndex = Roo.bootstrap.Modal.zIndex++;
10466 this.maskEl.top.setStyle('position', 'absolute');
10467 this.maskEl.top.setStyle('z-index', zIndex);
10468 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10469 this.maskEl.top.setLeft(0);
10470 this.maskEl.top.setTop(0);
10471 this.maskEl.top.show();
10473 this.maskEl.left.setStyle('position', 'absolute');
10474 this.maskEl.left.setStyle('z-index', zIndex);
10475 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10476 this.maskEl.left.setLeft(0);
10477 this.maskEl.left.setTop(box.y - this.padding);
10478 this.maskEl.left.show();
10480 this.maskEl.bottom.setStyle('position', 'absolute');
10481 this.maskEl.bottom.setStyle('z-index', zIndex);
10482 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10483 this.maskEl.bottom.setLeft(0);
10484 this.maskEl.bottom.setTop(box.bottom + this.padding);
10485 this.maskEl.bottom.show();
10487 this.maskEl.right.setStyle('position', 'absolute');
10488 this.maskEl.right.setStyle('z-index', zIndex);
10489 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10490 this.maskEl.right.setLeft(box.right + this.padding);
10491 this.maskEl.right.setTop(box.y - this.padding);
10492 this.maskEl.right.show();
10494 this.toolTip.bindEl = this.target.el;
10496 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10498 var tip = this.target.blankText;
10500 if(this.target.getValue() !== '' ) {
10502 if (this.target.invalidText.length) {
10503 tip = this.target.invalidText;
10504 } else if (this.target.regexText.length){
10505 tip = this.target.regexText;
10509 this.toolTip.show(tip);
10511 this.intervalID = window.setInterval(function() {
10512 Roo.bootstrap.Form.popover.unmask();
10515 window.onwheel = function(){ return false;};
10517 (function(){ this.isMasked = true; }).defer(500, this);
10521 unmask : function()
10523 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10527 this.maskEl.top.setStyle('position', 'absolute');
10528 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10529 this.maskEl.top.hide();
10531 this.maskEl.left.setStyle('position', 'absolute');
10532 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10533 this.maskEl.left.hide();
10535 this.maskEl.bottom.setStyle('position', 'absolute');
10536 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10537 this.maskEl.bottom.hide();
10539 this.maskEl.right.setStyle('position', 'absolute');
10540 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10541 this.maskEl.right.hide();
10543 this.toolTip.hide();
10545 this.toolTip.el.hide();
10547 window.onwheel = function(){ return true;};
10549 if(this.intervalID){
10550 window.clearInterval(this.intervalID);
10551 this.intervalID = false;
10554 this.isMasked = false;
10564 * Ext JS Library 1.1.1
10565 * Copyright(c) 2006-2007, Ext JS, LLC.
10567 * Originally Released Under LGPL - original licence link has changed is not relivant.
10570 * <script type="text/javascript">
10573 * @class Roo.form.VTypes
10574 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10577 Roo.form.VTypes = function(){
10578 // closure these in so they are only created once.
10579 var alpha = /^[a-zA-Z_]+$/;
10580 var alphanum = /^[a-zA-Z0-9_]+$/;
10581 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10582 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10584 // All these messages and functions are configurable
10587 * The function used to validate email addresses
10588 * @param {String} value The email address
10590 'email' : function(v){
10591 return email.test(v);
10594 * The error text to display when the email validation function returns false
10597 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10599 * The keystroke filter mask to be applied on email input
10602 'emailMask' : /[a-z0-9_\.\-@]/i,
10605 * The function used to validate URLs
10606 * @param {String} value The URL
10608 'url' : function(v){
10609 return url.test(v);
10612 * The error text to display when the url validation function returns false
10615 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10618 * The function used to validate alpha values
10619 * @param {String} value The value
10621 'alpha' : function(v){
10622 return alpha.test(v);
10625 * The error text to display when the alpha validation function returns false
10628 'alphaText' : 'This field should only contain letters and _',
10630 * The keystroke filter mask to be applied on alpha input
10633 'alphaMask' : /[a-z_]/i,
10636 * The function used to validate alphanumeric values
10637 * @param {String} value The value
10639 'alphanum' : function(v){
10640 return alphanum.test(v);
10643 * The error text to display when the alphanumeric validation function returns false
10646 'alphanumText' : 'This field should only contain letters, numbers and _',
10648 * The keystroke filter mask to be applied on alphanumeric input
10651 'alphanumMask' : /[a-z0-9_]/i
10661 * @class Roo.bootstrap.Input
10662 * @extends Roo.bootstrap.Component
10663 * Bootstrap Input class
10664 * @cfg {Boolean} disabled is it disabled
10665 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10666 * @cfg {String} name name of the input
10667 * @cfg {string} fieldLabel - the label associated
10668 * @cfg {string} placeholder - placeholder to put in text.
10669 * @cfg {string} before - input group add on before
10670 * @cfg {string} after - input group add on after
10671 * @cfg {string} size - (lg|sm) or leave empty..
10672 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10673 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10674 * @cfg {Number} md colspan out of 12 for computer-sized screens
10675 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10676 * @cfg {string} value default value of the input
10677 * @cfg {Number} labelWidth set the width of label
10678 * @cfg {Number} labellg set the width of label (1-12)
10679 * @cfg {Number} labelmd set the width of label (1-12)
10680 * @cfg {Number} labelsm set the width of label (1-12)
10681 * @cfg {Number} labelxs set the width of label (1-12)
10682 * @cfg {String} labelAlign (top|left)
10683 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10684 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10685 * @cfg {String} indicatorpos (left|right) default left
10686 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10687 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10688 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10690 * @cfg {String} align (left|center|right) Default left
10691 * @cfg {Boolean} forceFeedback (true|false) Default false
10694 * Create a new Input
10695 * @param {Object} config The config object
10698 Roo.bootstrap.Input = function(config){
10700 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10705 * Fires when this field receives input focus.
10706 * @param {Roo.form.Field} this
10711 * Fires when this field loses input focus.
10712 * @param {Roo.form.Field} this
10716 * @event specialkey
10717 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10718 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10719 * @param {Roo.form.Field} this
10720 * @param {Roo.EventObject} e The event object
10725 * Fires just before the field blurs if the field value has changed.
10726 * @param {Roo.form.Field} this
10727 * @param {Mixed} newValue The new value
10728 * @param {Mixed} oldValue The original value
10733 * Fires after the field has been marked as invalid.
10734 * @param {Roo.form.Field} this
10735 * @param {String} msg The validation message
10740 * Fires after the field has been validated with no errors.
10741 * @param {Roo.form.Field} this
10746 * Fires after the key up
10747 * @param {Roo.form.Field} this
10748 * @param {Roo.EventObject} e The event Object
10753 * Fires after the user pastes into input
10754 * @param {Roo.form.Field} this
10755 * @param {Roo.EventObject} e The event Object
10761 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10763 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10764 automatic validation (defaults to "keyup").
10766 validationEvent : "keyup",
10768 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10770 validateOnBlur : true,
10772 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10774 validationDelay : 250,
10776 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10778 focusClass : "x-form-focus", // not needed???
10782 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10784 invalidClass : "has-warning",
10787 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10789 validClass : "has-success",
10792 * @cfg {Boolean} hasFeedback (true|false) default true
10794 hasFeedback : true,
10797 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10799 invalidFeedbackClass : "glyphicon-warning-sign",
10802 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10804 validFeedbackClass : "glyphicon-ok",
10807 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10809 selectOnFocus : false,
10812 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10816 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10821 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10823 disableKeyFilter : false,
10826 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10830 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10834 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10836 blankText : "Please complete this mandatory field",
10839 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10843 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10845 maxLength : Number.MAX_VALUE,
10847 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10849 minLengthText : "The minimum length for this field is {0}",
10851 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10853 maxLengthText : "The maximum length for this field is {0}",
10857 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10858 * If available, this function will be called only after the basic validators all return true, and will be passed the
10859 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10863 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10864 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10865 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10869 * @cfg {String} regexText -- Depricated - use Invalid Text
10874 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10880 autocomplete: false,
10884 inputType : 'text',
10887 placeholder: false,
10892 preventMark: false,
10893 isFormField : true,
10896 labelAlign : false,
10899 formatedValue : false,
10900 forceFeedback : false,
10902 indicatorpos : 'left',
10912 parentLabelAlign : function()
10915 while (parent.parent()) {
10916 parent = parent.parent();
10917 if (typeof(parent.labelAlign) !='undefined') {
10918 return parent.labelAlign;
10925 getAutoCreate : function()
10927 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10933 if(this.inputType != 'hidden'){
10934 cfg.cls = 'form-group' //input-group
10940 type : this.inputType,
10941 value : this.value,
10942 cls : 'form-control',
10943 placeholder : this.placeholder || '',
10944 autocomplete : this.autocomplete || 'new-password'
10946 if (this.inputType == 'file') {
10947 input.style = 'overflow:hidden'; // why not in CSS?
10950 if(this.capture.length){
10951 input.capture = this.capture;
10954 if(this.accept.length){
10955 input.accept = this.accept + "/*";
10959 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10962 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10963 input.maxLength = this.maxLength;
10966 if (this.disabled) {
10967 input.disabled=true;
10970 if (this.readOnly) {
10971 input.readonly=true;
10975 input.name = this.name;
10979 input.cls += ' input-' + this.size;
10983 ['xs','sm','md','lg'].map(function(size){
10984 if (settings[size]) {
10985 cfg.cls += ' col-' + size + '-' + settings[size];
10989 var inputblock = input;
10993 cls: 'glyphicon form-control-feedback'
10996 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10999 cls : 'has-feedback',
11007 if (this.before || this.after) {
11010 cls : 'input-group',
11014 if (this.before && typeof(this.before) == 'string') {
11016 inputblock.cn.push({
11018 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11022 if (this.before && typeof(this.before) == 'object') {
11023 this.before = Roo.factory(this.before);
11025 inputblock.cn.push({
11027 cls : 'roo-input-before input-group-prepend input-group-' +
11028 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11032 inputblock.cn.push(input);
11034 if (this.after && typeof(this.after) == 'string') {
11035 inputblock.cn.push({
11037 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11041 if (this.after && typeof(this.after) == 'object') {
11042 this.after = Roo.factory(this.after);
11044 inputblock.cn.push({
11046 cls : 'roo-input-after input-group-append input-group-' +
11047 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11051 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11052 inputblock.cls += ' has-feedback';
11053 inputblock.cn.push(feedback);
11058 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11059 tooltip : 'This field is required'
11061 if (this.allowBlank ) {
11062 indicator.style = this.allowBlank ? ' display:none' : '';
11064 if (align ==='left' && this.fieldLabel.length) {
11066 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11073 cls : 'control-label col-form-label',
11074 html : this.fieldLabel
11085 var labelCfg = cfg.cn[1];
11086 var contentCfg = cfg.cn[2];
11088 if(this.indicatorpos == 'right'){
11093 cls : 'control-label col-form-label',
11097 html : this.fieldLabel
11111 labelCfg = cfg.cn[0];
11112 contentCfg = cfg.cn[1];
11116 if(this.labelWidth > 12){
11117 labelCfg.style = "width: " + this.labelWidth + 'px';
11120 if(this.labelWidth < 13 && this.labelmd == 0){
11121 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11124 if(this.labellg > 0){
11125 labelCfg.cls += ' col-lg-' + this.labellg;
11126 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11129 if(this.labelmd > 0){
11130 labelCfg.cls += ' col-md-' + this.labelmd;
11131 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11134 if(this.labelsm > 0){
11135 labelCfg.cls += ' col-sm-' + this.labelsm;
11136 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11139 if(this.labelxs > 0){
11140 labelCfg.cls += ' col-xs-' + this.labelxs;
11141 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11145 } else if ( this.fieldLabel.length) {
11152 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11153 tooltip : 'This field is required',
11154 style : this.allowBlank ? ' display:none' : ''
11158 //cls : 'input-group-addon',
11159 html : this.fieldLabel
11167 if(this.indicatorpos == 'right'){
11172 //cls : 'input-group-addon',
11173 html : this.fieldLabel
11178 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11179 tooltip : 'This field is required',
11180 style : this.allowBlank ? ' display:none' : ''
11200 if (this.parentType === 'Navbar' && this.parent().bar) {
11201 cfg.cls += ' navbar-form';
11204 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11205 // on BS4 we do this only if not form
11206 cfg.cls += ' navbar-form';
11214 * return the real input element.
11216 inputEl: function ()
11218 return this.el.select('input.form-control',true).first();
11221 tooltipEl : function()
11223 return this.inputEl();
11226 indicatorEl : function()
11228 if (Roo.bootstrap.version == 4) {
11229 return false; // not enabled in v4 yet.
11232 var indicator = this.el.select('i.roo-required-indicator',true).first();
11242 setDisabled : function(v)
11244 var i = this.inputEl().dom;
11246 i.removeAttribute('disabled');
11250 i.setAttribute('disabled','true');
11252 initEvents : function()
11255 this.inputEl().on("keydown" , this.fireKey, this);
11256 this.inputEl().on("focus", this.onFocus, this);
11257 this.inputEl().on("blur", this.onBlur, this);
11259 this.inputEl().relayEvent('keyup', this);
11260 this.inputEl().relayEvent('paste', this);
11262 this.indicator = this.indicatorEl();
11264 if(this.indicator){
11265 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11268 // reference to original value for reset
11269 this.originalValue = this.getValue();
11270 //Roo.form.TextField.superclass.initEvents.call(this);
11271 if(this.validationEvent == 'keyup'){
11272 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11273 this.inputEl().on('keyup', this.filterValidation, this);
11275 else if(this.validationEvent !== false){
11276 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11279 if(this.selectOnFocus){
11280 this.on("focus", this.preFocus, this);
11283 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11284 this.inputEl().on("keypress", this.filterKeys, this);
11286 this.inputEl().relayEvent('keypress', this);
11289 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11290 this.el.on("click", this.autoSize, this);
11293 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11294 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11297 if (typeof(this.before) == 'object') {
11298 this.before.render(this.el.select('.roo-input-before',true).first());
11300 if (typeof(this.after) == 'object') {
11301 this.after.render(this.el.select('.roo-input-after',true).first());
11304 this.inputEl().on('change', this.onChange, this);
11307 filterValidation : function(e){
11308 if(!e.isNavKeyPress()){
11309 this.validationTask.delay(this.validationDelay);
11313 * Validates the field value
11314 * @return {Boolean} True if the value is valid, else false
11316 validate : function(){
11317 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11318 if(this.disabled || this.validateValue(this.getRawValue())){
11323 this.markInvalid();
11329 * Validates a value according to the field's validation rules and marks the field as invalid
11330 * if the validation fails
11331 * @param {Mixed} value The value to validate
11332 * @return {Boolean} True if the value is valid, else false
11334 validateValue : function(value)
11336 if(this.getVisibilityEl().hasClass('hidden')){
11340 if(value.length < 1) { // if it's blank
11341 if(this.allowBlank){
11347 if(value.length < this.minLength){
11350 if(value.length > this.maxLength){
11354 var vt = Roo.form.VTypes;
11355 if(!vt[this.vtype](value, this)){
11359 if(typeof this.validator == "function"){
11360 var msg = this.validator(value);
11364 if (typeof(msg) == 'string') {
11365 this.invalidText = msg;
11369 if(this.regex && !this.regex.test(value)){
11377 fireKey : function(e){
11378 //Roo.log('field ' + e.getKey());
11379 if(e.isNavKeyPress()){
11380 this.fireEvent("specialkey", this, e);
11383 focus : function (selectText){
11385 this.inputEl().focus();
11386 if(selectText === true){
11387 this.inputEl().dom.select();
11393 onFocus : function(){
11394 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11395 // this.el.addClass(this.focusClass);
11397 if(!this.hasFocus){
11398 this.hasFocus = true;
11399 this.startValue = this.getValue();
11400 this.fireEvent("focus", this);
11404 beforeBlur : Roo.emptyFn,
11408 onBlur : function(){
11410 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11411 //this.el.removeClass(this.focusClass);
11413 this.hasFocus = false;
11414 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11417 var v = this.getValue();
11418 if(String(v) !== String(this.startValue)){
11419 this.fireEvent('change', this, v, this.startValue);
11421 this.fireEvent("blur", this);
11424 onChange : function(e)
11426 var v = this.getValue();
11427 if(String(v) !== String(this.startValue)){
11428 this.fireEvent('change', this, v, this.startValue);
11434 * Resets the current field value to the originally loaded value and clears any validation messages
11436 reset : function(){
11437 this.setValue(this.originalValue);
11441 * Returns the name of the field
11442 * @return {Mixed} name The name field
11444 getName: function(){
11448 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11449 * @return {Mixed} value The field value
11451 getValue : function(){
11453 var v = this.inputEl().getValue();
11458 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11459 * @return {Mixed} value The field value
11461 getRawValue : function(){
11462 var v = this.inputEl().getValue();
11468 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11469 * @param {Mixed} value The value to set
11471 setRawValue : function(v){
11472 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11475 selectText : function(start, end){
11476 var v = this.getRawValue();
11478 start = start === undefined ? 0 : start;
11479 end = end === undefined ? v.length : end;
11480 var d = this.inputEl().dom;
11481 if(d.setSelectionRange){
11482 d.setSelectionRange(start, end);
11483 }else if(d.createTextRange){
11484 var range = d.createTextRange();
11485 range.moveStart("character", start);
11486 range.moveEnd("character", v.length-end);
11493 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11494 * @param {Mixed} value The value to set
11496 setValue : function(v){
11499 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11505 processValue : function(value){
11506 if(this.stripCharsRe){
11507 var newValue = value.replace(this.stripCharsRe, '');
11508 if(newValue !== value){
11509 this.setRawValue(newValue);
11516 preFocus : function(){
11518 if(this.selectOnFocus){
11519 this.inputEl().dom.select();
11522 filterKeys : function(e){
11523 var k = e.getKey();
11524 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11527 var c = e.getCharCode(), cc = String.fromCharCode(c);
11528 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11531 if(!this.maskRe.test(cc)){
11536 * Clear any invalid styles/messages for this field
11538 clearInvalid : function(){
11540 if(!this.el || this.preventMark){ // not rendered
11545 this.el.removeClass([this.invalidClass, 'is-invalid']);
11547 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11549 var feedback = this.el.select('.form-control-feedback', true).first();
11552 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11557 if(this.indicator){
11558 this.indicator.removeClass('visible');
11559 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11562 this.fireEvent('valid', this);
11566 * Mark this field as valid
11568 markValid : function()
11570 if(!this.el || this.preventMark){ // not rendered...
11574 this.el.removeClass([this.invalidClass, this.validClass]);
11575 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11577 var feedback = this.el.select('.form-control-feedback', true).first();
11580 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11583 if(this.indicator){
11584 this.indicator.removeClass('visible');
11585 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11593 if(this.allowBlank && !this.getRawValue().length){
11596 if (Roo.bootstrap.version == 3) {
11597 this.el.addClass(this.validClass);
11599 this.inputEl().addClass('is-valid');
11602 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11604 var feedback = this.el.select('.form-control-feedback', true).first();
11607 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11608 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11613 this.fireEvent('valid', this);
11617 * Mark this field as invalid
11618 * @param {String} msg The validation message
11620 markInvalid : function(msg)
11622 if(!this.el || this.preventMark){ // not rendered
11626 this.el.removeClass([this.invalidClass, this.validClass]);
11627 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass(
11633 [this.invalidFeedbackClass, this.validFeedbackClass]);
11640 if(this.allowBlank && !this.getRawValue().length){
11644 if(this.indicator){
11645 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11646 this.indicator.addClass('visible');
11648 if (Roo.bootstrap.version == 3) {
11649 this.el.addClass(this.invalidClass);
11651 this.inputEl().addClass('is-invalid');
11656 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11658 var feedback = this.el.select('.form-control-feedback', true).first();
11661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11663 if(this.getValue().length || this.forceFeedback){
11664 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11671 this.fireEvent('invalid', this, msg);
11674 SafariOnKeyDown : function(event)
11676 // this is a workaround for a password hang bug on chrome/ webkit.
11677 if (this.inputEl().dom.type != 'password') {
11681 var isSelectAll = false;
11683 if(this.inputEl().dom.selectionEnd > 0){
11684 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11686 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11687 event.preventDefault();
11692 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11694 event.preventDefault();
11695 // this is very hacky as keydown always get's upper case.
11697 var cc = String.fromCharCode(event.getCharCode());
11698 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11702 adjustWidth : function(tag, w){
11703 tag = tag.toLowerCase();
11704 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11705 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11706 if(tag == 'input'){
11709 if(tag == 'textarea'){
11712 }else if(Roo.isOpera){
11713 if(tag == 'input'){
11716 if(tag == 'textarea'){
11724 setFieldLabel : function(v)
11726 if(!this.rendered){
11730 if(this.indicatorEl()){
11731 var ar = this.el.select('label > span',true);
11733 if (ar.elements.length) {
11734 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11735 this.fieldLabel = v;
11739 var br = this.el.select('label',true);
11741 if(br.elements.length) {
11742 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11743 this.fieldLabel = v;
11747 Roo.log('Cannot Found any of label > span || label in input');
11751 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11752 this.fieldLabel = v;
11767 * @class Roo.bootstrap.TextArea
11768 * @extends Roo.bootstrap.Input
11769 * Bootstrap TextArea class
11770 * @cfg {Number} cols Specifies the visible width of a text area
11771 * @cfg {Number} rows Specifies the visible number of lines in a text area
11772 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11773 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11774 * @cfg {string} html text
11777 * Create a new TextArea
11778 * @param {Object} config The config object
11781 Roo.bootstrap.TextArea = function(config){
11782 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11786 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11796 getAutoCreate : function(){
11798 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11804 if(this.inputType != 'hidden'){
11805 cfg.cls = 'form-group' //input-group
11813 value : this.value || '',
11814 html: this.html || '',
11815 cls : 'form-control',
11816 placeholder : this.placeholder || ''
11820 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11821 input.maxLength = this.maxLength;
11825 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11829 input.cols = this.cols;
11832 if (this.readOnly) {
11833 input.readonly = true;
11837 input.name = this.name;
11841 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11845 ['xs','sm','md','lg'].map(function(size){
11846 if (settings[size]) {
11847 cfg.cls += ' col-' + size + '-' + settings[size];
11851 var inputblock = input;
11853 if(this.hasFeedback && !this.allowBlank){
11857 cls: 'glyphicon form-control-feedback'
11861 cls : 'has-feedback',
11870 if (this.before || this.after) {
11873 cls : 'input-group',
11877 inputblock.cn.push({
11879 cls : 'input-group-addon',
11884 inputblock.cn.push(input);
11886 if(this.hasFeedback && !this.allowBlank){
11887 inputblock.cls += ' has-feedback';
11888 inputblock.cn.push(feedback);
11892 inputblock.cn.push({
11894 cls : 'input-group-addon',
11901 if (align ==='left' && this.fieldLabel.length) {
11906 cls : 'control-label',
11907 html : this.fieldLabel
11918 if(this.labelWidth > 12){
11919 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11922 if(this.labelWidth < 13 && this.labelmd == 0){
11923 this.labelmd = this.labelWidth;
11926 if(this.labellg > 0){
11927 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11928 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11931 if(this.labelmd > 0){
11932 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11933 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11936 if(this.labelsm > 0){
11937 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11938 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11941 if(this.labelxs > 0){
11942 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11943 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11946 } else if ( this.fieldLabel.length) {
11951 //cls : 'input-group-addon',
11952 html : this.fieldLabel
11970 if (this.disabled) {
11971 input.disabled=true;
11978 * return the real textarea element.
11980 inputEl: function ()
11982 return this.el.select('textarea.form-control',true).first();
11986 * Clear any invalid styles/messages for this field
11988 clearInvalid : function()
11991 if(!this.el || this.preventMark){ // not rendered
11995 var label = this.el.select('label', true).first();
11996 var icon = this.el.select('i.fa-star', true).first();
12001 this.el.removeClass( this.validClass);
12002 this.inputEl().removeClass('is-invalid');
12004 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12006 var feedback = this.el.select('.form-control-feedback', true).first();
12009 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12014 this.fireEvent('valid', this);
12018 * Mark this field as valid
12020 markValid : function()
12022 if(!this.el || this.preventMark){ // not rendered
12026 this.el.removeClass([this.invalidClass, this.validClass]);
12027 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12029 var feedback = this.el.select('.form-control-feedback', true).first();
12032 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12035 if(this.disabled || this.allowBlank){
12039 var label = this.el.select('label', true).first();
12040 var icon = this.el.select('i.fa-star', true).first();
12045 if (Roo.bootstrap.version == 3) {
12046 this.el.addClass(this.validClass);
12048 this.inputEl().addClass('is-valid');
12052 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12054 var feedback = this.el.select('.form-control-feedback', true).first();
12057 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12058 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12063 this.fireEvent('valid', this);
12067 * Mark this field as invalid
12068 * @param {String} msg The validation message
12070 markInvalid : function(msg)
12072 if(!this.el || this.preventMark){ // not rendered
12076 this.el.removeClass([this.invalidClass, this.validClass]);
12077 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12079 var feedback = this.el.select('.form-control-feedback', true).first();
12082 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12085 if(this.disabled || this.allowBlank){
12089 var label = this.el.select('label', true).first();
12090 var icon = this.el.select('i.fa-star', true).first();
12092 if(!this.getValue().length && label && !icon){
12093 this.el.createChild({
12095 cls : 'text-danger fa fa-lg fa-star',
12096 tooltip : 'This field is required',
12097 style : 'margin-right:5px;'
12101 if (Roo.bootstrap.version == 3) {
12102 this.el.addClass(this.invalidClass);
12104 this.inputEl().addClass('is-invalid');
12107 // fixme ... this may be depricated need to test..
12108 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12110 var feedback = this.el.select('.form-control-feedback', true).first();
12113 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12115 if(this.getValue().length || this.forceFeedback){
12116 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12123 this.fireEvent('invalid', this, msg);
12131 * trigger field - base class for combo..
12136 * @class Roo.bootstrap.TriggerField
12137 * @extends Roo.bootstrap.Input
12138 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12139 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12140 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12141 * for which you can provide a custom implementation. For example:
12143 var trigger = new Roo.bootstrap.TriggerField();
12144 trigger.onTriggerClick = myTriggerFn;
12145 trigger.applyTo('my-field');
12148 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12149 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12150 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12151 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12152 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12155 * Create a new TriggerField.
12156 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12157 * to the base TextField)
12159 Roo.bootstrap.TriggerField = function(config){
12160 this.mimicing = false;
12161 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12164 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12166 * @cfg {String} triggerClass A CSS class to apply to the trigger
12169 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12174 * @cfg {Boolean} removable (true|false) special filter default false
12178 /** @cfg {Boolean} grow @hide */
12179 /** @cfg {Number} growMin @hide */
12180 /** @cfg {Number} growMax @hide */
12186 autoSize: Roo.emptyFn,
12190 deferHeight : true,
12193 actionMode : 'wrap',
12198 getAutoCreate : function(){
12200 var align = this.labelAlign || this.parentLabelAlign();
12205 cls: 'form-group' //input-group
12212 type : this.inputType,
12213 cls : 'form-control',
12214 autocomplete: 'new-password',
12215 placeholder : this.placeholder || ''
12219 input.name = this.name;
12222 input.cls += ' input-' + this.size;
12225 if (this.disabled) {
12226 input.disabled=true;
12229 var inputblock = input;
12231 if(this.hasFeedback && !this.allowBlank){
12235 cls: 'glyphicon form-control-feedback'
12238 if(this.removable && !this.editable ){
12240 cls : 'has-feedback',
12246 cls : 'roo-combo-removable-btn close'
12253 cls : 'has-feedback',
12262 if(this.removable && !this.editable ){
12264 cls : 'roo-removable',
12270 cls : 'roo-combo-removable-btn close'
12277 if (this.before || this.after) {
12280 cls : 'input-group',
12284 inputblock.cn.push({
12286 cls : 'input-group-addon input-group-prepend input-group-text',
12291 inputblock.cn.push(input);
12293 if(this.hasFeedback && !this.allowBlank){
12294 inputblock.cls += ' has-feedback';
12295 inputblock.cn.push(feedback);
12299 inputblock.cn.push({
12301 cls : 'input-group-addon input-group-append input-group-text',
12310 var ibwrap = inputblock;
12315 cls: 'roo-select2-choices',
12319 cls: 'roo-select2-search-field',
12331 cls: 'roo-select2-container input-group',
12336 cls: 'form-hidden-field'
12342 if(!this.multiple && this.showToggleBtn){
12348 if (this.caret != false) {
12351 cls: 'fa fa-' + this.caret
12358 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12360 Roo.bootstrap.version == 3 ? caret : '',
12363 cls: 'combobox-clear',
12377 combobox.cls += ' roo-select2-container-multi';
12381 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12382 tooltip : 'This field is required'
12384 if (Roo.bootstrap.version == 4) {
12387 style : 'display:none'
12392 if (align ==='left' && this.fieldLabel.length) {
12394 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12401 cls : 'control-label',
12402 html : this.fieldLabel
12414 var labelCfg = cfg.cn[1];
12415 var contentCfg = cfg.cn[2];
12417 if(this.indicatorpos == 'right'){
12422 cls : 'control-label',
12426 html : this.fieldLabel
12440 labelCfg = cfg.cn[0];
12441 contentCfg = cfg.cn[1];
12444 if(this.labelWidth > 12){
12445 labelCfg.style = "width: " + this.labelWidth + 'px';
12448 if(this.labelWidth < 13 && this.labelmd == 0){
12449 this.labelmd = this.labelWidth;
12452 if(this.labellg > 0){
12453 labelCfg.cls += ' col-lg-' + this.labellg;
12454 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12457 if(this.labelmd > 0){
12458 labelCfg.cls += ' col-md-' + this.labelmd;
12459 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12462 if(this.labelsm > 0){
12463 labelCfg.cls += ' col-sm-' + this.labelsm;
12464 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12467 if(this.labelxs > 0){
12468 labelCfg.cls += ' col-xs-' + this.labelxs;
12469 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12472 } else if ( this.fieldLabel.length) {
12473 // Roo.log(" label");
12478 //cls : 'input-group-addon',
12479 html : this.fieldLabel
12487 if(this.indicatorpos == 'right'){
12495 html : this.fieldLabel
12509 // Roo.log(" no label && no align");
12516 ['xs','sm','md','lg'].map(function(size){
12517 if (settings[size]) {
12518 cfg.cls += ' col-' + size + '-' + settings[size];
12529 onResize : function(w, h){
12530 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12531 // if(typeof w == 'number'){
12532 // var x = w - this.trigger.getWidth();
12533 // this.inputEl().setWidth(this.adjustWidth('input', x));
12534 // this.trigger.setStyle('left', x+'px');
12539 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12542 getResizeEl : function(){
12543 return this.inputEl();
12547 getPositionEl : function(){
12548 return this.inputEl();
12552 alignErrorIcon : function(){
12553 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12557 initEvents : function(){
12561 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12562 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12563 if(!this.multiple && this.showToggleBtn){
12564 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12565 if(this.hideTrigger){
12566 this.trigger.setDisplayed(false);
12568 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12572 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12575 if(this.removable && !this.editable && !this.tickable){
12576 var close = this.closeTriggerEl();
12579 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12580 close.on('click', this.removeBtnClick, this, close);
12584 //this.trigger.addClassOnOver('x-form-trigger-over');
12585 //this.trigger.addClassOnClick('x-form-trigger-click');
12588 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12592 closeTriggerEl : function()
12594 var close = this.el.select('.roo-combo-removable-btn', true).first();
12595 return close ? close : false;
12598 removeBtnClick : function(e, h, el)
12600 e.preventDefault();
12602 if(this.fireEvent("remove", this) !== false){
12604 this.fireEvent("afterremove", this)
12608 createList : function()
12610 this.list = Roo.get(document.body).createChild({
12611 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12612 cls: 'typeahead typeahead-long dropdown-menu shadow',
12613 style: 'display:none'
12616 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12621 initTrigger : function(){
12626 onDestroy : function(){
12628 this.trigger.removeAllListeners();
12629 // this.trigger.remove();
12632 // this.wrap.remove();
12634 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12638 onFocus : function(){
12639 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12641 if(!this.mimicing){
12642 this.wrap.addClass('x-trigger-wrap-focus');
12643 this.mimicing = true;
12644 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12645 if(this.monitorTab){
12646 this.el.on("keydown", this.checkTab, this);
12653 checkTab : function(e){
12654 if(e.getKey() == e.TAB){
12655 this.triggerBlur();
12660 onBlur : function(){
12665 mimicBlur : function(e, t){
12667 if(!this.wrap.contains(t) && this.validateBlur()){
12668 this.triggerBlur();
12674 triggerBlur : function(){
12675 this.mimicing = false;
12676 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12677 if(this.monitorTab){
12678 this.el.un("keydown", this.checkTab, this);
12680 //this.wrap.removeClass('x-trigger-wrap-focus');
12681 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12685 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12686 validateBlur : function(e, t){
12691 onDisable : function(){
12692 this.inputEl().dom.disabled = true;
12693 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12695 // this.wrap.addClass('x-item-disabled');
12700 onEnable : function(){
12701 this.inputEl().dom.disabled = false;
12702 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12704 // this.el.removeClass('x-item-disabled');
12709 onShow : function(){
12710 var ae = this.getActionEl();
12713 ae.dom.style.display = '';
12714 ae.dom.style.visibility = 'visible';
12720 onHide : function(){
12721 var ae = this.getActionEl();
12722 ae.dom.style.display = 'none';
12726 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12727 * by an implementing function.
12729 * @param {EventObject} e
12731 onTriggerClick : Roo.emptyFn
12739 * @class Roo.bootstrap.CardUploader
12740 * @extends Roo.bootstrap.Button
12741 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12742 * @cfg {Number} errorTimeout default 3000
12743 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12744 * @cfg {Array} html The button text.
12748 * Create a new CardUploader
12749 * @param {Object} config The config object
12752 Roo.bootstrap.CardUploader = function(config){
12756 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12759 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12767 * When a image is clicked on - and needs to display a slideshow or similar..
12768 * @param {Roo.bootstrap.Card} this
12769 * @param {Object} The image information data
12775 * When a the download link is clicked
12776 * @param {Roo.bootstrap.Card} this
12777 * @param {Object} The image information data contains
12784 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12787 errorTimeout : 3000,
12791 fileCollection : false,
12794 getAutoCreate : function()
12798 cls :'form-group' ,
12803 //cls : 'input-group-addon',
12804 html : this.fieldLabel
12812 value : this.value,
12813 cls : 'd-none form-control'
12818 multiple : 'multiple',
12820 cls : 'd-none roo-card-upload-selector'
12824 cls : 'roo-card-uploader-button-container w-100 mb-2'
12827 cls : 'card-columns roo-card-uploader-container'
12837 getChildContainer : function() /// what children are added to.
12839 return this.containerEl;
12842 getButtonContainer : function() /// what children are added to.
12844 return this.el.select(".roo-card-uploader-button-container").first();
12847 initEvents : function()
12850 Roo.bootstrap.Input.prototype.initEvents.call(this);
12854 xns: Roo.bootstrap,
12857 container_method : 'getButtonContainer' ,
12858 html : this.html, // fix changable?
12861 'click' : function(btn, e) {
12870 this.urlAPI = (window.createObjectURL && window) ||
12871 (window.URL && URL.revokeObjectURL && URL) ||
12872 (window.webkitURL && webkitURL);
12877 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12879 this.selectorEl.on('change', this.onFileSelected, this);
12882 this.images.forEach(function(img) {
12885 this.images = false;
12887 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12893 onClick : function(e)
12895 e.preventDefault();
12897 this.selectorEl.dom.click();
12901 onFileSelected : function(e)
12903 e.preventDefault();
12905 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12909 Roo.each(this.selectorEl.dom.files, function(file){
12910 this.addFile(file);
12919 addFile : function(file)
12922 if(typeof(file) === 'string'){
12923 throw "Add file by name?"; // should not happen
12927 if(!file || !this.urlAPI){
12937 var url = _this.urlAPI.createObjectURL( file);
12940 id : Roo.bootstrap.CardUploader.ID--,
12941 is_uploaded : false,
12945 mimetype : file.type,
12953 * addCard - add an Attachment to the uploader
12954 * @param data - the data about the image to upload
12958 title : "Title of file",
12959 is_uploaded : false,
12960 src : "http://.....",
12961 srcfile : { the File upload object },
12962 mimetype : file.type,
12965 .. any other data...
12971 addCard : function (data)
12973 // hidden input element?
12974 // if the file is not an image...
12975 //then we need to use something other that and header_image
12980 xns : Roo.bootstrap,
12981 xtype : 'CardFooter',
12984 xns : Roo.bootstrap,
12990 xns : Roo.bootstrap,
12992 html : String.format("<small>{0}</small>", data.title),
12993 cls : 'col-10 text-left',
12998 click : function() {
13000 t.fireEvent( "download", t, data );
13006 xns : Roo.bootstrap,
13008 style: 'max-height: 28px; ',
13014 click : function() {
13015 t.removeCard(data.id)
13027 var cn = this.addxtype(
13030 xns : Roo.bootstrap,
13033 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13034 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13035 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13040 initEvents : function() {
13041 Roo.bootstrap.Card.prototype.initEvents.call(this);
13043 this.imgEl = this.el.select('.card-img-top').first();
13045 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13046 this.imgEl.set({ 'pointer' : 'cursor' });
13049 this.getCardFooter().addClass('p-1');
13056 // dont' really need ot update items.
13057 // this.items.push(cn);
13058 this.fileCollection.add(cn);
13060 if (!data.srcfile) {
13061 this.updateInput();
13066 var reader = new FileReader();
13067 reader.addEventListener("load", function() {
13068 data.srcdata = reader.result;
13071 reader.readAsDataURL(data.srcfile);
13076 removeCard : function(id)
13079 var card = this.fileCollection.get(id);
13080 card.data.is_deleted = 1;
13081 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13082 //this.fileCollection.remove(card);
13083 //this.items = this.items.filter(function(e) { return e != card });
13084 // dont' really need ot update items.
13085 card.el.dom.parentNode.removeChild(card.el.dom);
13086 this.updateInput();
13092 this.fileCollection.each(function(card) {
13093 if (card.el.dom && card.el.dom.parentNode) {
13094 card.el.dom.parentNode.removeChild(card.el.dom);
13097 this.fileCollection.clear();
13098 this.updateInput();
13101 updateInput : function()
13104 this.fileCollection.each(function(e) {
13108 this.inputEl().dom.value = JSON.stringify(data);
13118 Roo.bootstrap.CardUploader.ID = -1;/*
13120 * Ext JS Library 1.1.1
13121 * Copyright(c) 2006-2007, Ext JS, LLC.
13123 * Originally Released Under LGPL - original licence link has changed is not relivant.
13126 * <script type="text/javascript">
13131 * @class Roo.data.SortTypes
13133 * Defines the default sorting (casting?) comparison functions used when sorting data.
13135 Roo.data.SortTypes = {
13137 * Default sort that does nothing
13138 * @param {Mixed} s The value being converted
13139 * @return {Mixed} The comparison value
13141 none : function(s){
13146 * The regular expression used to strip tags
13150 stripTagsRE : /<\/?[^>]+>/gi,
13153 * Strips all HTML tags to sort on text only
13154 * @param {Mixed} s The value being converted
13155 * @return {String} The comparison value
13157 asText : function(s){
13158 return String(s).replace(this.stripTagsRE, "");
13162 * Strips all HTML tags to sort on text only - Case insensitive
13163 * @param {Mixed} s The value being converted
13164 * @return {String} The comparison value
13166 asUCText : function(s){
13167 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13171 * Case insensitive string
13172 * @param {Mixed} s The value being converted
13173 * @return {String} The comparison value
13175 asUCString : function(s) {
13176 return String(s).toUpperCase();
13181 * @param {Mixed} s The value being converted
13182 * @return {Number} The comparison value
13184 asDate : function(s) {
13188 if(s instanceof Date){
13189 return s.getTime();
13191 return Date.parse(String(s));
13196 * @param {Mixed} s The value being converted
13197 * @return {Float} The comparison value
13199 asFloat : function(s) {
13200 var val = parseFloat(String(s).replace(/,/g, ""));
13209 * @param {Mixed} s The value being converted
13210 * @return {Number} The comparison value
13212 asInt : function(s) {
13213 var val = parseInt(String(s).replace(/,/g, ""));
13221 * Ext JS Library 1.1.1
13222 * Copyright(c) 2006-2007, Ext JS, LLC.
13224 * Originally Released Under LGPL - original licence link has changed is not relivant.
13227 * <script type="text/javascript">
13231 * @class Roo.data.Record
13232 * Instances of this class encapsulate both record <em>definition</em> information, and record
13233 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13234 * to access Records cached in an {@link Roo.data.Store} object.<br>
13236 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13237 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13240 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13242 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13243 * {@link #create}. The parameters are the same.
13244 * @param {Array} data An associative Array of data values keyed by the field name.
13245 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13246 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13247 * not specified an integer id is generated.
13249 Roo.data.Record = function(data, id){
13250 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13255 * Generate a constructor for a specific record layout.
13256 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13257 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13258 * Each field definition object may contain the following properties: <ul>
13259 * <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,
13260 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13261 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13262 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13263 * is being used, then this is a string containing the javascript expression to reference the data relative to
13264 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13265 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13266 * this may be omitted.</p></li>
13267 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13268 * <ul><li>auto (Default, implies no conversion)</li>
13273 * <li>date</li></ul></p></li>
13274 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13275 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13276 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13277 * by the Reader into an object that will be stored in the Record. It is passed the
13278 * following parameters:<ul>
13279 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13281 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13283 * <br>usage:<br><pre><code>
13284 var TopicRecord = Roo.data.Record.create(
13285 {name: 'title', mapping: 'topic_title'},
13286 {name: 'author', mapping: 'username'},
13287 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13288 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13289 {name: 'lastPoster', mapping: 'user2'},
13290 {name: 'excerpt', mapping: 'post_text'}
13293 var myNewRecord = new TopicRecord({
13294 title: 'Do my job please',
13297 lastPost: new Date(),
13298 lastPoster: 'Animal',
13299 excerpt: 'No way dude!'
13301 myStore.add(myNewRecord);
13306 Roo.data.Record.create = function(o){
13307 var f = function(){
13308 f.superclass.constructor.apply(this, arguments);
13310 Roo.extend(f, Roo.data.Record);
13311 var p = f.prototype;
13312 p.fields = new Roo.util.MixedCollection(false, function(field){
13315 for(var i = 0, len = o.length; i < len; i++){
13316 p.fields.add(new Roo.data.Field(o[i]));
13318 f.getField = function(name){
13319 return p.fields.get(name);
13324 Roo.data.Record.AUTO_ID = 1000;
13325 Roo.data.Record.EDIT = 'edit';
13326 Roo.data.Record.REJECT = 'reject';
13327 Roo.data.Record.COMMIT = 'commit';
13329 Roo.data.Record.prototype = {
13331 * Readonly flag - true if this record has been modified.
13340 join : function(store){
13341 this.store = store;
13345 * Set the named field to the specified value.
13346 * @param {String} name The name of the field to set.
13347 * @param {Object} value The value to set the field to.
13349 set : function(name, value){
13350 if(this.data[name] == value){
13354 if(!this.modified){
13355 this.modified = {};
13357 if(typeof this.modified[name] == 'undefined'){
13358 this.modified[name] = this.data[name];
13360 this.data[name] = value;
13361 if(!this.editing && this.store){
13362 this.store.afterEdit(this);
13367 * Get the value of the named field.
13368 * @param {String} name The name of the field to get the value of.
13369 * @return {Object} The value of the field.
13371 get : function(name){
13372 return this.data[name];
13376 beginEdit : function(){
13377 this.editing = true;
13378 this.modified = {};
13382 cancelEdit : function(){
13383 this.editing = false;
13384 delete this.modified;
13388 endEdit : function(){
13389 this.editing = false;
13390 if(this.dirty && this.store){
13391 this.store.afterEdit(this);
13396 * Usually called by the {@link Roo.data.Store} which owns the Record.
13397 * Rejects all changes made to the Record since either creation, or the last commit operation.
13398 * Modified fields are reverted to their original values.
13400 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13401 * of reject operations.
13403 reject : function(){
13404 var m = this.modified;
13406 if(typeof m[n] != "function"){
13407 this.data[n] = m[n];
13410 this.dirty = false;
13411 delete this.modified;
13412 this.editing = false;
13414 this.store.afterReject(this);
13419 * Usually called by the {@link Roo.data.Store} which owns the Record.
13420 * Commits all changes made to the Record since either creation, or the last commit operation.
13422 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13423 * of commit operations.
13425 commit : function(){
13426 this.dirty = false;
13427 delete this.modified;
13428 this.editing = false;
13430 this.store.afterCommit(this);
13435 hasError : function(){
13436 return this.error != null;
13440 clearError : function(){
13445 * Creates a copy of this record.
13446 * @param {String} id (optional) A new record id if you don't want to use this record's id
13449 copy : function(newId) {
13450 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13454 * Ext JS Library 1.1.1
13455 * Copyright(c) 2006-2007, Ext JS, LLC.
13457 * Originally Released Under LGPL - original licence link has changed is not relivant.
13460 * <script type="text/javascript">
13466 * @class Roo.data.Store
13467 * @extends Roo.util.Observable
13468 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13469 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13471 * 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
13472 * has no knowledge of the format of the data returned by the Proxy.<br>
13474 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13475 * instances from the data object. These records are cached and made available through accessor functions.
13477 * Creates a new Store.
13478 * @param {Object} config A config object containing the objects needed for the Store to access data,
13479 * and read the data into Records.
13481 Roo.data.Store = function(config){
13482 this.data = new Roo.util.MixedCollection(false);
13483 this.data.getKey = function(o){
13486 this.baseParams = {};
13488 this.paramNames = {
13493 "multisort" : "_multisort"
13496 if(config && config.data){
13497 this.inlineData = config.data;
13498 delete config.data;
13501 Roo.apply(this, config);
13503 if(this.reader){ // reader passed
13504 this.reader = Roo.factory(this.reader, Roo.data);
13505 this.reader.xmodule = this.xmodule || false;
13506 if(!this.recordType){
13507 this.recordType = this.reader.recordType;
13509 if(this.reader.onMetaChange){
13510 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13514 if(this.recordType){
13515 this.fields = this.recordType.prototype.fields;
13517 this.modified = [];
13521 * @event datachanged
13522 * Fires when the data cache has changed, and a widget which is using this Store
13523 * as a Record cache should refresh its view.
13524 * @param {Store} this
13526 datachanged : true,
13528 * @event metachange
13529 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13530 * @param {Store} this
13531 * @param {Object} meta The JSON metadata
13536 * Fires when Records have been added to the Store
13537 * @param {Store} this
13538 * @param {Roo.data.Record[]} records The array of Records added
13539 * @param {Number} index The index at which the record(s) were added
13544 * Fires when a Record has been removed from the Store
13545 * @param {Store} this
13546 * @param {Roo.data.Record} record The Record that was removed
13547 * @param {Number} index The index at which the record was removed
13552 * Fires when a Record has been updated
13553 * @param {Store} this
13554 * @param {Roo.data.Record} record The Record that was updated
13555 * @param {String} operation The update operation being performed. Value may be one of:
13557 Roo.data.Record.EDIT
13558 Roo.data.Record.REJECT
13559 Roo.data.Record.COMMIT
13565 * Fires when the data cache has been cleared.
13566 * @param {Store} this
13570 * @event beforeload
13571 * Fires before a request is made for a new data object. If the beforeload handler returns false
13572 * the load action will be canceled.
13573 * @param {Store} this
13574 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13578 * @event beforeloadadd
13579 * Fires after a new set of Records has been loaded.
13580 * @param {Store} this
13581 * @param {Roo.data.Record[]} records The Records that were loaded
13582 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13584 beforeloadadd : true,
13587 * Fires after a new set of Records has been loaded, before they are added to the store.
13588 * @param {Store} this
13589 * @param {Roo.data.Record[]} records The Records that were loaded
13590 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13591 * @params {Object} return from reader
13595 * @event loadexception
13596 * Fires if an exception occurs in the Proxy during loading.
13597 * Called with the signature of the Proxy's "loadexception" event.
13598 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13601 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13602 * @param {Object} load options
13603 * @param {Object} jsonData from your request (normally this contains the Exception)
13605 loadexception : true
13609 this.proxy = Roo.factory(this.proxy, Roo.data);
13610 this.proxy.xmodule = this.xmodule || false;
13611 this.relayEvents(this.proxy, ["loadexception"]);
13613 this.sortToggle = {};
13614 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13616 Roo.data.Store.superclass.constructor.call(this);
13618 if(this.inlineData){
13619 this.loadData(this.inlineData);
13620 delete this.inlineData;
13624 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13626 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13627 * without a remote query - used by combo/forms at present.
13631 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13634 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13637 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13638 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13641 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13642 * on any HTTP request
13645 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13648 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13652 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13653 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13655 remoteSort : false,
13658 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13659 * loaded or when a record is removed. (defaults to false).
13661 pruneModifiedRecords : false,
13664 lastOptions : null,
13667 * Add Records to the Store and fires the add event.
13668 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13670 add : function(records){
13671 records = [].concat(records);
13672 for(var i = 0, len = records.length; i < len; i++){
13673 records[i].join(this);
13675 var index = this.data.length;
13676 this.data.addAll(records);
13677 this.fireEvent("add", this, records, index);
13681 * Remove a Record from the Store and fires the remove event.
13682 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13684 remove : function(record){
13685 var index = this.data.indexOf(record);
13686 this.data.removeAt(index);
13688 if(this.pruneModifiedRecords){
13689 this.modified.remove(record);
13691 this.fireEvent("remove", this, record, index);
13695 * Remove all Records from the Store and fires the clear event.
13697 removeAll : function(){
13699 if(this.pruneModifiedRecords){
13700 this.modified = [];
13702 this.fireEvent("clear", this);
13706 * Inserts Records to the Store at the given index and fires the add event.
13707 * @param {Number} index The start index at which to insert the passed Records.
13708 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13710 insert : function(index, records){
13711 records = [].concat(records);
13712 for(var i = 0, len = records.length; i < len; i++){
13713 this.data.insert(index, records[i]);
13714 records[i].join(this);
13716 this.fireEvent("add", this, records, index);
13720 * Get the index within the cache of the passed Record.
13721 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13722 * @return {Number} The index of the passed Record. Returns -1 if not found.
13724 indexOf : function(record){
13725 return this.data.indexOf(record);
13729 * Get the index within the cache of the Record with the passed id.
13730 * @param {String} id The id of the Record to find.
13731 * @return {Number} The index of the Record. Returns -1 if not found.
13733 indexOfId : function(id){
13734 return this.data.indexOfKey(id);
13738 * Get the Record with the specified id.
13739 * @param {String} id The id of the Record to find.
13740 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13742 getById : function(id){
13743 return this.data.key(id);
13747 * Get the Record at the specified index.
13748 * @param {Number} index The index of the Record to find.
13749 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13751 getAt : function(index){
13752 return this.data.itemAt(index);
13756 * Returns a range of Records between specified indices.
13757 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13758 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13759 * @return {Roo.data.Record[]} An array of Records
13761 getRange : function(start, end){
13762 return this.data.getRange(start, end);
13766 storeOptions : function(o){
13767 o = Roo.apply({}, o);
13770 this.lastOptions = o;
13774 * Loads the Record cache from the configured Proxy using the configured Reader.
13776 * If using remote paging, then the first load call must specify the <em>start</em>
13777 * and <em>limit</em> properties in the options.params property to establish the initial
13778 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13780 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13781 * and this call will return before the new data has been loaded. Perform any post-processing
13782 * in a callback function, or in a "load" event handler.</strong>
13784 * @param {Object} options An object containing properties which control loading options:<ul>
13785 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13786 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13787 * passed the following arguments:<ul>
13788 * <li>r : Roo.data.Record[]</li>
13789 * <li>options: Options object from the load call</li>
13790 * <li>success: Boolean success indicator</li></ul></li>
13791 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13792 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13795 load : function(options){
13796 options = options || {};
13797 if(this.fireEvent("beforeload", this, options) !== false){
13798 this.storeOptions(options);
13799 var p = Roo.apply(options.params || {}, this.baseParams);
13800 // if meta was not loaded from remote source.. try requesting it.
13801 if (!this.reader.metaFromRemote) {
13802 p._requestMeta = 1;
13804 if(this.sortInfo && this.remoteSort){
13805 var pn = this.paramNames;
13806 p[pn["sort"]] = this.sortInfo.field;
13807 p[pn["dir"]] = this.sortInfo.direction;
13809 if (this.multiSort) {
13810 var pn = this.paramNames;
13811 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13814 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13819 * Reloads the Record cache from the configured Proxy using the configured Reader and
13820 * the options from the last load operation performed.
13821 * @param {Object} options (optional) An object containing properties which may override the options
13822 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13823 * the most recently used options are reused).
13825 reload : function(options){
13826 this.load(Roo.applyIf(options||{}, this.lastOptions));
13830 // Called as a callback by the Reader during a load operation.
13831 loadRecords : function(o, options, success){
13832 if(!o || success === false){
13833 if(success !== false){
13834 this.fireEvent("load", this, [], options, o);
13836 if(options.callback){
13837 options.callback.call(options.scope || this, [], options, false);
13841 // if data returned failure - throw an exception.
13842 if (o.success === false) {
13843 // show a message if no listener is registered.
13844 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13845 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13847 // loadmask wil be hooked into this..
13848 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13851 var r = o.records, t = o.totalRecords || r.length;
13853 this.fireEvent("beforeloadadd", this, r, options, o);
13855 if(!options || options.add !== true){
13856 if(this.pruneModifiedRecords){
13857 this.modified = [];
13859 for(var i = 0, len = r.length; i < len; i++){
13863 this.data = this.snapshot;
13864 delete this.snapshot;
13867 this.data.addAll(r);
13868 this.totalLength = t;
13870 this.fireEvent("datachanged", this);
13872 this.totalLength = Math.max(t, this.data.length+r.length);
13876 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13878 var e = new Roo.data.Record({});
13880 e.set(this.parent.displayField, this.parent.emptyTitle);
13881 e.set(this.parent.valueField, '');
13886 this.fireEvent("load", this, r, options, o);
13887 if(options.callback){
13888 options.callback.call(options.scope || this, r, options, true);
13894 * Loads data from a passed data block. A Reader which understands the format of the data
13895 * must have been configured in the constructor.
13896 * @param {Object} data The data block from which to read the Records. The format of the data expected
13897 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13898 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13900 loadData : function(o, append){
13901 var r = this.reader.readRecords(o);
13902 this.loadRecords(r, {add: append}, true);
13906 * using 'cn' the nested child reader read the child array into it's child stores.
13907 * @param {Object} rec The record with a 'children array
13909 loadDataFromChildren : function(rec)
13911 this.loadData(this.reader.toLoadData(rec));
13916 * Gets the number of cached records.
13918 * <em>If using paging, this may not be the total size of the dataset. If the data object
13919 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13920 * the data set size</em>
13922 getCount : function(){
13923 return this.data.length || 0;
13927 * Gets the total number of records in the dataset as returned by the server.
13929 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13930 * the dataset size</em>
13932 getTotalCount : function(){
13933 return this.totalLength || 0;
13937 * Returns the sort state of the Store as an object with two properties:
13939 field {String} The name of the field by which the Records are sorted
13940 direction {String} The sort order, "ASC" or "DESC"
13943 getSortState : function(){
13944 return this.sortInfo;
13948 applySort : function(){
13949 if(this.sortInfo && !this.remoteSort){
13950 var s = this.sortInfo, f = s.field;
13951 var st = this.fields.get(f).sortType;
13952 var fn = function(r1, r2){
13953 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13954 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13956 this.data.sort(s.direction, fn);
13957 if(this.snapshot && this.snapshot != this.data){
13958 this.snapshot.sort(s.direction, fn);
13964 * Sets the default sort column and order to be used by the next load operation.
13965 * @param {String} fieldName The name of the field to sort by.
13966 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13968 setDefaultSort : function(field, dir){
13969 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13973 * Sort the Records.
13974 * If remote sorting is used, the sort is performed on the server, and the cache is
13975 * reloaded. If local sorting is used, the cache is sorted internally.
13976 * @param {String} fieldName The name of the field to sort by.
13977 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13979 sort : function(fieldName, dir){
13980 var f = this.fields.get(fieldName);
13982 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13984 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13985 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13990 this.sortToggle[f.name] = dir;
13991 this.sortInfo = {field: f.name, direction: dir};
13992 if(!this.remoteSort){
13994 this.fireEvent("datachanged", this);
13996 this.load(this.lastOptions);
14001 * Calls the specified function for each of the Records in the cache.
14002 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14003 * Returning <em>false</em> aborts and exits the iteration.
14004 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14006 each : function(fn, scope){
14007 this.data.each(fn, scope);
14011 * Gets all records modified since the last commit. Modified records are persisted across load operations
14012 * (e.g., during paging).
14013 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14015 getModifiedRecords : function(){
14016 return this.modified;
14020 createFilterFn : function(property, value, anyMatch){
14021 if(!value.exec){ // not a regex
14022 value = String(value);
14023 if(value.length == 0){
14026 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14028 return function(r){
14029 return value.test(r.data[property]);
14034 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14035 * @param {String} property A field on your records
14036 * @param {Number} start The record index to start at (defaults to 0)
14037 * @param {Number} end The last record index to include (defaults to length - 1)
14038 * @return {Number} The sum
14040 sum : function(property, start, end){
14041 var rs = this.data.items, v = 0;
14042 start = start || 0;
14043 end = (end || end === 0) ? end : rs.length-1;
14045 for(var i = start; i <= end; i++){
14046 v += (rs[i].data[property] || 0);
14052 * Filter the records by a specified property.
14053 * @param {String} field A field on your records
14054 * @param {String/RegExp} value Either a string that the field
14055 * should start with or a RegExp to test against the field
14056 * @param {Boolean} anyMatch True to match any part not just the beginning
14058 filter : function(property, value, anyMatch){
14059 var fn = this.createFilterFn(property, value, anyMatch);
14060 return fn ? this.filterBy(fn) : this.clearFilter();
14064 * Filter by a function. The specified function will be called with each
14065 * record in this data source. If the function returns true the record is included,
14066 * otherwise it is filtered.
14067 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14068 * @param {Object} scope (optional) The scope of the function (defaults to this)
14070 filterBy : function(fn, scope){
14071 this.snapshot = this.snapshot || this.data;
14072 this.data = this.queryBy(fn, scope||this);
14073 this.fireEvent("datachanged", this);
14077 * Query the records by a specified property.
14078 * @param {String} field A field on your records
14079 * @param {String/RegExp} value Either a string that the field
14080 * should start with or a RegExp to test against the field
14081 * @param {Boolean} anyMatch True to match any part not just the beginning
14082 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14084 query : function(property, value, anyMatch){
14085 var fn = this.createFilterFn(property, value, anyMatch);
14086 return fn ? this.queryBy(fn) : this.data.clone();
14090 * Query by a function. The specified function will be called with each
14091 * record in this data source. If the function returns true the record is included
14093 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14094 * @param {Object} scope (optional) The scope of the function (defaults to this)
14095 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14097 queryBy : function(fn, scope){
14098 var data = this.snapshot || this.data;
14099 return data.filterBy(fn, scope||this);
14103 * Collects unique values for a particular dataIndex from this store.
14104 * @param {String} dataIndex The property to collect
14105 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14106 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14107 * @return {Array} An array of the unique values
14109 collect : function(dataIndex, allowNull, bypassFilter){
14110 var d = (bypassFilter === true && this.snapshot) ?
14111 this.snapshot.items : this.data.items;
14112 var v, sv, r = [], l = {};
14113 for(var i = 0, len = d.length; i < len; i++){
14114 v = d[i].data[dataIndex];
14116 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14125 * Revert to a view of the Record cache with no filtering applied.
14126 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14128 clearFilter : function(suppressEvent){
14129 if(this.snapshot && this.snapshot != this.data){
14130 this.data = this.snapshot;
14131 delete this.snapshot;
14132 if(suppressEvent !== true){
14133 this.fireEvent("datachanged", this);
14139 afterEdit : function(record){
14140 if(this.modified.indexOf(record) == -1){
14141 this.modified.push(record);
14143 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14147 afterReject : function(record){
14148 this.modified.remove(record);
14149 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14153 afterCommit : function(record){
14154 this.modified.remove(record);
14155 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14159 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14160 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14162 commitChanges : function(){
14163 var m = this.modified.slice(0);
14164 this.modified = [];
14165 for(var i = 0, len = m.length; i < len; i++){
14171 * Cancel outstanding changes on all changed records.
14173 rejectChanges : function(){
14174 var m = this.modified.slice(0);
14175 this.modified = [];
14176 for(var i = 0, len = m.length; i < len; i++){
14181 onMetaChange : function(meta, rtype, o){
14182 this.recordType = rtype;
14183 this.fields = rtype.prototype.fields;
14184 delete this.snapshot;
14185 this.sortInfo = meta.sortInfo || this.sortInfo;
14186 this.modified = [];
14187 this.fireEvent('metachange', this, this.reader.meta);
14190 moveIndex : function(data, type)
14192 var index = this.indexOf(data);
14194 var newIndex = index + type;
14198 this.insert(newIndex, data);
14203 * Ext JS Library 1.1.1
14204 * Copyright(c) 2006-2007, Ext JS, LLC.
14206 * Originally Released Under LGPL - original licence link has changed is not relivant.
14209 * <script type="text/javascript">
14213 * @class Roo.data.SimpleStore
14214 * @extends Roo.data.Store
14215 * Small helper class to make creating Stores from Array data easier.
14216 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14217 * @cfg {Array} fields An array of field definition objects, or field name strings.
14218 * @cfg {Object} an existing reader (eg. copied from another store)
14219 * @cfg {Array} data The multi-dimensional array of data
14221 * @param {Object} config
14223 Roo.data.SimpleStore = function(config)
14225 Roo.data.SimpleStore.superclass.constructor.call(this, {
14227 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14230 Roo.data.Record.create(config.fields)
14232 proxy : new Roo.data.MemoryProxy(config.data)
14236 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14238 * Ext JS Library 1.1.1
14239 * Copyright(c) 2006-2007, Ext JS, LLC.
14241 * Originally Released Under LGPL - original licence link has changed is not relivant.
14244 * <script type="text/javascript">
14249 * @extends Roo.data.Store
14250 * @class Roo.data.JsonStore
14251 * Small helper class to make creating Stores for JSON data easier. <br/>
14253 var store = new Roo.data.JsonStore({
14254 url: 'get-images.php',
14256 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14259 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14260 * JsonReader and HttpProxy (unless inline data is provided).</b>
14261 * @cfg {Array} fields An array of field definition objects, or field name strings.
14263 * @param {Object} config
14265 Roo.data.JsonStore = function(c){
14266 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14267 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14268 reader: new Roo.data.JsonReader(c, c.fields)
14271 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14273 * Ext JS Library 1.1.1
14274 * Copyright(c) 2006-2007, Ext JS, LLC.
14276 * Originally Released Under LGPL - original licence link has changed is not relivant.
14279 * <script type="text/javascript">
14283 Roo.data.Field = function(config){
14284 if(typeof config == "string"){
14285 config = {name: config};
14287 Roo.apply(this, config);
14290 this.type = "auto";
14293 var st = Roo.data.SortTypes;
14294 // named sortTypes are supported, here we look them up
14295 if(typeof this.sortType == "string"){
14296 this.sortType = st[this.sortType];
14299 // set default sortType for strings and dates
14300 if(!this.sortType){
14303 this.sortType = st.asUCString;
14306 this.sortType = st.asDate;
14309 this.sortType = st.none;
14314 var stripRe = /[\$,%]/g;
14316 // prebuilt conversion function for this field, instead of
14317 // switching every time we're reading a value
14319 var cv, dateFormat = this.dateFormat;
14324 cv = function(v){ return v; };
14327 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14331 return v !== undefined && v !== null && v !== '' ?
14332 parseInt(String(v).replace(stripRe, ""), 10) : '';
14337 return v !== undefined && v !== null && v !== '' ?
14338 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14343 cv = function(v){ return v === true || v === "true" || v == 1; };
14350 if(v instanceof Date){
14354 if(dateFormat == "timestamp"){
14355 return new Date(v*1000);
14357 return Date.parseDate(v, dateFormat);
14359 var parsed = Date.parse(v);
14360 return parsed ? new Date(parsed) : null;
14369 Roo.data.Field.prototype = {
14377 * Ext JS Library 1.1.1
14378 * Copyright(c) 2006-2007, Ext JS, LLC.
14380 * Originally Released Under LGPL - original licence link has changed is not relivant.
14383 * <script type="text/javascript">
14386 // Base class for reading structured data from a data source. This class is intended to be
14387 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14390 * @class Roo.data.DataReader
14391 * Base class for reading structured data from a data source. This class is intended to be
14392 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14395 Roo.data.DataReader = function(meta, recordType){
14399 this.recordType = recordType instanceof Array ?
14400 Roo.data.Record.create(recordType) : recordType;
14403 Roo.data.DataReader.prototype = {
14406 readerType : 'Data',
14408 * Create an empty record
14409 * @param {Object} data (optional) - overlay some values
14410 * @return {Roo.data.Record} record created.
14412 newRow : function(d) {
14414 this.recordType.prototype.fields.each(function(c) {
14416 case 'int' : da[c.name] = 0; break;
14417 case 'date' : da[c.name] = new Date(); break;
14418 case 'float' : da[c.name] = 0.0; break;
14419 case 'boolean' : da[c.name] = false; break;
14420 default : da[c.name] = ""; break;
14424 return new this.recordType(Roo.apply(da, d));
14430 * Ext JS Library 1.1.1
14431 * Copyright(c) 2006-2007, Ext JS, LLC.
14433 * Originally Released Under LGPL - original licence link has changed is not relivant.
14436 * <script type="text/javascript">
14440 * @class Roo.data.DataProxy
14441 * @extends Roo.data.Observable
14442 * This class is an abstract base class for implementations which provide retrieval of
14443 * unformatted data objects.<br>
14445 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14446 * (of the appropriate type which knows how to parse the data object) to provide a block of
14447 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14449 * Custom implementations must implement the load method as described in
14450 * {@link Roo.data.HttpProxy#load}.
14452 Roo.data.DataProxy = function(){
14455 * @event beforeload
14456 * Fires before a network request is made to retrieve a data object.
14457 * @param {Object} This DataProxy object.
14458 * @param {Object} params The params parameter to the load function.
14463 * Fires before the load method's callback is called.
14464 * @param {Object} This DataProxy object.
14465 * @param {Object} o The data object.
14466 * @param {Object} arg The callback argument object passed to the load function.
14470 * @event loadexception
14471 * Fires if an Exception occurs during data retrieval.
14472 * @param {Object} This DataProxy object.
14473 * @param {Object} o The data object.
14474 * @param {Object} arg The callback argument object passed to the load function.
14475 * @param {Object} e The Exception.
14477 loadexception : true
14479 Roo.data.DataProxy.superclass.constructor.call(this);
14482 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14485 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14489 * Ext JS Library 1.1.1
14490 * Copyright(c) 2006-2007, Ext JS, LLC.
14492 * Originally Released Under LGPL - original licence link has changed is not relivant.
14495 * <script type="text/javascript">
14498 * @class Roo.data.MemoryProxy
14499 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14500 * to the Reader when its load method is called.
14502 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14504 Roo.data.MemoryProxy = function(data){
14508 Roo.data.MemoryProxy.superclass.constructor.call(this);
14512 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14515 * Load data from the requested source (in this case an in-memory
14516 * data object passed to the constructor), read the data object into
14517 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14518 * process that block using the passed callback.
14519 * @param {Object} params This parameter is not used by the MemoryProxy class.
14520 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14521 * object into a block of Roo.data.Records.
14522 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14523 * The function must be passed <ul>
14524 * <li>The Record block object</li>
14525 * <li>The "arg" argument from the load function</li>
14526 * <li>A boolean success indicator</li>
14528 * @param {Object} scope The scope in which to call the callback
14529 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14531 load : function(params, reader, callback, scope, arg){
14532 params = params || {};
14535 result = reader.readRecords(params.data ? params.data :this.data);
14537 this.fireEvent("loadexception", this, arg, null, e);
14538 callback.call(scope, null, arg, false);
14541 callback.call(scope, result, arg, true);
14545 update : function(params, records){
14550 * Ext JS Library 1.1.1
14551 * Copyright(c) 2006-2007, Ext JS, LLC.
14553 * Originally Released Under LGPL - original licence link has changed is not relivant.
14556 * <script type="text/javascript">
14559 * @class Roo.data.HttpProxy
14560 * @extends Roo.data.DataProxy
14561 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14562 * configured to reference a certain URL.<br><br>
14564 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14565 * from which the running page was served.<br><br>
14567 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14569 * Be aware that to enable the browser to parse an XML document, the server must set
14570 * the Content-Type header in the HTTP response to "text/xml".
14572 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14573 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14574 * will be used to make the request.
14576 Roo.data.HttpProxy = function(conn){
14577 Roo.data.HttpProxy.superclass.constructor.call(this);
14578 // is conn a conn config or a real conn?
14580 this.useAjax = !conn || !conn.events;
14584 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14585 // thse are take from connection...
14588 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14591 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14592 * extra parameters to each request made by this object. (defaults to undefined)
14595 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14596 * to each request made by this object. (defaults to undefined)
14599 * @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)
14602 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14605 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14611 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14615 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14616 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14617 * a finer-grained basis than the DataProxy events.
14619 getConnection : function(){
14620 return this.useAjax ? Roo.Ajax : this.conn;
14624 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14625 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14626 * process that block using the passed callback.
14627 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14628 * for the request to the remote server.
14629 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14630 * object into a block of Roo.data.Records.
14631 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14632 * The function must be passed <ul>
14633 * <li>The Record block object</li>
14634 * <li>The "arg" argument from the load function</li>
14635 * <li>A boolean success indicator</li>
14637 * @param {Object} scope The scope in which to call the callback
14638 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14640 load : function(params, reader, callback, scope, arg){
14641 if(this.fireEvent("beforeload", this, params) !== false){
14643 params : params || {},
14645 callback : callback,
14650 callback : this.loadResponse,
14654 Roo.applyIf(o, this.conn);
14655 if(this.activeRequest){
14656 Roo.Ajax.abort(this.activeRequest);
14658 this.activeRequest = Roo.Ajax.request(o);
14660 this.conn.request(o);
14663 callback.call(scope||this, null, arg, false);
14668 loadResponse : function(o, success, response){
14669 delete this.activeRequest;
14671 this.fireEvent("loadexception", this, o, response);
14672 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14677 result = o.reader.read(response);
14679 this.fireEvent("loadexception", this, o, response, e);
14680 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14684 this.fireEvent("load", this, o, o.request.arg);
14685 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14689 update : function(dataSet){
14694 updateResponse : function(dataSet){
14699 * Ext JS Library 1.1.1
14700 * Copyright(c) 2006-2007, Ext JS, LLC.
14702 * Originally Released Under LGPL - original licence link has changed is not relivant.
14705 * <script type="text/javascript">
14709 * @class Roo.data.ScriptTagProxy
14710 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14711 * other than the originating domain of the running page.<br><br>
14713 * <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
14714 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14716 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14717 * source code that is used as the source inside a <script> tag.<br><br>
14719 * In order for the browser to process the returned data, the server must wrap the data object
14720 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14721 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14722 * depending on whether the callback name was passed:
14725 boolean scriptTag = false;
14726 String cb = request.getParameter("callback");
14729 response.setContentType("text/javascript");
14731 response.setContentType("application/x-json");
14733 Writer out = response.getWriter();
14735 out.write(cb + "(");
14737 out.print(dataBlock.toJsonString());
14744 * @param {Object} config A configuration object.
14746 Roo.data.ScriptTagProxy = function(config){
14747 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14748 Roo.apply(this, config);
14749 this.head = document.getElementsByTagName("head")[0];
14752 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14754 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14756 * @cfg {String} url The URL from which to request the data object.
14759 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14763 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14764 * the server the name of the callback function set up by the load call to process the returned data object.
14765 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14766 * javascript output which calls this named function passing the data object as its only parameter.
14768 callbackParam : "callback",
14770 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14771 * name to the request.
14776 * Load data from the configured URL, read the data object into
14777 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14778 * process that block using the passed callback.
14779 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14780 * for the request to the remote server.
14781 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14782 * object into a block of Roo.data.Records.
14783 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14784 * The function must be passed <ul>
14785 * <li>The Record block object</li>
14786 * <li>The "arg" argument from the load function</li>
14787 * <li>A boolean success indicator</li>
14789 * @param {Object} scope The scope in which to call the callback
14790 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14792 load : function(params, reader, callback, scope, arg){
14793 if(this.fireEvent("beforeload", this, params) !== false){
14795 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14797 var url = this.url;
14798 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14800 url += "&_dc=" + (new Date().getTime());
14802 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14805 cb : "stcCallback"+transId,
14806 scriptId : "stcScript"+transId,
14810 callback : callback,
14816 window[trans.cb] = function(o){
14817 conn.handleResponse(o, trans);
14820 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14822 if(this.autoAbort !== false){
14826 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14828 var script = document.createElement("script");
14829 script.setAttribute("src", url);
14830 script.setAttribute("type", "text/javascript");
14831 script.setAttribute("id", trans.scriptId);
14832 this.head.appendChild(script);
14834 this.trans = trans;
14836 callback.call(scope||this, null, arg, false);
14841 isLoading : function(){
14842 return this.trans ? true : false;
14846 * Abort the current server request.
14848 abort : function(){
14849 if(this.isLoading()){
14850 this.destroyTrans(this.trans);
14855 destroyTrans : function(trans, isLoaded){
14856 this.head.removeChild(document.getElementById(trans.scriptId));
14857 clearTimeout(trans.timeoutId);
14859 window[trans.cb] = undefined;
14861 delete window[trans.cb];
14864 // if hasn't been loaded, wait for load to remove it to prevent script error
14865 window[trans.cb] = function(){
14866 window[trans.cb] = undefined;
14868 delete window[trans.cb];
14875 handleResponse : function(o, trans){
14876 this.trans = false;
14877 this.destroyTrans(trans, true);
14880 result = trans.reader.readRecords(o);
14882 this.fireEvent("loadexception", this, o, trans.arg, e);
14883 trans.callback.call(trans.scope||window, null, trans.arg, false);
14886 this.fireEvent("load", this, o, trans.arg);
14887 trans.callback.call(trans.scope||window, result, trans.arg, true);
14891 handleFailure : function(trans){
14892 this.trans = false;
14893 this.destroyTrans(trans, false);
14894 this.fireEvent("loadexception", this, null, trans.arg);
14895 trans.callback.call(trans.scope||window, null, trans.arg, false);
14899 * Ext JS Library 1.1.1
14900 * Copyright(c) 2006-2007, Ext JS, LLC.
14902 * Originally Released Under LGPL - original licence link has changed is not relivant.
14905 * <script type="text/javascript">
14909 * @class Roo.data.JsonReader
14910 * @extends Roo.data.DataReader
14911 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14912 * based on mappings in a provided Roo.data.Record constructor.
14914 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14915 * in the reply previously.
14920 var RecordDef = Roo.data.Record.create([
14921 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14922 {name: 'occupation'} // This field will use "occupation" as the mapping.
14924 var myReader = new Roo.data.JsonReader({
14925 totalProperty: "results", // The property which contains the total dataset size (optional)
14926 root: "rows", // The property which contains an Array of row objects
14927 id: "id" // The property within each row object that provides an ID for the record (optional)
14931 * This would consume a JSON file like this:
14933 { 'results': 2, 'rows': [
14934 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14935 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14938 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14939 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14940 * paged from the remote server.
14941 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14942 * @cfg {String} root name of the property which contains the Array of row objects.
14943 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14944 * @cfg {Array} fields Array of field definition objects
14946 * Create a new JsonReader
14947 * @param {Object} meta Metadata configuration options
14948 * @param {Object} recordType Either an Array of field definition objects,
14949 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14951 Roo.data.JsonReader = function(meta, recordType){
14954 // set some defaults:
14955 Roo.applyIf(meta, {
14956 totalProperty: 'total',
14957 successProperty : 'success',
14962 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14964 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14966 readerType : 'Json',
14969 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14970 * Used by Store query builder to append _requestMeta to params.
14973 metaFromRemote : false,
14975 * This method is only used by a DataProxy which has retrieved data from a remote server.
14976 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14977 * @return {Object} data A data block which is used by an Roo.data.Store object as
14978 * a cache of Roo.data.Records.
14980 read : function(response){
14981 var json = response.responseText;
14983 var o = /* eval:var:o */ eval("("+json+")");
14985 throw {message: "JsonReader.read: Json object not found"};
14991 this.metaFromRemote = true;
14992 this.meta = o.metaData;
14993 this.recordType = Roo.data.Record.create(o.metaData.fields);
14994 this.onMetaChange(this.meta, this.recordType, o);
14996 return this.readRecords(o);
14999 // private function a store will implement
15000 onMetaChange : function(meta, recordType, o){
15007 simpleAccess: function(obj, subsc) {
15014 getJsonAccessor: function(){
15016 return function(expr) {
15018 return(re.test(expr))
15019 ? new Function("obj", "return obj." + expr)
15024 return Roo.emptyFn;
15029 * Create a data block containing Roo.data.Records from an XML document.
15030 * @param {Object} o An object which contains an Array of row objects in the property specified
15031 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15032 * which contains the total size of the dataset.
15033 * @return {Object} data A data block which is used by an Roo.data.Store object as
15034 * a cache of Roo.data.Records.
15036 readRecords : function(o){
15038 * After any data loads, the raw JSON data is available for further custom processing.
15042 var s = this.meta, Record = this.recordType,
15043 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15045 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15047 if(s.totalProperty) {
15048 this.getTotal = this.getJsonAccessor(s.totalProperty);
15050 if(s.successProperty) {
15051 this.getSuccess = this.getJsonAccessor(s.successProperty);
15053 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15055 var g = this.getJsonAccessor(s.id);
15056 this.getId = function(rec) {
15058 return (r === undefined || r === "") ? null : r;
15061 this.getId = function(){return null;};
15064 for(var jj = 0; jj < fl; jj++){
15066 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15067 this.ef[jj] = this.getJsonAccessor(map);
15071 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15072 if(s.totalProperty){
15073 var vt = parseInt(this.getTotal(o), 10);
15078 if(s.successProperty){
15079 var vs = this.getSuccess(o);
15080 if(vs === false || vs === 'false'){
15085 for(var i = 0; i < c; i++){
15088 var id = this.getId(n);
15089 for(var j = 0; j < fl; j++){
15091 var v = this.ef[j](n);
15093 Roo.log('missing convert for ' + f.name);
15097 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15099 var record = new Record(values, id);
15101 records[i] = record;
15107 totalRecords : totalRecords
15110 // used when loading children.. @see loadDataFromChildren
15111 toLoadData: function(rec)
15113 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15114 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15115 return { data : data, total : data.length };
15120 * Ext JS Library 1.1.1
15121 * Copyright(c) 2006-2007, Ext JS, LLC.
15123 * Originally Released Under LGPL - original licence link has changed is not relivant.
15126 * <script type="text/javascript">
15130 * @class Roo.data.ArrayReader
15131 * @extends Roo.data.DataReader
15132 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15133 * Each element of that Array represents a row of data fields. The
15134 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15135 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15139 var RecordDef = Roo.data.Record.create([
15140 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15141 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15143 var myReader = new Roo.data.ArrayReader({
15144 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15148 * This would consume an Array like this:
15150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15154 * Create a new JsonReader
15155 * @param {Object} meta Metadata configuration options.
15156 * @param {Object|Array} recordType Either an Array of field definition objects
15158 * @cfg {Array} fields Array of field definition objects
15159 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15160 * as specified to {@link Roo.data.Record#create},
15161 * or an {@link Roo.data.Record} object
15164 * created using {@link Roo.data.Record#create}.
15166 Roo.data.ArrayReader = function(meta, recordType)
15168 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15174 * Create a data block containing Roo.data.Records from an XML document.
15175 * @param {Object} o An Array of row objects which represents the dataset.
15176 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15177 * a cache of Roo.data.Records.
15179 readRecords : function(o)
15181 var sid = this.meta ? this.meta.id : null;
15182 var recordType = this.recordType, fields = recordType.prototype.fields;
15185 for(var i = 0; i < root.length; i++){
15188 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15189 for(var j = 0, jlen = fields.length; j < jlen; j++){
15190 var f = fields.items[j];
15191 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15192 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15194 values[f.name] = v;
15196 var record = new recordType(values, id);
15198 records[records.length] = record;
15202 totalRecords : records.length
15205 // used when loading children.. @see loadDataFromChildren
15206 toLoadData: function(rec)
15208 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15209 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15220 * @class Roo.bootstrap.ComboBox
15221 * @extends Roo.bootstrap.TriggerField
15222 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15223 * @cfg {Boolean} append (true|false) default false
15224 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15225 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15226 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15227 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15228 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15229 * @cfg {Boolean} animate default true
15230 * @cfg {Boolean} emptyResultText only for touch device
15231 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15232 * @cfg {String} emptyTitle default ''
15233 * @cfg {Number} width fixed with? experimental
15235 * Create a new ComboBox.
15236 * @param {Object} config Configuration options
15238 Roo.bootstrap.ComboBox = function(config){
15239 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15243 * Fires when the dropdown list is expanded
15244 * @param {Roo.bootstrap.ComboBox} combo This combo box
15249 * Fires when the dropdown list is collapsed
15250 * @param {Roo.bootstrap.ComboBox} combo This combo box
15254 * @event beforeselect
15255 * Fires before a list item is selected. Return false to cancel the selection.
15256 * @param {Roo.bootstrap.ComboBox} combo This combo box
15257 * @param {Roo.data.Record} record The data record returned from the underlying store
15258 * @param {Number} index The index of the selected item in the dropdown list
15260 'beforeselect' : true,
15263 * Fires when a list item is selected
15264 * @param {Roo.bootstrap.ComboBox} combo This combo box
15265 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15266 * @param {Number} index The index of the selected item in the dropdown list
15270 * @event beforequery
15271 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15272 * The event object passed has these properties:
15273 * @param {Roo.bootstrap.ComboBox} combo This combo box
15274 * @param {String} query The query
15275 * @param {Boolean} forceAll true to force "all" query
15276 * @param {Boolean} cancel true to cancel the query
15277 * @param {Object} e The query event object
15279 'beforequery': true,
15282 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15283 * @param {Roo.bootstrap.ComboBox} combo This combo box
15288 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15289 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15295 * Fires when the remove value from the combobox array
15296 * @param {Roo.bootstrap.ComboBox} combo This combo box
15300 * @event afterremove
15301 * Fires when the remove value from the combobox array
15302 * @param {Roo.bootstrap.ComboBox} combo This combo box
15304 'afterremove' : true,
15306 * @event specialfilter
15307 * Fires when specialfilter
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15310 'specialfilter' : true,
15313 * Fires when tick the element
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15318 * @event touchviewdisplay
15319 * Fires when touch view require special display (default is using displayField)
15320 * @param {Roo.bootstrap.ComboBox} combo This combo box
15321 * @param {Object} cfg set html .
15323 'touchviewdisplay' : true
15328 this.tickItems = [];
15330 this.selectedIndex = -1;
15331 if(this.mode == 'local'){
15332 if(config.queryDelay === undefined){
15333 this.queryDelay = 10;
15335 if(config.minChars === undefined){
15341 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15344 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15345 * rendering into an Roo.Editor, defaults to false)
15348 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15349 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15352 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15355 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15356 * the dropdown list (defaults to undefined, with no header element)
15360 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15364 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15366 listWidth: undefined,
15368 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15369 * mode = 'remote' or 'text' if mode = 'local')
15371 displayField: undefined,
15374 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15375 * mode = 'remote' or 'value' if mode = 'local').
15376 * Note: use of a valueField requires the user make a selection
15377 * in order for a value to be mapped.
15379 valueField: undefined,
15381 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15386 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15387 * field's data value (defaults to the underlying DOM element's name)
15389 hiddenName: undefined,
15391 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15395 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15397 selectedClass: 'active',
15400 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15404 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15405 * anchor positions (defaults to 'tl-bl')
15407 listAlign: 'tl-bl?',
15409 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15413 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15414 * query specified by the allQuery config option (defaults to 'query')
15416 triggerAction: 'query',
15418 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15419 * (defaults to 4, does not apply if editable = false)
15423 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15424 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15428 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15429 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15433 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15434 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15438 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15439 * when editable = true (defaults to false)
15441 selectOnFocus:false,
15443 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15445 queryParam: 'query',
15447 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15448 * when mode = 'remote' (defaults to 'Loading...')
15450 loadingText: 'Loading...',
15452 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15456 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15460 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15461 * traditional select (defaults to true)
15465 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15469 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15473 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15474 * listWidth has a higher value)
15478 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15479 * allow the user to set arbitrary text into the field (defaults to false)
15481 forceSelection:false,
15483 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15484 * if typeAhead = true (defaults to 250)
15486 typeAheadDelay : 250,
15488 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15489 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15491 valueNotFoundText : undefined,
15493 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15495 blockFocus : false,
15498 * @cfg {Boolean} disableClear Disable showing of clear button.
15500 disableClear : false,
15502 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15504 alwaysQuery : false,
15507 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15512 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15514 invalidClass : "has-warning",
15517 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15519 validClass : "has-success",
15522 * @cfg {Boolean} specialFilter (true|false) special filter default false
15524 specialFilter : false,
15527 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15529 mobileTouchView : true,
15532 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15534 useNativeIOS : false,
15537 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15539 mobile_restrict_height : false,
15541 ios_options : false,
15553 btnPosition : 'right',
15554 triggerList : true,
15555 showToggleBtn : true,
15557 emptyResultText: 'Empty',
15558 triggerText : 'Select',
15562 // element that contains real text value.. (when hidden is used..)
15564 getAutoCreate : function()
15569 * Render classic select for iso
15572 if(Roo.isIOS && this.useNativeIOS){
15573 cfg = this.getAutoCreateNativeIOS();
15581 if(Roo.isTouch && this.mobileTouchView){
15582 cfg = this.getAutoCreateTouchView();
15589 if(!this.tickable){
15590 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15595 * ComboBox with tickable selections
15598 var align = this.labelAlign || this.parentLabelAlign();
15601 cls : 'form-group roo-combobox-tickable' //input-group
15604 var btn_text_select = '';
15605 var btn_text_done = '';
15606 var btn_text_cancel = '';
15608 if (this.btn_text_show) {
15609 btn_text_select = 'Select';
15610 btn_text_done = 'Done';
15611 btn_text_cancel = 'Cancel';
15616 cls : 'tickable-buttons',
15621 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15622 //html : this.triggerText
15623 html: btn_text_select
15629 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15631 html: btn_text_done
15637 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15639 html: btn_text_cancel
15645 buttons.cn.unshift({
15647 cls: 'roo-select2-search-field-input'
15653 Roo.each(buttons.cn, function(c){
15655 c.cls += ' btn-' + _this.size;
15658 if (_this.disabled) {
15665 style : 'display: contents',
15670 cls: 'form-hidden-field'
15674 cls: 'roo-select2-choices',
15678 cls: 'roo-select2-search-field',
15689 cls: 'roo-select2-container input-group roo-select2-container-multi',
15695 // cls: 'typeahead typeahead-long dropdown-menu',
15696 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15701 if(this.hasFeedback && !this.allowBlank){
15705 cls: 'glyphicon form-control-feedback'
15708 combobox.cn.push(feedback);
15715 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15716 tooltip : 'This field is required'
15718 if (Roo.bootstrap.version == 4) {
15721 style : 'display:none'
15724 if (align ==='left' && this.fieldLabel.length) {
15726 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15733 cls : 'control-label col-form-label',
15734 html : this.fieldLabel
15746 var labelCfg = cfg.cn[1];
15747 var contentCfg = cfg.cn[2];
15750 if(this.indicatorpos == 'right'){
15756 cls : 'control-label col-form-label',
15760 html : this.fieldLabel
15776 labelCfg = cfg.cn[0];
15777 contentCfg = cfg.cn[1];
15781 if(this.labelWidth > 12){
15782 labelCfg.style = "width: " + this.labelWidth + 'px';
15784 if(this.width * 1 > 0){
15785 contentCfg.style = "width: " + this.width + 'px';
15787 if(this.labelWidth < 13 && this.labelmd == 0){
15788 this.labelmd = this.labelWidth;
15791 if(this.labellg > 0){
15792 labelCfg.cls += ' col-lg-' + this.labellg;
15793 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15796 if(this.labelmd > 0){
15797 labelCfg.cls += ' col-md-' + this.labelmd;
15798 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15801 if(this.labelsm > 0){
15802 labelCfg.cls += ' col-sm-' + this.labelsm;
15803 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15806 if(this.labelxs > 0){
15807 labelCfg.cls += ' col-xs-' + this.labelxs;
15808 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15812 } else if ( this.fieldLabel.length) {
15813 // Roo.log(" label");
15818 //cls : 'input-group-addon',
15819 html : this.fieldLabel
15824 if(this.indicatorpos == 'right'){
15828 //cls : 'input-group-addon',
15829 html : this.fieldLabel
15839 // Roo.log(" no label && no align");
15846 ['xs','sm','md','lg'].map(function(size){
15847 if (settings[size]) {
15848 cfg.cls += ' col-' + size + '-' + settings[size];
15856 _initEventsCalled : false,
15859 initEvents: function()
15861 if (this._initEventsCalled) { // as we call render... prevent looping...
15864 this._initEventsCalled = true;
15867 throw "can not find store for combo";
15870 this.indicator = this.indicatorEl();
15872 this.store = Roo.factory(this.store, Roo.data);
15873 this.store.parent = this;
15875 // if we are building from html. then this element is so complex, that we can not really
15876 // use the rendered HTML.
15877 // so we have to trash and replace the previous code.
15878 if (Roo.XComponent.build_from_html) {
15879 // remove this element....
15880 var e = this.el.dom, k=0;
15881 while (e ) { e = e.previousSibling; ++k;}
15886 this.rendered = false;
15888 this.render(this.parent().getChildContainer(true), k);
15891 if(Roo.isIOS && this.useNativeIOS){
15892 this.initIOSView();
15900 if(Roo.isTouch && this.mobileTouchView){
15901 this.initTouchView();
15906 this.initTickableEvents();
15910 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15912 if(this.hiddenName){
15914 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15916 this.hiddenField.dom.value =
15917 this.hiddenValue !== undefined ? this.hiddenValue :
15918 this.value !== undefined ? this.value : '';
15920 // prevent input submission
15921 this.el.dom.removeAttribute('name');
15922 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15927 // this.el.dom.setAttribute('autocomplete', 'off');
15930 var cls = 'x-combo-list';
15932 //this.list = new Roo.Layer({
15933 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15939 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15940 _this.list.setWidth(lw);
15943 this.list.on('mouseover', this.onViewOver, this);
15944 this.list.on('mousemove', this.onViewMove, this);
15945 this.list.on('scroll', this.onViewScroll, this);
15948 this.list.swallowEvent('mousewheel');
15949 this.assetHeight = 0;
15952 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15953 this.assetHeight += this.header.getHeight();
15956 this.innerList = this.list.createChild({cls:cls+'-inner'});
15957 this.innerList.on('mouseover', this.onViewOver, this);
15958 this.innerList.on('mousemove', this.onViewMove, this);
15959 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15961 if(this.allowBlank && !this.pageSize && !this.disableClear){
15962 this.footer = this.list.createChild({cls:cls+'-ft'});
15963 this.pageTb = new Roo.Toolbar(this.footer);
15967 this.footer = this.list.createChild({cls:cls+'-ft'});
15968 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15969 {pageSize: this.pageSize});
15973 if (this.pageTb && this.allowBlank && !this.disableClear) {
15975 this.pageTb.add(new Roo.Toolbar.Fill(), {
15976 cls: 'x-btn-icon x-btn-clear',
15978 handler: function()
15981 _this.clearValue();
15982 _this.onSelect(false, -1);
15987 this.assetHeight += this.footer.getHeight();
15992 this.tpl = Roo.bootstrap.version == 4 ?
15993 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15994 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15997 this.view = new Roo.View(this.list, this.tpl, {
15998 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16000 //this.view.wrapEl.setDisplayed(false);
16001 this.view.on('click', this.onViewClick, this);
16004 this.store.on('beforeload', this.onBeforeLoad, this);
16005 this.store.on('load', this.onLoad, this);
16006 this.store.on('loadexception', this.onLoadException, this);
16008 if(this.resizable){
16009 this.resizer = new Roo.Resizable(this.list, {
16010 pinned:true, handles:'se'
16012 this.resizer.on('resize', function(r, w, h){
16013 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16014 this.listWidth = w;
16015 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16016 this.restrictHeight();
16018 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16021 if(!this.editable){
16022 this.editable = true;
16023 this.setEditable(false);
16028 if (typeof(this.events.add.listeners) != 'undefined') {
16030 this.addicon = this.wrap.createChild(
16031 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16033 this.addicon.on('click', function(e) {
16034 this.fireEvent('add', this);
16037 if (typeof(this.events.edit.listeners) != 'undefined') {
16039 this.editicon = this.wrap.createChild(
16040 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16041 if (this.addicon) {
16042 this.editicon.setStyle('margin-left', '40px');
16044 this.editicon.on('click', function(e) {
16046 // we fire even if inothing is selected..
16047 this.fireEvent('edit', this, this.lastData );
16053 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16054 "up" : function(e){
16055 this.inKeyMode = true;
16059 "down" : function(e){
16060 if(!this.isExpanded()){
16061 this.onTriggerClick();
16063 this.inKeyMode = true;
16068 "enter" : function(e){
16069 // this.onViewClick();
16073 if(this.fireEvent("specialkey", this, e)){
16074 this.onViewClick(false);
16080 "esc" : function(e){
16084 "tab" : function(e){
16087 if(this.fireEvent("specialkey", this, e)){
16088 this.onViewClick(false);
16096 doRelay : function(foo, bar, hname){
16097 if(hname == 'down' || this.scope.isExpanded()){
16098 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16107 this.queryDelay = Math.max(this.queryDelay || 10,
16108 this.mode == 'local' ? 10 : 250);
16111 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16113 if(this.typeAhead){
16114 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16116 if(this.editable !== false){
16117 this.inputEl().on("keyup", this.onKeyUp, this);
16119 if(this.forceSelection){
16120 this.inputEl().on('blur', this.doForce, this);
16124 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16125 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16129 initTickableEvents: function()
16133 if(this.hiddenName){
16135 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16137 this.hiddenField.dom.value =
16138 this.hiddenValue !== undefined ? this.hiddenValue :
16139 this.value !== undefined ? this.value : '';
16141 // prevent input submission
16142 this.el.dom.removeAttribute('name');
16143 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16148 // this.list = this.el.select('ul.dropdown-menu',true).first();
16150 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16151 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16152 if(this.triggerList){
16153 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16156 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16157 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16159 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16160 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16162 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16163 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16165 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16166 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16167 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16170 this.cancelBtn.hide();
16175 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16176 _this.list.setWidth(lw);
16179 this.list.on('mouseover', this.onViewOver, this);
16180 this.list.on('mousemove', this.onViewMove, this);
16182 this.list.on('scroll', this.onViewScroll, this);
16185 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16186 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16189 this.view = new Roo.View(this.list, this.tpl, {
16194 selectedClass: this.selectedClass
16197 //this.view.wrapEl.setDisplayed(false);
16198 this.view.on('click', this.onViewClick, this);
16202 this.store.on('beforeload', this.onBeforeLoad, this);
16203 this.store.on('load', this.onLoad, this);
16204 this.store.on('loadexception', this.onLoadException, this);
16207 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16208 "up" : function(e){
16209 this.inKeyMode = true;
16213 "down" : function(e){
16214 this.inKeyMode = true;
16218 "enter" : function(e){
16219 if(this.fireEvent("specialkey", this, e)){
16220 this.onViewClick(false);
16226 "esc" : function(e){
16227 this.onTickableFooterButtonClick(e, false, false);
16230 "tab" : function(e){
16231 this.fireEvent("specialkey", this, e);
16233 this.onTickableFooterButtonClick(e, false, false);
16240 doRelay : function(e, fn, key){
16241 if(this.scope.isExpanded()){
16242 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16251 this.queryDelay = Math.max(this.queryDelay || 10,
16252 this.mode == 'local' ? 10 : 250);
16255 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16257 if(this.typeAhead){
16258 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16261 if(this.editable !== false){
16262 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16265 this.indicator = this.indicatorEl();
16267 if(this.indicator){
16268 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16269 this.indicator.hide();
16274 onDestroy : function(){
16276 this.view.setStore(null);
16277 this.view.el.removeAllListeners();
16278 this.view.el.remove();
16279 this.view.purgeListeners();
16282 this.list.dom.innerHTML = '';
16286 this.store.un('beforeload', this.onBeforeLoad, this);
16287 this.store.un('load', this.onLoad, this);
16288 this.store.un('loadexception', this.onLoadException, this);
16290 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16294 fireKey : function(e){
16295 if(e.isNavKeyPress() && !this.list.isVisible()){
16296 this.fireEvent("specialkey", this, e);
16301 onResize: function(w, h)
16305 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16307 // if(typeof w != 'number'){
16308 // // we do not handle it!?!?
16311 // var tw = this.trigger.getWidth();
16312 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16313 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16315 // this.inputEl().setWidth( this.adjustWidth('input', x));
16317 // //this.trigger.setStyle('left', x+'px');
16319 // if(this.list && this.listWidth === undefined){
16320 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16321 // this.list.setWidth(lw);
16322 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16330 * Allow or prevent the user from directly editing the field text. If false is passed,
16331 * the user will only be able to select from the items defined in the dropdown list. This method
16332 * is the runtime equivalent of setting the 'editable' config option at config time.
16333 * @param {Boolean} value True to allow the user to directly edit the field text
16335 setEditable : function(value){
16336 if(value == this.editable){
16339 this.editable = value;
16341 this.inputEl().dom.setAttribute('readOnly', true);
16342 this.inputEl().on('mousedown', this.onTriggerClick, this);
16343 this.inputEl().addClass('x-combo-noedit');
16345 this.inputEl().dom.setAttribute('readOnly', false);
16346 this.inputEl().un('mousedown', this.onTriggerClick, this);
16347 this.inputEl().removeClass('x-combo-noedit');
16353 onBeforeLoad : function(combo,opts){
16354 if(!this.hasFocus){
16358 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16360 this.restrictHeight();
16361 this.selectedIndex = -1;
16365 onLoad : function(){
16367 this.hasQuery = false;
16369 if(!this.hasFocus){
16373 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16374 this.loading.hide();
16377 if(this.store.getCount() > 0){
16380 this.restrictHeight();
16381 if(this.lastQuery == this.allQuery){
16382 if(this.editable && !this.tickable){
16383 this.inputEl().dom.select();
16387 !this.selectByValue(this.value, true) &&
16390 !this.store.lastOptions ||
16391 typeof(this.store.lastOptions.add) == 'undefined' ||
16392 this.store.lastOptions.add != true
16395 this.select(0, true);
16398 if(this.autoFocus){
16401 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16402 this.taTask.delay(this.typeAheadDelay);
16406 this.onEmptyResults();
16412 onLoadException : function()
16414 this.hasQuery = false;
16416 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16417 this.loading.hide();
16420 if(this.tickable && this.editable){
16425 // only causes errors at present
16426 //Roo.log(this.store.reader.jsonData);
16427 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16429 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16435 onTypeAhead : function(){
16436 if(this.store.getCount() > 0){
16437 var r = this.store.getAt(0);
16438 var newValue = r.data[this.displayField];
16439 var len = newValue.length;
16440 var selStart = this.getRawValue().length;
16442 if(selStart != len){
16443 this.setRawValue(newValue);
16444 this.selectText(selStart, newValue.length);
16450 onSelect : function(record, index){
16452 if(this.fireEvent('beforeselect', this, record, index) !== false){
16454 this.setFromData(index > -1 ? record.data : false);
16457 this.fireEvent('select', this, record, index);
16462 * Returns the currently selected field value or empty string if no value is set.
16463 * @return {String} value The selected value
16465 getValue : function()
16467 if(Roo.isIOS && this.useNativeIOS){
16468 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16472 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16475 if(this.valueField){
16476 return typeof this.value != 'undefined' ? this.value : '';
16478 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16482 getRawValue : function()
16484 if(Roo.isIOS && this.useNativeIOS){
16485 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16488 var v = this.inputEl().getValue();
16494 * Clears any text/value currently set in the field
16496 clearValue : function(){
16498 if(this.hiddenField){
16499 this.hiddenField.dom.value = '';
16502 this.setRawValue('');
16503 this.lastSelectionText = '';
16504 this.lastData = false;
16506 var close = this.closeTriggerEl();
16517 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16518 * will be displayed in the field. If the value does not match the data value of an existing item,
16519 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16520 * Otherwise the field will be blank (although the value will still be set).
16521 * @param {String} value The value to match
16523 setValue : function(v)
16525 if(Roo.isIOS && this.useNativeIOS){
16526 this.setIOSValue(v);
16536 if(this.valueField){
16537 var r = this.findRecord(this.valueField, v);
16539 text = r.data[this.displayField];
16540 }else if(this.valueNotFoundText !== undefined){
16541 text = this.valueNotFoundText;
16544 this.lastSelectionText = text;
16545 if(this.hiddenField){
16546 this.hiddenField.dom.value = v;
16548 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16551 var close = this.closeTriggerEl();
16554 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16560 * @property {Object} the last set data for the element
16565 * Sets the value of the field based on a object which is related to the record format for the store.
16566 * @param {Object} value the value to set as. or false on reset?
16568 setFromData : function(o){
16575 var dv = ''; // display value
16576 var vv = ''; // value value..
16578 if (this.displayField) {
16579 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16581 // this is an error condition!!!
16582 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16585 if(this.valueField){
16586 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16589 var close = this.closeTriggerEl();
16592 if(dv.length || vv * 1 > 0){
16594 this.blockFocus=true;
16600 if(this.hiddenField){
16601 this.hiddenField.dom.value = vv;
16603 this.lastSelectionText = dv;
16604 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16608 // no hidden field.. - we store the value in 'value', but still display
16609 // display field!!!!
16610 this.lastSelectionText = dv;
16611 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16618 reset : function(){
16619 // overridden so that last data is reset..
16626 this.setValue(this.originalValue);
16627 //this.clearInvalid();
16628 this.lastData = false;
16630 this.view.clearSelections();
16636 findRecord : function(prop, value){
16638 if(this.store.getCount() > 0){
16639 this.store.each(function(r){
16640 if(r.data[prop] == value){
16650 getName: function()
16652 // returns hidden if it's set..
16653 if (!this.rendered) {return ''};
16654 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16658 onViewMove : function(e, t){
16659 this.inKeyMode = false;
16663 onViewOver : function(e, t){
16664 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16667 var item = this.view.findItemFromChild(t);
16670 var index = this.view.indexOf(item);
16671 this.select(index, false);
16676 onViewClick : function(view, doFocus, el, e)
16678 var index = this.view.getSelectedIndexes()[0];
16680 var r = this.store.getAt(index);
16684 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16691 Roo.each(this.tickItems, function(v,k){
16693 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16695 _this.tickItems.splice(k, 1);
16697 if(typeof(e) == 'undefined' && view == false){
16698 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16710 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16711 this.tickItems.push(r.data);
16714 if(typeof(e) == 'undefined' && view == false){
16715 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16722 this.onSelect(r, index);
16724 if(doFocus !== false && !this.blockFocus){
16725 this.inputEl().focus();
16730 restrictHeight : function(){
16731 //this.innerList.dom.style.height = '';
16732 //var inner = this.innerList.dom;
16733 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16734 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16735 //this.list.beginUpdate();
16736 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16737 this.list.alignTo(this.inputEl(), this.listAlign);
16738 this.list.alignTo(this.inputEl(), this.listAlign);
16739 //this.list.endUpdate();
16743 onEmptyResults : function(){
16745 if(this.tickable && this.editable){
16746 this.hasFocus = false;
16747 this.restrictHeight();
16755 * Returns true if the dropdown list is expanded, else false.
16757 isExpanded : function(){
16758 return this.list.isVisible();
16762 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16763 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16764 * @param {String} value The data value of the item to select
16765 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16766 * selected item if it is not currently in view (defaults to true)
16767 * @return {Boolean} True if the value matched an item in the list, else false
16769 selectByValue : function(v, scrollIntoView){
16770 if(v !== undefined && v !== null){
16771 var r = this.findRecord(this.valueField || this.displayField, v);
16773 this.select(this.store.indexOf(r), scrollIntoView);
16781 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16782 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16783 * @param {Number} index The zero-based index of the list item to select
16784 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16785 * selected item if it is not currently in view (defaults to true)
16787 select : function(index, scrollIntoView){
16788 this.selectedIndex = index;
16789 this.view.select(index);
16790 if(scrollIntoView !== false){
16791 var el = this.view.getNode(index);
16793 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16796 this.list.scrollChildIntoView(el, false);
16802 selectNext : function(){
16803 var ct = this.store.getCount();
16805 if(this.selectedIndex == -1){
16807 }else if(this.selectedIndex < ct-1){
16808 this.select(this.selectedIndex+1);
16814 selectPrev : function(){
16815 var ct = this.store.getCount();
16817 if(this.selectedIndex == -1){
16819 }else if(this.selectedIndex != 0){
16820 this.select(this.selectedIndex-1);
16826 onKeyUp : function(e){
16827 if(this.editable !== false && !e.isSpecialKey()){
16828 this.lastKey = e.getKey();
16829 this.dqTask.delay(this.queryDelay);
16834 validateBlur : function(){
16835 return !this.list || !this.list.isVisible();
16839 initQuery : function(){
16841 var v = this.getRawValue();
16843 if(this.tickable && this.editable){
16844 v = this.tickableInputEl().getValue();
16851 doForce : function(){
16852 if(this.inputEl().dom.value.length > 0){
16853 this.inputEl().dom.value =
16854 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16860 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16861 * query allowing the query action to be canceled if needed.
16862 * @param {String} query The SQL query to execute
16863 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16864 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16865 * saved in the current store (defaults to false)
16867 doQuery : function(q, forceAll){
16869 if(q === undefined || q === null){
16874 forceAll: forceAll,
16878 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16883 forceAll = qe.forceAll;
16884 if(forceAll === true || (q.length >= this.minChars)){
16886 this.hasQuery = true;
16888 if(this.lastQuery != q || this.alwaysQuery){
16889 this.lastQuery = q;
16890 if(this.mode == 'local'){
16891 this.selectedIndex = -1;
16893 this.store.clearFilter();
16896 if(this.specialFilter){
16897 this.fireEvent('specialfilter', this);
16902 this.store.filter(this.displayField, q);
16905 this.store.fireEvent("datachanged", this.store);
16912 this.store.baseParams[this.queryParam] = q;
16914 var options = {params : this.getParams(q)};
16917 options.add = true;
16918 options.params.start = this.page * this.pageSize;
16921 this.store.load(options);
16924 * this code will make the page width larger, at the beginning, the list not align correctly,
16925 * we should expand the list on onLoad
16926 * so command out it
16931 this.selectedIndex = -1;
16936 this.loadNext = false;
16940 getParams : function(q){
16942 //p[this.queryParam] = q;
16946 p.limit = this.pageSize;
16952 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16954 collapse : function(){
16955 if(!this.isExpanded()){
16961 this.hasFocus = false;
16965 this.cancelBtn.hide();
16966 this.trigger.show();
16969 this.tickableInputEl().dom.value = '';
16970 this.tickableInputEl().blur();
16975 Roo.get(document).un('mousedown', this.collapseIf, this);
16976 Roo.get(document).un('mousewheel', this.collapseIf, this);
16977 if (!this.editable) {
16978 Roo.get(document).un('keydown', this.listKeyPress, this);
16980 this.fireEvent('collapse', this);
16986 collapseIf : function(e){
16987 var in_combo = e.within(this.el);
16988 var in_list = e.within(this.list);
16989 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16991 if (in_combo || in_list || is_list) {
16992 //e.stopPropagation();
16997 this.onTickableFooterButtonClick(e, false, false);
17005 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17007 expand : function(){
17009 if(this.isExpanded() || !this.hasFocus){
17013 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17014 this.list.setWidth(lw);
17020 this.restrictHeight();
17024 this.tickItems = Roo.apply([], this.item);
17027 this.cancelBtn.show();
17028 this.trigger.hide();
17031 this.tickableInputEl().focus();
17036 Roo.get(document).on('mousedown', this.collapseIf, this);
17037 Roo.get(document).on('mousewheel', this.collapseIf, this);
17038 if (!this.editable) {
17039 Roo.get(document).on('keydown', this.listKeyPress, this);
17042 this.fireEvent('expand', this);
17046 // Implements the default empty TriggerField.onTriggerClick function
17047 onTriggerClick : function(e)
17049 Roo.log('trigger click');
17051 if(this.disabled || !this.triggerList){
17056 this.loadNext = false;
17058 if(this.isExpanded()){
17060 if (!this.blockFocus) {
17061 this.inputEl().focus();
17065 this.hasFocus = true;
17066 if(this.triggerAction == 'all') {
17067 this.doQuery(this.allQuery, true);
17069 this.doQuery(this.getRawValue());
17071 if (!this.blockFocus) {
17072 this.inputEl().focus();
17077 onTickableTriggerClick : function(e)
17084 this.loadNext = false;
17085 this.hasFocus = true;
17087 if(this.triggerAction == 'all') {
17088 this.doQuery(this.allQuery, true);
17090 this.doQuery(this.getRawValue());
17094 onSearchFieldClick : function(e)
17096 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17097 this.onTickableFooterButtonClick(e, false, false);
17101 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17106 this.loadNext = false;
17107 this.hasFocus = true;
17109 if(this.triggerAction == 'all') {
17110 this.doQuery(this.allQuery, true);
17112 this.doQuery(this.getRawValue());
17116 listKeyPress : function(e)
17118 //Roo.log('listkeypress');
17119 // scroll to first matching element based on key pres..
17120 if (e.isSpecialKey()) {
17123 var k = String.fromCharCode(e.getKey()).toUpperCase();
17126 var csel = this.view.getSelectedNodes();
17127 var cselitem = false;
17129 var ix = this.view.indexOf(csel[0]);
17130 cselitem = this.store.getAt(ix);
17131 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17137 this.store.each(function(v) {
17139 // start at existing selection.
17140 if (cselitem.id == v.id) {
17146 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17147 match = this.store.indexOf(v);
17153 if (match === false) {
17154 return true; // no more action?
17157 this.view.select(match);
17158 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17159 sn.scrollIntoView(sn.dom.parentNode, false);
17162 onViewScroll : function(e, t){
17164 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){
17168 this.hasQuery = true;
17170 this.loading = this.list.select('.loading', true).first();
17172 if(this.loading === null){
17173 this.list.createChild({
17175 cls: 'loading roo-select2-more-results roo-select2-active',
17176 html: 'Loading more results...'
17179 this.loading = this.list.select('.loading', true).first();
17181 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17183 this.loading.hide();
17186 this.loading.show();
17191 this.loadNext = true;
17193 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17198 addItem : function(o)
17200 var dv = ''; // display value
17202 if (this.displayField) {
17203 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17205 // this is an error condition!!!
17206 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17213 var choice = this.choices.createChild({
17215 cls: 'roo-select2-search-choice',
17224 cls: 'roo-select2-search-choice-close fa fa-times',
17229 }, this.searchField);
17231 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17233 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17241 this.inputEl().dom.value = '';
17246 onRemoveItem : function(e, _self, o)
17248 e.preventDefault();
17250 this.lastItem = Roo.apply([], this.item);
17252 var index = this.item.indexOf(o.data) * 1;
17255 Roo.log('not this item?!');
17259 this.item.splice(index, 1);
17264 this.fireEvent('remove', this, e);
17270 syncValue : function()
17272 if(!this.item.length){
17279 Roo.each(this.item, function(i){
17280 if(_this.valueField){
17281 value.push(i[_this.valueField]);
17288 this.value = value.join(',');
17290 if(this.hiddenField){
17291 this.hiddenField.dom.value = this.value;
17294 this.store.fireEvent("datachanged", this.store);
17299 clearItem : function()
17301 if(!this.multiple){
17307 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17315 if(this.tickable && !Roo.isTouch){
17316 this.view.refresh();
17320 inputEl: function ()
17322 if(Roo.isIOS && this.useNativeIOS){
17323 return this.el.select('select.roo-ios-select', true).first();
17326 if(Roo.isTouch && this.mobileTouchView){
17327 return this.el.select('input.form-control',true).first();
17331 return this.searchField;
17334 return this.el.select('input.form-control',true).first();
17337 onTickableFooterButtonClick : function(e, btn, el)
17339 e.preventDefault();
17341 this.lastItem = Roo.apply([], this.item);
17343 if(btn && btn.name == 'cancel'){
17344 this.tickItems = Roo.apply([], this.item);
17353 Roo.each(this.tickItems, function(o){
17361 validate : function()
17363 if(this.getVisibilityEl().hasClass('hidden')){
17367 var v = this.getRawValue();
17370 v = this.getValue();
17373 if(this.disabled || this.allowBlank || v.length){
17378 this.markInvalid();
17382 tickableInputEl : function()
17384 if(!this.tickable || !this.editable){
17385 return this.inputEl();
17388 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17392 getAutoCreateTouchView : function()
17397 cls: 'form-group' //input-group
17403 type : this.inputType,
17404 cls : 'form-control x-combo-noedit',
17405 autocomplete: 'new-password',
17406 placeholder : this.placeholder || '',
17411 input.name = this.name;
17415 input.cls += ' input-' + this.size;
17418 if (this.disabled) {
17419 input.disabled = true;
17423 cls : 'roo-combobox-wrap',
17430 inputblock.cls += ' input-group';
17432 inputblock.cn.unshift({
17434 cls : 'input-group-addon input-group-prepend input-group-text',
17439 if(this.removable && !this.multiple){
17440 inputblock.cls += ' roo-removable';
17442 inputblock.cn.push({
17445 cls : 'roo-combo-removable-btn close'
17449 if(this.hasFeedback && !this.allowBlank){
17451 inputblock.cls += ' has-feedback';
17453 inputblock.cn.push({
17455 cls: 'glyphicon form-control-feedback'
17462 inputblock.cls += (this.before) ? '' : ' input-group';
17464 inputblock.cn.push({
17466 cls : 'input-group-addon input-group-append input-group-text',
17472 var ibwrap = inputblock;
17477 cls: 'roo-select2-choices',
17481 cls: 'roo-select2-search-field',
17494 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17499 cls: 'form-hidden-field'
17505 if(!this.multiple && this.showToggleBtn){
17511 if (this.caret != false) {
17514 cls: 'fa fa-' + this.caret
17521 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17523 Roo.bootstrap.version == 3 ? caret : '',
17526 cls: 'combobox-clear',
17540 combobox.cls += ' roo-select2-container-multi';
17543 var align = this.labelAlign || this.parentLabelAlign();
17545 if (align ==='left' && this.fieldLabel.length) {
17550 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17551 tooltip : 'This field is required'
17555 cls : 'control-label col-form-label',
17556 html : this.fieldLabel
17560 cls : 'roo-combobox-wrap ',
17567 var labelCfg = cfg.cn[1];
17568 var contentCfg = cfg.cn[2];
17571 if(this.indicatorpos == 'right'){
17576 cls : 'control-label col-form-label',
17580 html : this.fieldLabel
17584 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17585 tooltip : 'This field is required'
17590 cls : "roo-combobox-wrap ",
17598 labelCfg = cfg.cn[0];
17599 contentCfg = cfg.cn[1];
17604 if(this.labelWidth > 12){
17605 labelCfg.style = "width: " + this.labelWidth + 'px';
17608 if(this.labelWidth < 13 && this.labelmd == 0){
17609 this.labelmd = this.labelWidth;
17612 if(this.labellg > 0){
17613 labelCfg.cls += ' col-lg-' + this.labellg;
17614 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17617 if(this.labelmd > 0){
17618 labelCfg.cls += ' col-md-' + this.labelmd;
17619 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17622 if(this.labelsm > 0){
17623 labelCfg.cls += ' col-sm-' + this.labelsm;
17624 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17627 if(this.labelxs > 0){
17628 labelCfg.cls += ' col-xs-' + this.labelxs;
17629 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17633 } else if ( this.fieldLabel.length) {
17637 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17638 tooltip : 'This field is required'
17642 cls : 'control-label',
17643 html : this.fieldLabel
17654 if(this.indicatorpos == 'right'){
17658 cls : 'control-label',
17659 html : this.fieldLabel,
17663 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17664 tooltip : 'This field is required'
17681 var settings = this;
17683 ['xs','sm','md','lg'].map(function(size){
17684 if (settings[size]) {
17685 cfg.cls += ' col-' + size + '-' + settings[size];
17692 initTouchView : function()
17694 this.renderTouchView();
17696 this.touchViewEl.on('scroll', function(){
17697 this.el.dom.scrollTop = 0;
17700 this.originalValue = this.getValue();
17702 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17704 this.inputEl().on("click", this.showTouchView, this);
17705 if (this.triggerEl) {
17706 this.triggerEl.on("click", this.showTouchView, this);
17710 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17711 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17713 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17715 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17716 this.store.on('load', this.onTouchViewLoad, this);
17717 this.store.on('loadexception', this.onTouchViewLoadException, this);
17719 if(this.hiddenName){
17721 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17723 this.hiddenField.dom.value =
17724 this.hiddenValue !== undefined ? this.hiddenValue :
17725 this.value !== undefined ? this.value : '';
17727 this.el.dom.removeAttribute('name');
17728 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17732 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17733 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17736 if(this.removable && !this.multiple){
17737 var close = this.closeTriggerEl();
17739 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17740 close.on('click', this.removeBtnClick, this, close);
17744 * fix the bug in Safari iOS8
17746 this.inputEl().on("focus", function(e){
17747 document.activeElement.blur();
17750 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17757 renderTouchView : function()
17759 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17760 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17762 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17763 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17765 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17766 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17767 this.touchViewBodyEl.setStyle('overflow', 'auto');
17769 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17770 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17772 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17773 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17777 showTouchView : function()
17783 this.touchViewHeaderEl.hide();
17785 if(this.modalTitle.length){
17786 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17787 this.touchViewHeaderEl.show();
17790 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17791 this.touchViewEl.show();
17793 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17795 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17796 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17798 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17800 if(this.modalTitle.length){
17801 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17804 this.touchViewBodyEl.setHeight(bodyHeight);
17808 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17810 this.touchViewEl.addClass(['in','show']);
17813 if(this._touchViewMask){
17814 Roo.get(document.body).addClass("x-body-masked");
17815 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17816 this._touchViewMask.setStyle('z-index', 10000);
17817 this._touchViewMask.addClass('show');
17820 this.doTouchViewQuery();
17824 hideTouchView : function()
17826 this.touchViewEl.removeClass(['in','show']);
17830 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17832 this.touchViewEl.setStyle('display', 'none');
17835 if(this._touchViewMask){
17836 this._touchViewMask.removeClass('show');
17837 Roo.get(document.body).removeClass("x-body-masked");
17841 setTouchViewValue : function()
17848 Roo.each(this.tickItems, function(o){
17853 this.hideTouchView();
17856 doTouchViewQuery : function()
17865 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17869 if(!this.alwaysQuery || this.mode == 'local'){
17870 this.onTouchViewLoad();
17877 onTouchViewBeforeLoad : function(combo,opts)
17883 onTouchViewLoad : function()
17885 if(this.store.getCount() < 1){
17886 this.onTouchViewEmptyResults();
17890 this.clearTouchView();
17892 var rawValue = this.getRawValue();
17894 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17896 this.tickItems = [];
17898 this.store.data.each(function(d, rowIndex){
17899 var row = this.touchViewListGroup.createChild(template);
17901 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17902 row.addClass(d.data.cls);
17905 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17908 html : d.data[this.displayField]
17911 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17912 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17915 row.removeClass('selected');
17916 if(!this.multiple && this.valueField &&
17917 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17920 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17921 row.addClass('selected');
17924 if(this.multiple && this.valueField &&
17925 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17929 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17930 this.tickItems.push(d.data);
17933 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17937 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17939 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17941 if(this.modalTitle.length){
17942 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17945 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17947 if(this.mobile_restrict_height && listHeight < bodyHeight){
17948 this.touchViewBodyEl.setHeight(listHeight);
17953 if(firstChecked && listHeight > bodyHeight){
17954 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17959 onTouchViewLoadException : function()
17961 this.hideTouchView();
17964 onTouchViewEmptyResults : function()
17966 this.clearTouchView();
17968 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17970 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17974 clearTouchView : function()
17976 this.touchViewListGroup.dom.innerHTML = '';
17979 onTouchViewClick : function(e, el, o)
17981 e.preventDefault();
17984 var rowIndex = o.rowIndex;
17986 var r = this.store.getAt(rowIndex);
17988 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17990 if(!this.multiple){
17991 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17992 c.dom.removeAttribute('checked');
17995 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17997 this.setFromData(r.data);
17999 var close = this.closeTriggerEl();
18005 this.hideTouchView();
18007 this.fireEvent('select', this, r, rowIndex);
18012 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18013 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18014 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18018 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18019 this.addItem(r.data);
18020 this.tickItems.push(r.data);
18024 getAutoCreateNativeIOS : function()
18027 cls: 'form-group' //input-group,
18032 cls : 'roo-ios-select'
18036 combobox.name = this.name;
18039 if (this.disabled) {
18040 combobox.disabled = true;
18043 var settings = this;
18045 ['xs','sm','md','lg'].map(function(size){
18046 if (settings[size]) {
18047 cfg.cls += ' col-' + size + '-' + settings[size];
18057 initIOSView : function()
18059 this.store.on('load', this.onIOSViewLoad, this);
18064 onIOSViewLoad : function()
18066 if(this.store.getCount() < 1){
18070 this.clearIOSView();
18072 if(this.allowBlank) {
18074 var default_text = '-- SELECT --';
18076 if(this.placeholder.length){
18077 default_text = this.placeholder;
18080 if(this.emptyTitle.length){
18081 default_text += ' - ' + this.emptyTitle + ' -';
18084 var opt = this.inputEl().createChild({
18087 html : default_text
18091 o[this.valueField] = 0;
18092 o[this.displayField] = default_text;
18094 this.ios_options.push({
18101 this.store.data.each(function(d, rowIndex){
18105 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18106 html = d.data[this.displayField];
18111 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18112 value = d.data[this.valueField];
18121 if(this.value == d.data[this.valueField]){
18122 option['selected'] = true;
18125 var opt = this.inputEl().createChild(option);
18127 this.ios_options.push({
18134 this.inputEl().on('change', function(){
18135 this.fireEvent('select', this);
18140 clearIOSView: function()
18142 this.inputEl().dom.innerHTML = '';
18144 this.ios_options = [];
18147 setIOSValue: function(v)
18151 if(!this.ios_options){
18155 Roo.each(this.ios_options, function(opts){
18157 opts.el.dom.removeAttribute('selected');
18159 if(opts.data[this.valueField] != v){
18163 opts.el.dom.setAttribute('selected', true);
18169 * @cfg {Boolean} grow
18173 * @cfg {Number} growMin
18177 * @cfg {Number} growMax
18186 Roo.apply(Roo.bootstrap.ComboBox, {
18190 cls: 'modal-header',
18212 cls: 'list-group-item',
18216 cls: 'roo-combobox-list-group-item-value'
18220 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18234 listItemCheckbox : {
18236 cls: 'list-group-item',
18240 cls: 'roo-combobox-list-group-item-value'
18244 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18260 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18265 cls: 'modal-footer',
18273 cls: 'col-xs-6 text-left',
18276 cls: 'btn btn-danger roo-touch-view-cancel',
18282 cls: 'col-xs-6 text-right',
18285 cls: 'btn btn-success roo-touch-view-ok',
18296 Roo.apply(Roo.bootstrap.ComboBox, {
18298 touchViewTemplate : {
18300 cls: 'modal fade roo-combobox-touch-view',
18304 cls: 'modal-dialog',
18305 style : 'position:fixed', // we have to fix position....
18309 cls: 'modal-content',
18311 Roo.bootstrap.ComboBox.header,
18312 Roo.bootstrap.ComboBox.body,
18313 Roo.bootstrap.ComboBox.footer
18322 * Ext JS Library 1.1.1
18323 * Copyright(c) 2006-2007, Ext JS, LLC.
18325 * Originally Released Under LGPL - original licence link has changed is not relivant.
18328 * <script type="text/javascript">
18333 * @extends Roo.util.Observable
18334 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18335 * This class also supports single and multi selection modes. <br>
18336 * Create a data model bound view:
18338 var store = new Roo.data.Store(...);
18340 var view = new Roo.View({
18342 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18344 singleSelect: true,
18345 selectedClass: "ydataview-selected",
18349 // listen for node click?
18350 view.on("click", function(vw, index, node, e){
18351 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18355 dataModel.load("foobar.xml");
18357 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18359 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18360 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18362 * Note: old style constructor is still suported (container, template, config)
18365 * Create a new View
18366 * @param {Object} config The config object
18369 Roo.View = function(config, depreciated_tpl, depreciated_config){
18371 this.parent = false;
18373 if (typeof(depreciated_tpl) == 'undefined') {
18374 // new way.. - universal constructor.
18375 Roo.apply(this, config);
18376 this.el = Roo.get(this.el);
18379 this.el = Roo.get(config);
18380 this.tpl = depreciated_tpl;
18381 Roo.apply(this, depreciated_config);
18383 this.wrapEl = this.el.wrap().wrap();
18384 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18387 if(typeof(this.tpl) == "string"){
18388 this.tpl = new Roo.Template(this.tpl);
18390 // support xtype ctors..
18391 this.tpl = new Roo.factory(this.tpl, Roo);
18395 this.tpl.compile();
18400 * @event beforeclick
18401 * Fires before a click is processed. Returns false to cancel the default action.
18402 * @param {Roo.View} this
18403 * @param {Number} index The index of the target node
18404 * @param {HTMLElement} node The target node
18405 * @param {Roo.EventObject} e The raw event object
18407 "beforeclick" : true,
18410 * Fires when a template node is clicked.
18411 * @param {Roo.View} this
18412 * @param {Number} index The index of the target node
18413 * @param {HTMLElement} node The target node
18414 * @param {Roo.EventObject} e The raw event object
18419 * Fires when a template node is double clicked.
18420 * @param {Roo.View} this
18421 * @param {Number} index The index of the target node
18422 * @param {HTMLElement} node The target node
18423 * @param {Roo.EventObject} e The raw event object
18427 * @event contextmenu
18428 * Fires when a template node is right clicked.
18429 * @param {Roo.View} this
18430 * @param {Number} index The index of the target node
18431 * @param {HTMLElement} node The target node
18432 * @param {Roo.EventObject} e The raw event object
18434 "contextmenu" : true,
18436 * @event selectionchange
18437 * Fires when the selected nodes change.
18438 * @param {Roo.View} this
18439 * @param {Array} selections Array of the selected nodes
18441 "selectionchange" : true,
18444 * @event beforeselect
18445 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18446 * @param {Roo.View} this
18447 * @param {HTMLElement} node The node to be selected
18448 * @param {Array} selections Array of currently selected nodes
18450 "beforeselect" : true,
18452 * @event preparedata
18453 * Fires on every row to render, to allow you to change the data.
18454 * @param {Roo.View} this
18455 * @param {Object} data to be rendered (change this)
18457 "preparedata" : true
18465 "click": this.onClick,
18466 "dblclick": this.onDblClick,
18467 "contextmenu": this.onContextMenu,
18471 this.selections = [];
18473 this.cmp = new Roo.CompositeElementLite([]);
18475 this.store = Roo.factory(this.store, Roo.data);
18476 this.setStore(this.store, true);
18479 if ( this.footer && this.footer.xtype) {
18481 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18483 this.footer.dataSource = this.store;
18484 this.footer.container = fctr;
18485 this.footer = Roo.factory(this.footer, Roo);
18486 fctr.insertFirst(this.el);
18488 // this is a bit insane - as the paging toolbar seems to detach the el..
18489 // dom.parentNode.parentNode.parentNode
18490 // they get detached?
18494 Roo.View.superclass.constructor.call(this);
18499 Roo.extend(Roo.View, Roo.util.Observable, {
18502 * @cfg {Roo.data.Store} store Data store to load data from.
18507 * @cfg {String|Roo.Element} el The container element.
18512 * @cfg {String|Roo.Template} tpl The template used by this View
18516 * @cfg {String} dataName the named area of the template to use as the data area
18517 * Works with domtemplates roo-name="name"
18521 * @cfg {String} selectedClass The css class to add to selected nodes
18523 selectedClass : "x-view-selected",
18525 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18530 * @cfg {String} text to display on mask (default Loading)
18534 * @cfg {Boolean} multiSelect Allow multiple selection
18536 multiSelect : false,
18538 * @cfg {Boolean} singleSelect Allow single selection
18540 singleSelect: false,
18543 * @cfg {Boolean} toggleSelect - selecting
18545 toggleSelect : false,
18548 * @cfg {Boolean} tickable - selecting
18553 * Returns the element this view is bound to.
18554 * @return {Roo.Element}
18556 getEl : function(){
18557 return this.wrapEl;
18563 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18565 refresh : function(){
18566 //Roo.log('refresh');
18569 // if we are using something like 'domtemplate', then
18570 // the what gets used is:
18571 // t.applySubtemplate(NAME, data, wrapping data..)
18572 // the outer template then get' applied with
18573 // the store 'extra data'
18574 // and the body get's added to the
18575 // roo-name="data" node?
18576 // <span class='roo-tpl-{name}'></span> ?????
18580 this.clearSelections();
18581 this.el.update("");
18583 var records = this.store.getRange();
18584 if(records.length < 1) {
18586 // is this valid?? = should it render a template??
18588 this.el.update(this.emptyText);
18592 if (this.dataName) {
18593 this.el.update(t.apply(this.store.meta)); //????
18594 el = this.el.child('.roo-tpl-' + this.dataName);
18597 for(var i = 0, len = records.length; i < len; i++){
18598 var data = this.prepareData(records[i].data, i, records[i]);
18599 this.fireEvent("preparedata", this, data, i, records[i]);
18601 var d = Roo.apply({}, data);
18604 Roo.apply(d, {'roo-id' : Roo.id()});
18608 Roo.each(this.parent.item, function(item){
18609 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18612 Roo.apply(d, {'roo-data-checked' : 'checked'});
18616 html[html.length] = Roo.util.Format.trim(
18618 t.applySubtemplate(this.dataName, d, this.store.meta) :
18625 el.update(html.join(""));
18626 this.nodes = el.dom.childNodes;
18627 this.updateIndexes(0);
18632 * Function to override to reformat the data that is sent to
18633 * the template for each node.
18634 * DEPRICATED - use the preparedata event handler.
18635 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18636 * a JSON object for an UpdateManager bound view).
18638 prepareData : function(data, index, record)
18640 this.fireEvent("preparedata", this, data, index, record);
18644 onUpdate : function(ds, record){
18645 // Roo.log('on update');
18646 this.clearSelections();
18647 var index = this.store.indexOf(record);
18648 var n = this.nodes[index];
18649 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18650 n.parentNode.removeChild(n);
18651 this.updateIndexes(index, index);
18657 onAdd : function(ds, records, index)
18659 //Roo.log(['on Add', ds, records, index] );
18660 this.clearSelections();
18661 if(this.nodes.length == 0){
18665 var n = this.nodes[index];
18666 for(var i = 0, len = records.length; i < len; i++){
18667 var d = this.prepareData(records[i].data, i, records[i]);
18669 this.tpl.insertBefore(n, d);
18672 this.tpl.append(this.el, d);
18675 this.updateIndexes(index);
18678 onRemove : function(ds, record, index){
18679 // Roo.log('onRemove');
18680 this.clearSelections();
18681 var el = this.dataName ?
18682 this.el.child('.roo-tpl-' + this.dataName) :
18685 el.dom.removeChild(this.nodes[index]);
18686 this.updateIndexes(index);
18690 * Refresh an individual node.
18691 * @param {Number} index
18693 refreshNode : function(index){
18694 this.onUpdate(this.store, this.store.getAt(index));
18697 updateIndexes : function(startIndex, endIndex){
18698 var ns = this.nodes;
18699 startIndex = startIndex || 0;
18700 endIndex = endIndex || ns.length - 1;
18701 for(var i = startIndex; i <= endIndex; i++){
18702 ns[i].nodeIndex = i;
18707 * Changes the data store this view uses and refresh the view.
18708 * @param {Store} store
18710 setStore : function(store, initial){
18711 if(!initial && this.store){
18712 this.store.un("datachanged", this.refresh);
18713 this.store.un("add", this.onAdd);
18714 this.store.un("remove", this.onRemove);
18715 this.store.un("update", this.onUpdate);
18716 this.store.un("clear", this.refresh);
18717 this.store.un("beforeload", this.onBeforeLoad);
18718 this.store.un("load", this.onLoad);
18719 this.store.un("loadexception", this.onLoad);
18723 store.on("datachanged", this.refresh, this);
18724 store.on("add", this.onAdd, this);
18725 store.on("remove", this.onRemove, this);
18726 store.on("update", this.onUpdate, this);
18727 store.on("clear", this.refresh, this);
18728 store.on("beforeload", this.onBeforeLoad, this);
18729 store.on("load", this.onLoad, this);
18730 store.on("loadexception", this.onLoad, this);
18738 * onbeforeLoad - masks the loading area.
18741 onBeforeLoad : function(store,opts)
18743 //Roo.log('onBeforeLoad');
18745 this.el.update("");
18747 this.el.mask(this.mask ? this.mask : "Loading" );
18749 onLoad : function ()
18756 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18757 * @param {HTMLElement} node
18758 * @return {HTMLElement} The template node
18760 findItemFromChild : function(node){
18761 var el = this.dataName ?
18762 this.el.child('.roo-tpl-' + this.dataName,true) :
18765 if(!node || node.parentNode == el){
18768 var p = node.parentNode;
18769 while(p && p != el){
18770 if(p.parentNode == el){
18779 onClick : function(e){
18780 var item = this.findItemFromChild(e.getTarget());
18782 var index = this.indexOf(item);
18783 if(this.onItemClick(item, index, e) !== false){
18784 this.fireEvent("click", this, index, item, e);
18787 this.clearSelections();
18792 onContextMenu : function(e){
18793 var item = this.findItemFromChild(e.getTarget());
18795 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18800 onDblClick : function(e){
18801 var item = this.findItemFromChild(e.getTarget());
18803 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18807 onItemClick : function(item, index, e)
18809 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18812 if (this.toggleSelect) {
18813 var m = this.isSelected(item) ? 'unselect' : 'select';
18816 _t[m](item, true, false);
18819 if(this.multiSelect || this.singleSelect){
18820 if(this.multiSelect && e.shiftKey && this.lastSelection){
18821 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18823 this.select(item, this.multiSelect && e.ctrlKey);
18824 this.lastSelection = item;
18827 if(!this.tickable){
18828 e.preventDefault();
18836 * Get the number of selected nodes.
18839 getSelectionCount : function(){
18840 return this.selections.length;
18844 * Get the currently selected nodes.
18845 * @return {Array} An array of HTMLElements
18847 getSelectedNodes : function(){
18848 return this.selections;
18852 * Get the indexes of the selected nodes.
18855 getSelectedIndexes : function(){
18856 var indexes = [], s = this.selections;
18857 for(var i = 0, len = s.length; i < len; i++){
18858 indexes.push(s[i].nodeIndex);
18864 * Clear all selections
18865 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18867 clearSelections : function(suppressEvent){
18868 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18869 this.cmp.elements = this.selections;
18870 this.cmp.removeClass(this.selectedClass);
18871 this.selections = [];
18872 if(!suppressEvent){
18873 this.fireEvent("selectionchange", this, this.selections);
18879 * Returns true if the passed node is selected
18880 * @param {HTMLElement/Number} node The node or node index
18881 * @return {Boolean}
18883 isSelected : function(node){
18884 var s = this.selections;
18888 node = this.getNode(node);
18889 return s.indexOf(node) !== -1;
18894 * @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
18895 * @param {Boolean} keepExisting (optional) true to keep existing selections
18896 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18898 select : function(nodeInfo, keepExisting, suppressEvent){
18899 if(nodeInfo instanceof Array){
18901 this.clearSelections(true);
18903 for(var i = 0, len = nodeInfo.length; i < len; i++){
18904 this.select(nodeInfo[i], true, true);
18908 var node = this.getNode(nodeInfo);
18909 if(!node || this.isSelected(node)){
18910 return; // already selected.
18913 this.clearSelections(true);
18916 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18917 Roo.fly(node).addClass(this.selectedClass);
18918 this.selections.push(node);
18919 if(!suppressEvent){
18920 this.fireEvent("selectionchange", this, this.selections);
18928 * @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
18929 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18930 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18932 unselect : function(nodeInfo, keepExisting, suppressEvent)
18934 if(nodeInfo instanceof Array){
18935 Roo.each(this.selections, function(s) {
18936 this.unselect(s, nodeInfo);
18940 var node = this.getNode(nodeInfo);
18941 if(!node || !this.isSelected(node)){
18942 //Roo.log("not selected");
18943 return; // not selected.
18947 Roo.each(this.selections, function(s) {
18949 Roo.fly(node).removeClass(this.selectedClass);
18956 this.selections= ns;
18957 this.fireEvent("selectionchange", this, this.selections);
18961 * Gets a template node.
18962 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18963 * @return {HTMLElement} The node or null if it wasn't found
18965 getNode : function(nodeInfo){
18966 if(typeof nodeInfo == "string"){
18967 return document.getElementById(nodeInfo);
18968 }else if(typeof nodeInfo == "number"){
18969 return this.nodes[nodeInfo];
18975 * Gets a range template nodes.
18976 * @param {Number} startIndex
18977 * @param {Number} endIndex
18978 * @return {Array} An array of nodes
18980 getNodes : function(start, end){
18981 var ns = this.nodes;
18982 start = start || 0;
18983 end = typeof end == "undefined" ? ns.length - 1 : end;
18986 for(var i = start; i <= end; i++){
18990 for(var i = start; i >= end; i--){
18998 * Finds the index of the passed node
18999 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19000 * @return {Number} The index of the node or -1
19002 indexOf : function(node){
19003 node = this.getNode(node);
19004 if(typeof node.nodeIndex == "number"){
19005 return node.nodeIndex;
19007 var ns = this.nodes;
19008 for(var i = 0, len = ns.length; i < len; i++){
19019 * based on jquery fullcalendar
19023 Roo.bootstrap = Roo.bootstrap || {};
19025 * @class Roo.bootstrap.Calendar
19026 * @extends Roo.bootstrap.Component
19027 * Bootstrap Calendar class
19028 * @cfg {Boolean} loadMask (true|false) default false
19029 * @cfg {Object} header generate the user specific header of the calendar, default false
19032 * Create a new Container
19033 * @param {Object} config The config object
19038 Roo.bootstrap.Calendar = function(config){
19039 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19043 * Fires when a date is selected
19044 * @param {DatePicker} this
19045 * @param {Date} date The selected date
19049 * @event monthchange
19050 * Fires when the displayed month changes
19051 * @param {DatePicker} this
19052 * @param {Date} date The selected month
19054 'monthchange': true,
19056 * @event evententer
19057 * Fires when mouse over an event
19058 * @param {Calendar} this
19059 * @param {event} Event
19061 'evententer': true,
19063 * @event eventleave
19064 * Fires when the mouse leaves an
19065 * @param {Calendar} this
19068 'eventleave': true,
19070 * @event eventclick
19071 * Fires when the mouse click an
19072 * @param {Calendar} this
19081 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19084 * @cfg {Number} startDay
19085 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19093 getAutoCreate : function(){
19096 var fc_button = function(name, corner, style, content ) {
19097 return Roo.apply({},{
19099 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19101 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19104 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19115 style : 'width:100%',
19122 cls : 'fc-header-left',
19124 fc_button('prev', 'left', 'arrow', '‹' ),
19125 fc_button('next', 'right', 'arrow', '›' ),
19126 { tag: 'span', cls: 'fc-header-space' },
19127 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19135 cls : 'fc-header-center',
19139 cls: 'fc-header-title',
19142 html : 'month / year'
19150 cls : 'fc-header-right',
19152 /* fc_button('month', 'left', '', 'month' ),
19153 fc_button('week', '', '', 'week' ),
19154 fc_button('day', 'right', '', 'day' )
19166 header = this.header;
19169 var cal_heads = function() {
19171 // fixme - handle this.
19173 for (var i =0; i < Date.dayNames.length; i++) {
19174 var d = Date.dayNames[i];
19177 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19178 html : d.substring(0,3)
19182 ret[0].cls += ' fc-first';
19183 ret[6].cls += ' fc-last';
19186 var cal_cell = function(n) {
19189 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19194 cls: 'fc-day-number',
19198 cls: 'fc-day-content',
19202 style: 'position: relative;' // height: 17px;
19214 var cal_rows = function() {
19217 for (var r = 0; r < 6; r++) {
19224 for (var i =0; i < Date.dayNames.length; i++) {
19225 var d = Date.dayNames[i];
19226 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19229 row.cn[0].cls+=' fc-first';
19230 row.cn[0].cn[0].style = 'min-height:90px';
19231 row.cn[6].cls+=' fc-last';
19235 ret[0].cls += ' fc-first';
19236 ret[4].cls += ' fc-prev-last';
19237 ret[5].cls += ' fc-last';
19244 cls: 'fc-border-separate',
19245 style : 'width:100%',
19253 cls : 'fc-first fc-last',
19271 cls : 'fc-content',
19272 style : "position: relative;",
19275 cls : 'fc-view fc-view-month fc-grid',
19276 style : 'position: relative',
19277 unselectable : 'on',
19280 cls : 'fc-event-container',
19281 style : 'position:absolute;z-index:8;top:0;left:0;'
19299 initEvents : function()
19302 throw "can not find store for calendar";
19308 style: "text-align:center",
19312 style: "background-color:white;width:50%;margin:250 auto",
19316 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19327 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19329 var size = this.el.select('.fc-content', true).first().getSize();
19330 this.maskEl.setSize(size.width, size.height);
19331 this.maskEl.enableDisplayMode("block");
19332 if(!this.loadMask){
19333 this.maskEl.hide();
19336 this.store = Roo.factory(this.store, Roo.data);
19337 this.store.on('load', this.onLoad, this);
19338 this.store.on('beforeload', this.onBeforeLoad, this);
19342 this.cells = this.el.select('.fc-day',true);
19343 //Roo.log(this.cells);
19344 this.textNodes = this.el.query('.fc-day-number');
19345 this.cells.addClassOnOver('fc-state-hover');
19347 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19348 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19349 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19350 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19352 this.on('monthchange', this.onMonthChange, this);
19354 this.update(new Date().clearTime());
19357 resize : function() {
19358 var sz = this.el.getSize();
19360 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19361 this.el.select('.fc-day-content div',true).setHeight(34);
19366 showPrevMonth : function(e){
19367 this.update(this.activeDate.add("mo", -1));
19369 showToday : function(e){
19370 this.update(new Date().clearTime());
19373 showNextMonth : function(e){
19374 this.update(this.activeDate.add("mo", 1));
19378 showPrevYear : function(){
19379 this.update(this.activeDate.add("y", -1));
19383 showNextYear : function(){
19384 this.update(this.activeDate.add("y", 1));
19389 update : function(date)
19391 var vd = this.activeDate;
19392 this.activeDate = date;
19393 // if(vd && this.el){
19394 // var t = date.getTime();
19395 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19396 // Roo.log('using add remove');
19398 // this.fireEvent('monthchange', this, date);
19400 // this.cells.removeClass("fc-state-highlight");
19401 // this.cells.each(function(c){
19402 // if(c.dateValue == t){
19403 // c.addClass("fc-state-highlight");
19404 // setTimeout(function(){
19405 // try{c.dom.firstChild.focus();}catch(e){}
19415 var days = date.getDaysInMonth();
19417 var firstOfMonth = date.getFirstDateOfMonth();
19418 var startingPos = firstOfMonth.getDay()-this.startDay;
19420 if(startingPos < this.startDay){
19424 var pm = date.add(Date.MONTH, -1);
19425 var prevStart = pm.getDaysInMonth()-startingPos;
19427 this.cells = this.el.select('.fc-day',true);
19428 this.textNodes = this.el.query('.fc-day-number');
19429 this.cells.addClassOnOver('fc-state-hover');
19431 var cells = this.cells.elements;
19432 var textEls = this.textNodes;
19434 Roo.each(cells, function(cell){
19435 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19438 days += startingPos;
19440 // convert everything to numbers so it's fast
19441 var day = 86400000;
19442 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19445 //Roo.log(prevStart);
19447 var today = new Date().clearTime().getTime();
19448 var sel = date.clearTime().getTime();
19449 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19450 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19451 var ddMatch = this.disabledDatesRE;
19452 var ddText = this.disabledDatesText;
19453 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19454 var ddaysText = this.disabledDaysText;
19455 var format = this.format;
19457 var setCellClass = function(cal, cell){
19461 //Roo.log('set Cell Class');
19463 var t = d.getTime();
19467 cell.dateValue = t;
19469 cell.className += " fc-today";
19470 cell.className += " fc-state-highlight";
19471 cell.title = cal.todayText;
19474 // disable highlight in other month..
19475 //cell.className += " fc-state-highlight";
19480 cell.className = " fc-state-disabled";
19481 cell.title = cal.minText;
19485 cell.className = " fc-state-disabled";
19486 cell.title = cal.maxText;
19490 if(ddays.indexOf(d.getDay()) != -1){
19491 cell.title = ddaysText;
19492 cell.className = " fc-state-disabled";
19495 if(ddMatch && format){
19496 var fvalue = d.dateFormat(format);
19497 if(ddMatch.test(fvalue)){
19498 cell.title = ddText.replace("%0", fvalue);
19499 cell.className = " fc-state-disabled";
19503 if (!cell.initialClassName) {
19504 cell.initialClassName = cell.dom.className;
19507 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19512 for(; i < startingPos; i++) {
19513 textEls[i].innerHTML = (++prevStart);
19514 d.setDate(d.getDate()+1);
19516 cells[i].className = "fc-past fc-other-month";
19517 setCellClass(this, cells[i]);
19522 for(; i < days; i++){
19523 intDay = i - startingPos + 1;
19524 textEls[i].innerHTML = (intDay);
19525 d.setDate(d.getDate()+1);
19527 cells[i].className = ''; // "x-date-active";
19528 setCellClass(this, cells[i]);
19532 for(; i < 42; i++) {
19533 textEls[i].innerHTML = (++extraDays);
19534 d.setDate(d.getDate()+1);
19536 cells[i].className = "fc-future fc-other-month";
19537 setCellClass(this, cells[i]);
19540 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19542 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19544 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19545 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19547 if(totalRows != 6){
19548 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19549 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19552 this.fireEvent('monthchange', this, date);
19556 if(!this.internalRender){
19557 var main = this.el.dom.firstChild;
19558 var w = main.offsetWidth;
19559 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19560 Roo.fly(main).setWidth(w);
19561 this.internalRender = true;
19562 // opera does not respect the auto grow header center column
19563 // then, after it gets a width opera refuses to recalculate
19564 // without a second pass
19565 if(Roo.isOpera && !this.secondPass){
19566 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19567 this.secondPass = true;
19568 this.update.defer(10, this, [date]);
19575 findCell : function(dt) {
19576 dt = dt.clearTime().getTime();
19578 this.cells.each(function(c){
19579 //Roo.log("check " +c.dateValue + '?=' + dt);
19580 if(c.dateValue == dt){
19590 findCells : function(ev) {
19591 var s = ev.start.clone().clearTime().getTime();
19593 var e= ev.end.clone().clearTime().getTime();
19596 this.cells.each(function(c){
19597 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19599 if(c.dateValue > e){
19602 if(c.dateValue < s){
19611 // findBestRow: function(cells)
19615 // for (var i =0 ; i < cells.length;i++) {
19616 // ret = Math.max(cells[i].rows || 0,ret);
19623 addItem : function(ev)
19625 // look for vertical location slot in
19626 var cells = this.findCells(ev);
19628 // ev.row = this.findBestRow(cells);
19630 // work out the location.
19634 for(var i =0; i < cells.length; i++) {
19636 cells[i].row = cells[0].row;
19639 cells[i].row = cells[i].row + 1;
19649 if (crow.start.getY() == cells[i].getY()) {
19651 crow.end = cells[i];
19668 cells[0].events.push(ev);
19670 this.calevents.push(ev);
19673 clearEvents: function() {
19675 if(!this.calevents){
19679 Roo.each(this.cells.elements, function(c){
19685 Roo.each(this.calevents, function(e) {
19686 Roo.each(e.els, function(el) {
19687 el.un('mouseenter' ,this.onEventEnter, this);
19688 el.un('mouseleave' ,this.onEventLeave, this);
19693 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19699 renderEvents: function()
19703 this.cells.each(function(c) {
19712 if(c.row != c.events.length){
19713 r = 4 - (4 - (c.row - c.events.length));
19716 c.events = ev.slice(0, r);
19717 c.more = ev.slice(r);
19719 if(c.more.length && c.more.length == 1){
19720 c.events.push(c.more.pop());
19723 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19727 this.cells.each(function(c) {
19729 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19732 for (var e = 0; e < c.events.length; e++){
19733 var ev = c.events[e];
19734 var rows = ev.rows;
19736 for(var i = 0; i < rows.length; i++) {
19738 // how many rows should it span..
19741 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19742 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19744 unselectable : "on",
19747 cls: 'fc-event-inner',
19751 // cls: 'fc-event-time',
19752 // html : cells.length > 1 ? '' : ev.time
19756 cls: 'fc-event-title',
19757 html : String.format('{0}', ev.title)
19764 cls: 'ui-resizable-handle ui-resizable-e',
19765 html : '  '
19772 cfg.cls += ' fc-event-start';
19774 if ((i+1) == rows.length) {
19775 cfg.cls += ' fc-event-end';
19778 var ctr = _this.el.select('.fc-event-container',true).first();
19779 var cg = ctr.createChild(cfg);
19781 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19782 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19784 var r = (c.more.length) ? 1 : 0;
19785 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19786 cg.setWidth(ebox.right - sbox.x -2);
19788 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19789 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19790 cg.on('click', _this.onEventClick, _this, ev);
19801 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19802 style : 'position: absolute',
19803 unselectable : "on",
19806 cls: 'fc-event-inner',
19810 cls: 'fc-event-title',
19818 cls: 'ui-resizable-handle ui-resizable-e',
19819 html : '  '
19825 var ctr = _this.el.select('.fc-event-container',true).first();
19826 var cg = ctr.createChild(cfg);
19828 var sbox = c.select('.fc-day-content',true).first().getBox();
19829 var ebox = c.select('.fc-day-content',true).first().getBox();
19831 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19832 cg.setWidth(ebox.right - sbox.x -2);
19834 cg.on('click', _this.onMoreEventClick, _this, c.more);
19844 onEventEnter: function (e, el,event,d) {
19845 this.fireEvent('evententer', this, el, event);
19848 onEventLeave: function (e, el,event,d) {
19849 this.fireEvent('eventleave', this, el, event);
19852 onEventClick: function (e, el,event,d) {
19853 this.fireEvent('eventclick', this, el, event);
19856 onMonthChange: function () {
19860 onMoreEventClick: function(e, el, more)
19864 this.calpopover.placement = 'right';
19865 this.calpopover.setTitle('More');
19867 this.calpopover.setContent('');
19869 var ctr = this.calpopover.el.select('.popover-content', true).first();
19871 Roo.each(more, function(m){
19873 cls : 'fc-event-hori fc-event-draggable',
19876 var cg = ctr.createChild(cfg);
19878 cg.on('click', _this.onEventClick, _this, m);
19881 this.calpopover.show(el);
19886 onLoad: function ()
19888 this.calevents = [];
19891 if(this.store.getCount() > 0){
19892 this.store.data.each(function(d){
19895 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19896 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19897 time : d.data.start_time,
19898 title : d.data.title,
19899 description : d.data.description,
19900 venue : d.data.venue
19905 this.renderEvents();
19907 if(this.calevents.length && this.loadMask){
19908 this.maskEl.hide();
19912 onBeforeLoad: function()
19914 this.clearEvents();
19916 this.maskEl.show();
19930 * @class Roo.bootstrap.Popover
19931 * @extends Roo.bootstrap.Component
19932 * Bootstrap Popover class
19933 * @cfg {String} html contents of the popover (or false to use children..)
19934 * @cfg {String} title of popover (or false to hide)
19935 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19936 * @cfg {String} trigger click || hover (or false to trigger manually)
19937 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19938 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19939 * - if false and it has a 'parent' then it will be automatically added to that element
19940 * - if string - Roo.get will be called
19941 * @cfg {Number} delay - delay before showing
19944 * Create a new Popover
19945 * @param {Object} config The config object
19948 Roo.bootstrap.Popover = function(config){
19949 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19955 * After the popover show
19957 * @param {Roo.bootstrap.Popover} this
19962 * After the popover hide
19964 * @param {Roo.bootstrap.Popover} this
19970 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19975 placement : 'right',
19976 trigger : 'hover', // hover
19982 can_build_overlaid : false,
19984 maskEl : false, // the mask element
19987 alignEl : false, // when show is called with an element - this get's stored.
19989 getChildContainer : function()
19991 return this.contentEl;
19994 getPopoverHeader : function()
19996 this.title = true; // flag not to hide it..
19997 this.headerEl.addClass('p-0');
19998 return this.headerEl
20002 getAutoCreate : function(){
20005 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20006 style: 'display:block',
20012 cls : 'popover-inner ',
20016 cls: 'popover-title popover-header',
20017 html : this.title === false ? '' : this.title
20020 cls : 'popover-content popover-body ' + (this.cls || ''),
20021 html : this.html || ''
20032 * @param {string} the title
20034 setTitle: function(str)
20038 this.headerEl.dom.innerHTML = str;
20043 * @param {string} the body content
20045 setContent: function(str)
20048 if (this.contentEl) {
20049 this.contentEl.dom.innerHTML = str;
20053 // as it get's added to the bottom of the page.
20054 onRender : function(ct, position)
20056 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20061 var cfg = Roo.apply({}, this.getAutoCreate());
20065 cfg.cls += ' ' + this.cls;
20068 cfg.style = this.style;
20070 //Roo.log("adding to ");
20071 this.el = Roo.get(document.body).createChild(cfg, position);
20072 // Roo.log(this.el);
20075 this.contentEl = this.el.select('.popover-content',true).first();
20076 this.headerEl = this.el.select('.popover-title',true).first();
20079 if(typeof(this.items) != 'undefined'){
20080 var items = this.items;
20083 for(var i =0;i < items.length;i++) {
20084 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20088 this.items = nitems;
20090 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20091 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20098 resizeMask : function()
20100 this.maskEl.setSize(
20101 Roo.lib.Dom.getViewWidth(true),
20102 Roo.lib.Dom.getViewHeight(true)
20106 initEvents : function()
20110 Roo.bootstrap.Popover.register(this);
20113 this.arrowEl = this.el.select('.arrow',true).first();
20114 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20115 this.el.enableDisplayMode('block');
20119 if (this.over === false && !this.parent()) {
20122 if (this.triggers === false) {
20127 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20128 var triggers = this.trigger ? this.trigger.split(' ') : [];
20129 Roo.each(triggers, function(trigger) {
20131 if (trigger == 'click') {
20132 on_el.on('click', this.toggle, this);
20133 } else if (trigger != 'manual') {
20134 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20135 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20137 on_el.on(eventIn ,this.enter, this);
20138 on_el.on(eventOut, this.leave, this);
20148 toggle : function () {
20149 this.hoverState == 'in' ? this.leave() : this.enter();
20152 enter : function () {
20154 clearTimeout(this.timeout);
20156 this.hoverState = 'in';
20158 if (!this.delay || !this.delay.show) {
20163 this.timeout = setTimeout(function () {
20164 if (_t.hoverState == 'in') {
20167 }, this.delay.show)
20170 leave : function() {
20171 clearTimeout(this.timeout);
20173 this.hoverState = 'out';
20175 if (!this.delay || !this.delay.hide) {
20180 this.timeout = setTimeout(function () {
20181 if (_t.hoverState == 'out') {
20184 }, this.delay.hide)
20188 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20189 * @param {string} (left|right|top|bottom) position
20191 show : function (on_el, placement)
20193 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20194 on_el = on_el || false; // default to false
20197 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20198 on_el = this.parent().el;
20199 } else if (this.over) {
20200 Roo.get(this.over);
20205 this.alignEl = Roo.get( on_el );
20208 this.render(document.body);
20214 if (this.title === false) {
20215 this.headerEl.hide();
20220 this.el.dom.style.display = 'block';
20223 if (this.alignEl) {
20224 this.updatePosition(this.placement, true);
20227 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20228 var es = this.el.getSize();
20229 var x = Roo.lib.Dom.getViewWidth()/2;
20230 var y = Roo.lib.Dom.getViewHeight()/2;
20231 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20236 //var arrow = this.el.select('.arrow',true).first();
20237 //arrow.set(align[2],
20239 this.el.addClass('in');
20243 this.hoverState = 'in';
20246 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20247 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20248 this.maskEl.dom.style.display = 'block';
20249 this.maskEl.addClass('show');
20251 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20253 this.fireEvent('show', this);
20257 * fire this manually after loading a grid in the table for example
20258 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20259 * @param {Boolean} try and move it if we cant get right position.
20261 updatePosition : function(placement, try_move)
20263 // allow for calling with no parameters
20264 placement = placement ? placement : this.placement;
20265 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20267 this.el.removeClass([
20268 'fade','top','bottom', 'left', 'right','in',
20269 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20271 this.el.addClass(placement + ' bs-popover-' + placement);
20273 if (!this.alignEl ) {
20277 switch (placement) {
20279 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20280 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20281 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20282 //normal display... or moved up/down.
20283 this.el.setXY(offset);
20284 var xy = this.alignEl.getAnchorXY('tr', false);
20286 this.arrowEl.setXY(xy);
20289 // continue through...
20290 return this.updatePosition('left', false);
20294 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20295 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20296 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20297 //normal display... or moved up/down.
20298 this.el.setXY(offset);
20299 var xy = this.alignEl.getAnchorXY('tl', false);
20300 xy[0]-=10;xy[1]+=5; // << fix me
20301 this.arrowEl.setXY(xy);
20305 return this.updatePosition('right', false);
20308 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20309 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20310 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20311 //normal display... or moved up/down.
20312 this.el.setXY(offset);
20313 var xy = this.alignEl.getAnchorXY('t', false);
20314 xy[1]-=10; // << fix me
20315 this.arrowEl.setXY(xy);
20319 return this.updatePosition('bottom', false);
20322 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20323 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20324 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20325 //normal display... or moved up/down.
20326 this.el.setXY(offset);
20327 var xy = this.alignEl.getAnchorXY('b', false);
20328 xy[1]+=2; // << fix me
20329 this.arrowEl.setXY(xy);
20333 return this.updatePosition('top', false);
20344 this.el.setXY([0,0]);
20345 this.el.removeClass('in');
20347 this.hoverState = null;
20348 this.maskEl.hide(); // always..
20349 this.fireEvent('hide', this);
20355 Roo.apply(Roo.bootstrap.Popover, {
20358 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20359 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20360 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20361 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20366 clickHander : false,
20369 onMouseDown : function(e)
20371 if (!e.getTarget(".roo-popover")) {
20379 register : function(popup)
20381 if (!Roo.bootstrap.Popover.clickHandler) {
20382 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20384 // hide other popups.
20386 this.popups.push(popup);
20388 hideAll : function()
20390 this.popups.forEach(function(p) {
20398 * Card header - holder for the card header elements.
20403 * @class Roo.bootstrap.PopoverNav
20404 * @extends Roo.bootstrap.NavGroup
20405 * Bootstrap Popover header navigation class
20407 * Create a new Popover Header Navigation
20408 * @param {Object} config The config object
20411 Roo.bootstrap.PopoverNav = function(config){
20412 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20415 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20418 container_method : 'getPopoverHeader'
20436 * @class Roo.bootstrap.Progress
20437 * @extends Roo.bootstrap.Component
20438 * Bootstrap Progress class
20439 * @cfg {Boolean} striped striped of the progress bar
20440 * @cfg {Boolean} active animated of the progress bar
20444 * Create a new Progress
20445 * @param {Object} config The config object
20448 Roo.bootstrap.Progress = function(config){
20449 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20452 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20457 getAutoCreate : function(){
20465 cfg.cls += ' progress-striped';
20469 cfg.cls += ' active';
20488 * @class Roo.bootstrap.ProgressBar
20489 * @extends Roo.bootstrap.Component
20490 * Bootstrap ProgressBar class
20491 * @cfg {Number} aria_valuenow aria-value now
20492 * @cfg {Number} aria_valuemin aria-value min
20493 * @cfg {Number} aria_valuemax aria-value max
20494 * @cfg {String} label label for the progress bar
20495 * @cfg {String} panel (success | info | warning | danger )
20496 * @cfg {String} role role of the progress bar
20497 * @cfg {String} sr_only text
20501 * Create a new ProgressBar
20502 * @param {Object} config The config object
20505 Roo.bootstrap.ProgressBar = function(config){
20506 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20509 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20513 aria_valuemax : 100,
20519 getAutoCreate : function()
20524 cls: 'progress-bar',
20525 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20537 cfg.role = this.role;
20540 if(this.aria_valuenow){
20541 cfg['aria-valuenow'] = this.aria_valuenow;
20544 if(this.aria_valuemin){
20545 cfg['aria-valuemin'] = this.aria_valuemin;
20548 if(this.aria_valuemax){
20549 cfg['aria-valuemax'] = this.aria_valuemax;
20552 if(this.label && !this.sr_only){
20553 cfg.html = this.label;
20557 cfg.cls += ' progress-bar-' + this.panel;
20563 update : function(aria_valuenow)
20565 this.aria_valuenow = aria_valuenow;
20567 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20582 * @class Roo.bootstrap.TabGroup
20583 * @extends Roo.bootstrap.Column
20584 * Bootstrap Column class
20585 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20586 * @cfg {Boolean} carousel true to make the group behave like a carousel
20587 * @cfg {Boolean} bullets show bullets for the panels
20588 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20589 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20590 * @cfg {Boolean} showarrow (true|false) show arrow default true
20593 * Create a new TabGroup
20594 * @param {Object} config The config object
20597 Roo.bootstrap.TabGroup = function(config){
20598 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20600 this.navId = Roo.id();
20603 Roo.bootstrap.TabGroup.register(this);
20607 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20610 transition : false,
20615 slideOnTouch : false,
20618 getAutoCreate : function()
20620 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20622 cfg.cls += ' tab-content';
20624 if (this.carousel) {
20625 cfg.cls += ' carousel slide';
20628 cls : 'carousel-inner',
20632 if(this.bullets && !Roo.isTouch){
20635 cls : 'carousel-bullets',
20639 if(this.bullets_cls){
20640 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20647 cfg.cn[0].cn.push(bullets);
20650 if(this.showarrow){
20651 cfg.cn[0].cn.push({
20653 class : 'carousel-arrow',
20657 class : 'carousel-prev',
20661 class : 'fa fa-chevron-left'
20667 class : 'carousel-next',
20671 class : 'fa fa-chevron-right'
20684 initEvents: function()
20686 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20687 // this.el.on("touchstart", this.onTouchStart, this);
20690 if(this.autoslide){
20693 this.slideFn = window.setInterval(function() {
20694 _this.showPanelNext();
20698 if(this.showarrow){
20699 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20700 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20706 // onTouchStart : function(e, el, o)
20708 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20712 // this.showPanelNext();
20716 getChildContainer : function()
20718 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20722 * register a Navigation item
20723 * @param {Roo.bootstrap.NavItem} the navitem to add
20725 register : function(item)
20727 this.tabs.push( item);
20728 item.navId = this.navId; // not really needed..
20733 getActivePanel : function()
20736 Roo.each(this.tabs, function(t) {
20746 getPanelByName : function(n)
20749 Roo.each(this.tabs, function(t) {
20750 if (t.tabId == n) {
20758 indexOfPanel : function(p)
20761 Roo.each(this.tabs, function(t,i) {
20762 if (t.tabId == p.tabId) {
20771 * show a specific panel
20772 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20773 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20775 showPanel : function (pan)
20777 if(this.transition || typeof(pan) == 'undefined'){
20778 Roo.log("waiting for the transitionend");
20782 if (typeof(pan) == 'number') {
20783 pan = this.tabs[pan];
20786 if (typeof(pan) == 'string') {
20787 pan = this.getPanelByName(pan);
20790 var cur = this.getActivePanel();
20793 Roo.log('pan or acitve pan is undefined');
20797 if (pan.tabId == this.getActivePanel().tabId) {
20801 if (false === cur.fireEvent('beforedeactivate')) {
20805 if(this.bullets > 0 && !Roo.isTouch){
20806 this.setActiveBullet(this.indexOfPanel(pan));
20809 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20811 //class="carousel-item carousel-item-next carousel-item-left"
20813 this.transition = true;
20814 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20815 var lr = dir == 'next' ? 'left' : 'right';
20816 pan.el.addClass(dir); // or prev
20817 pan.el.addClass('carousel-item-' + dir); // or prev
20818 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20819 cur.el.addClass(lr); // or right
20820 pan.el.addClass(lr);
20821 cur.el.addClass('carousel-item-' +lr); // or right
20822 pan.el.addClass('carousel-item-' +lr);
20826 cur.el.on('transitionend', function() {
20827 Roo.log("trans end?");
20829 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20830 pan.setActive(true);
20832 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20833 cur.setActive(false);
20835 _this.transition = false;
20837 }, this, { single: true } );
20842 cur.setActive(false);
20843 pan.setActive(true);
20848 showPanelNext : function()
20850 var i = this.indexOfPanel(this.getActivePanel());
20852 if (i >= this.tabs.length - 1 && !this.autoslide) {
20856 if (i >= this.tabs.length - 1 && this.autoslide) {
20860 this.showPanel(this.tabs[i+1]);
20863 showPanelPrev : function()
20865 var i = this.indexOfPanel(this.getActivePanel());
20867 if (i < 1 && !this.autoslide) {
20871 if (i < 1 && this.autoslide) {
20872 i = this.tabs.length;
20875 this.showPanel(this.tabs[i-1]);
20879 addBullet: function()
20881 if(!this.bullets || Roo.isTouch){
20884 var ctr = this.el.select('.carousel-bullets',true).first();
20885 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20886 var bullet = ctr.createChild({
20887 cls : 'bullet bullet-' + i
20888 },ctr.dom.lastChild);
20893 bullet.on('click', (function(e, el, o, ii, t){
20895 e.preventDefault();
20897 this.showPanel(ii);
20899 if(this.autoslide && this.slideFn){
20900 clearInterval(this.slideFn);
20901 this.slideFn = window.setInterval(function() {
20902 _this.showPanelNext();
20906 }).createDelegate(this, [i, bullet], true));
20911 setActiveBullet : function(i)
20917 Roo.each(this.el.select('.bullet', true).elements, function(el){
20918 el.removeClass('selected');
20921 var bullet = this.el.select('.bullet-' + i, true).first();
20927 bullet.addClass('selected');
20938 Roo.apply(Roo.bootstrap.TabGroup, {
20942 * register a Navigation Group
20943 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20945 register : function(navgrp)
20947 this.groups[navgrp.navId] = navgrp;
20951 * fetch a Navigation Group based on the navigation ID
20952 * if one does not exist , it will get created.
20953 * @param {string} the navgroup to add
20954 * @returns {Roo.bootstrap.NavGroup} the navgroup
20956 get: function(navId) {
20957 if (typeof(this.groups[navId]) == 'undefined') {
20958 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20960 return this.groups[navId] ;
20975 * @class Roo.bootstrap.TabPanel
20976 * @extends Roo.bootstrap.Component
20977 * Bootstrap TabPanel class
20978 * @cfg {Boolean} active panel active
20979 * @cfg {String} html panel content
20980 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20981 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20982 * @cfg {String} href click to link..
20983 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20987 * Create a new TabPanel
20988 * @param {Object} config The config object
20991 Roo.bootstrap.TabPanel = function(config){
20992 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20996 * Fires when the active status changes
20997 * @param {Roo.bootstrap.TabPanel} this
20998 * @param {Boolean} state the new state
21003 * @event beforedeactivate
21004 * Fires before a tab is de-activated - can be used to do validation on a form.
21005 * @param {Roo.bootstrap.TabPanel} this
21006 * @return {Boolean} false if there is an error
21009 'beforedeactivate': true
21012 this.tabId = this.tabId || Roo.id();
21016 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21023 touchSlide : false,
21024 getAutoCreate : function(){
21029 // item is needed for carousel - not sure if it has any effect otherwise
21030 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21031 html: this.html || ''
21035 cfg.cls += ' active';
21039 cfg.tabId = this.tabId;
21047 initEvents: function()
21049 var p = this.parent();
21051 this.navId = this.navId || p.navId;
21053 if (typeof(this.navId) != 'undefined') {
21054 // not really needed.. but just in case.. parent should be a NavGroup.
21055 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21059 var i = tg.tabs.length - 1;
21061 if(this.active && tg.bullets > 0 && i < tg.bullets){
21062 tg.setActiveBullet(i);
21066 this.el.on('click', this.onClick, this);
21068 if(Roo.isTouch && this.touchSlide){
21069 this.el.on("touchstart", this.onTouchStart, this);
21070 this.el.on("touchmove", this.onTouchMove, this);
21071 this.el.on("touchend", this.onTouchEnd, this);
21076 onRender : function(ct, position)
21078 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21081 setActive : function(state)
21083 Roo.log("panel - set active " + this.tabId + "=" + state);
21085 this.active = state;
21087 this.el.removeClass('active');
21089 } else if (!this.el.hasClass('active')) {
21090 this.el.addClass('active');
21093 this.fireEvent('changed', this, state);
21096 onClick : function(e)
21098 e.preventDefault();
21100 if(!this.href.length){
21104 window.location.href = this.href;
21113 onTouchStart : function(e)
21115 this.swiping = false;
21117 this.startX = e.browserEvent.touches[0].clientX;
21118 this.startY = e.browserEvent.touches[0].clientY;
21121 onTouchMove : function(e)
21123 this.swiping = true;
21125 this.endX = e.browserEvent.touches[0].clientX;
21126 this.endY = e.browserEvent.touches[0].clientY;
21129 onTouchEnd : function(e)
21136 var tabGroup = this.parent();
21138 if(this.endX > this.startX){ // swiping right
21139 tabGroup.showPanelPrev();
21143 if(this.startX > this.endX){ // swiping left
21144 tabGroup.showPanelNext();
21163 * @class Roo.bootstrap.DateField
21164 * @extends Roo.bootstrap.Input
21165 * Bootstrap DateField class
21166 * @cfg {Number} weekStart default 0
21167 * @cfg {String} viewMode default empty, (months|years)
21168 * @cfg {String} minViewMode default empty, (months|years)
21169 * @cfg {Number} startDate default -Infinity
21170 * @cfg {Number} endDate default Infinity
21171 * @cfg {Boolean} todayHighlight default false
21172 * @cfg {Boolean} todayBtn default false
21173 * @cfg {Boolean} calendarWeeks default false
21174 * @cfg {Object} daysOfWeekDisabled default empty
21175 * @cfg {Boolean} singleMode default false (true | false)
21177 * @cfg {Boolean} keyboardNavigation default true
21178 * @cfg {String} language default en
21181 * Create a new DateField
21182 * @param {Object} config The config object
21185 Roo.bootstrap.DateField = function(config){
21186 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21190 * Fires when this field show.
21191 * @param {Roo.bootstrap.DateField} this
21192 * @param {Mixed} date The date value
21197 * Fires when this field hide.
21198 * @param {Roo.bootstrap.DateField} this
21199 * @param {Mixed} date The date value
21204 * Fires when select a date.
21205 * @param {Roo.bootstrap.DateField} this
21206 * @param {Mixed} date The date value
21210 * @event beforeselect
21211 * Fires when before select a date.
21212 * @param {Roo.bootstrap.DateField} this
21213 * @param {Mixed} date The date value
21215 beforeselect : true
21219 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21222 * @cfg {String} format
21223 * The default date format string which can be overriden for localization support. The format must be
21224 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21228 * @cfg {String} altFormats
21229 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21230 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21232 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21240 todayHighlight : false,
21246 keyboardNavigation: true,
21248 calendarWeeks: false,
21250 startDate: -Infinity,
21254 daysOfWeekDisabled: [],
21258 singleMode : false,
21260 UTCDate: function()
21262 return new Date(Date.UTC.apply(Date, arguments));
21265 UTCToday: function()
21267 var today = new Date();
21268 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21271 getDate: function() {
21272 var d = this.getUTCDate();
21273 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21276 getUTCDate: function() {
21280 setDate: function(d) {
21281 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21284 setUTCDate: function(d) {
21286 this.setValue(this.formatDate(this.date));
21289 onRender: function(ct, position)
21292 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21294 this.language = this.language || 'en';
21295 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21296 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21298 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21299 this.format = this.format || 'm/d/y';
21300 this.isInline = false;
21301 this.isInput = true;
21302 this.component = this.el.select('.add-on', true).first() || false;
21303 this.component = (this.component && this.component.length === 0) ? false : this.component;
21304 this.hasInput = this.component && this.inputEl().length;
21306 if (typeof(this.minViewMode === 'string')) {
21307 switch (this.minViewMode) {
21309 this.minViewMode = 1;
21312 this.minViewMode = 2;
21315 this.minViewMode = 0;
21320 if (typeof(this.viewMode === 'string')) {
21321 switch (this.viewMode) {
21334 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21336 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21338 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21340 this.picker().on('mousedown', this.onMousedown, this);
21341 this.picker().on('click', this.onClick, this);
21343 this.picker().addClass('datepicker-dropdown');
21345 this.startViewMode = this.viewMode;
21347 if(this.singleMode){
21348 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21349 v.setVisibilityMode(Roo.Element.DISPLAY);
21353 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21354 v.setStyle('width', '189px');
21358 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21359 if(!this.calendarWeeks){
21364 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21365 v.attr('colspan', function(i, val){
21366 return parseInt(val) + 1;
21371 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21373 this.setStartDate(this.startDate);
21374 this.setEndDate(this.endDate);
21376 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21383 if(this.isInline) {
21388 picker : function()
21390 return this.pickerEl;
21391 // return this.el.select('.datepicker', true).first();
21394 fillDow: function()
21396 var dowCnt = this.weekStart;
21405 if(this.calendarWeeks){
21413 while (dowCnt < this.weekStart + 7) {
21417 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21421 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21424 fillMonths: function()
21427 var months = this.picker().select('>.datepicker-months td', true).first();
21429 months.dom.innerHTML = '';
21435 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21438 months.createChild(month);
21445 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;
21447 if (this.date < this.startDate) {
21448 this.viewDate = new Date(this.startDate);
21449 } else if (this.date > this.endDate) {
21450 this.viewDate = new Date(this.endDate);
21452 this.viewDate = new Date(this.date);
21460 var d = new Date(this.viewDate),
21461 year = d.getUTCFullYear(),
21462 month = d.getUTCMonth(),
21463 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21464 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21465 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21466 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21467 currentDate = this.date && this.date.valueOf(),
21468 today = this.UTCToday();
21470 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21472 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21474 // this.picker.select('>tfoot th.today').
21475 // .text(dates[this.language].today)
21476 // .toggle(this.todayBtn !== false);
21478 this.updateNavArrows();
21481 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21483 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21485 prevMonth.setUTCDate(day);
21487 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21489 var nextMonth = new Date(prevMonth);
21491 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21493 nextMonth = nextMonth.valueOf();
21495 var fillMonths = false;
21497 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21499 while(prevMonth.valueOf() <= nextMonth) {
21502 if (prevMonth.getUTCDay() === this.weekStart) {
21504 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21512 if(this.calendarWeeks){
21513 // ISO 8601: First week contains first thursday.
21514 // ISO also states week starts on Monday, but we can be more abstract here.
21516 // Start of current week: based on weekstart/current date
21517 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21518 // Thursday of this week
21519 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21520 // First Thursday of year, year from thursday
21521 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21522 // Calendar week: ms between thursdays, div ms per day, div 7 days
21523 calWeek = (th - yth) / 864e5 / 7 + 1;
21525 fillMonths.cn.push({
21533 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21535 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21538 if (this.todayHighlight &&
21539 prevMonth.getUTCFullYear() == today.getFullYear() &&
21540 prevMonth.getUTCMonth() == today.getMonth() &&
21541 prevMonth.getUTCDate() == today.getDate()) {
21542 clsName += ' today';
21545 if (currentDate && prevMonth.valueOf() === currentDate) {
21546 clsName += ' active';
21549 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21550 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21551 clsName += ' disabled';
21554 fillMonths.cn.push({
21556 cls: 'day ' + clsName,
21557 html: prevMonth.getDate()
21560 prevMonth.setDate(prevMonth.getDate()+1);
21563 var currentYear = this.date && this.date.getUTCFullYear();
21564 var currentMonth = this.date && this.date.getUTCMonth();
21566 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21568 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21569 v.removeClass('active');
21571 if(currentYear === year && k === currentMonth){
21572 v.addClass('active');
21575 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21576 v.addClass('disabled');
21582 year = parseInt(year/10, 10) * 10;
21584 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21586 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21589 for (var i = -1; i < 11; i++) {
21590 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21592 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21600 showMode: function(dir)
21603 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21606 Roo.each(this.picker().select('>div',true).elements, function(v){
21607 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21610 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21615 if(this.isInline) {
21619 this.picker().removeClass(['bottom', 'top']);
21621 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21623 * place to the top of element!
21627 this.picker().addClass('top');
21628 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21633 this.picker().addClass('bottom');
21635 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21638 parseDate : function(value)
21640 if(!value || value instanceof Date){
21643 var v = Date.parseDate(value, this.format);
21644 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21645 v = Date.parseDate(value, 'Y-m-d');
21647 if(!v && this.altFormats){
21648 if(!this.altFormatsArray){
21649 this.altFormatsArray = this.altFormats.split("|");
21651 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21652 v = Date.parseDate(value, this.altFormatsArray[i]);
21658 formatDate : function(date, fmt)
21660 return (!date || !(date instanceof Date)) ?
21661 date : date.dateFormat(fmt || this.format);
21664 onFocus : function()
21666 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21670 onBlur : function()
21672 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21674 var d = this.inputEl().getValue();
21681 showPopup : function()
21683 this.picker().show();
21687 this.fireEvent('showpopup', this, this.date);
21690 hidePopup : function()
21692 if(this.isInline) {
21695 this.picker().hide();
21696 this.viewMode = this.startViewMode;
21699 this.fireEvent('hidepopup', this, this.date);
21703 onMousedown: function(e)
21705 e.stopPropagation();
21706 e.preventDefault();
21711 Roo.bootstrap.DateField.superclass.keyup.call(this);
21715 setValue: function(v)
21717 if(this.fireEvent('beforeselect', this, v) !== false){
21718 var d = new Date(this.parseDate(v) ).clearTime();
21720 if(isNaN(d.getTime())){
21721 this.date = this.viewDate = '';
21722 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21726 v = this.formatDate(d);
21728 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21730 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21734 this.fireEvent('select', this, this.date);
21738 getValue: function()
21740 return this.formatDate(this.date);
21743 fireKey: function(e)
21745 if (!this.picker().isVisible()){
21746 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21752 var dateChanged = false,
21754 newDate, newViewDate;
21759 e.preventDefault();
21763 if (!this.keyboardNavigation) {
21766 dir = e.keyCode == 37 ? -1 : 1;
21769 newDate = this.moveYear(this.date, dir);
21770 newViewDate = this.moveYear(this.viewDate, dir);
21771 } else if (e.shiftKey){
21772 newDate = this.moveMonth(this.date, dir);
21773 newViewDate = this.moveMonth(this.viewDate, dir);
21775 newDate = new Date(this.date);
21776 newDate.setUTCDate(this.date.getUTCDate() + dir);
21777 newViewDate = new Date(this.viewDate);
21778 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21780 if (this.dateWithinRange(newDate)){
21781 this.date = newDate;
21782 this.viewDate = newViewDate;
21783 this.setValue(this.formatDate(this.date));
21785 e.preventDefault();
21786 dateChanged = true;
21791 if (!this.keyboardNavigation) {
21794 dir = e.keyCode == 38 ? -1 : 1;
21796 newDate = this.moveYear(this.date, dir);
21797 newViewDate = this.moveYear(this.viewDate, dir);
21798 } else if (e.shiftKey){
21799 newDate = this.moveMonth(this.date, dir);
21800 newViewDate = this.moveMonth(this.viewDate, dir);
21802 newDate = new Date(this.date);
21803 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21804 newViewDate = new Date(this.viewDate);
21805 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21807 if (this.dateWithinRange(newDate)){
21808 this.date = newDate;
21809 this.viewDate = newViewDate;
21810 this.setValue(this.formatDate(this.date));
21812 e.preventDefault();
21813 dateChanged = true;
21817 this.setValue(this.formatDate(this.date));
21819 e.preventDefault();
21822 this.setValue(this.formatDate(this.date));
21836 onClick: function(e)
21838 e.stopPropagation();
21839 e.preventDefault();
21841 var target = e.getTarget();
21843 if(target.nodeName.toLowerCase() === 'i'){
21844 target = Roo.get(target).dom.parentNode;
21847 var nodeName = target.nodeName;
21848 var className = target.className;
21849 var html = target.innerHTML;
21850 //Roo.log(nodeName);
21852 switch(nodeName.toLowerCase()) {
21854 switch(className) {
21860 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21861 switch(this.viewMode){
21863 this.viewDate = this.moveMonth(this.viewDate, dir);
21867 this.viewDate = this.moveYear(this.viewDate, dir);
21873 var date = new Date();
21874 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21876 this.setValue(this.formatDate(this.date));
21883 if (className.indexOf('disabled') < 0) {
21884 this.viewDate.setUTCDate(1);
21885 if (className.indexOf('month') > -1) {
21886 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21888 var year = parseInt(html, 10) || 0;
21889 this.viewDate.setUTCFullYear(year);
21893 if(this.singleMode){
21894 this.setValue(this.formatDate(this.viewDate));
21905 //Roo.log(className);
21906 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21907 var day = parseInt(html, 10) || 1;
21908 var year = (this.viewDate || new Date()).getUTCFullYear(),
21909 month = (this.viewDate || new Date()).getUTCMonth();
21911 if (className.indexOf('old') > -1) {
21918 } else if (className.indexOf('new') > -1) {
21926 //Roo.log([year,month,day]);
21927 this.date = this.UTCDate(year, month, day,0,0,0,0);
21928 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21930 //Roo.log(this.formatDate(this.date));
21931 this.setValue(this.formatDate(this.date));
21938 setStartDate: function(startDate)
21940 this.startDate = startDate || -Infinity;
21941 if (this.startDate !== -Infinity) {
21942 this.startDate = this.parseDate(this.startDate);
21945 this.updateNavArrows();
21948 setEndDate: function(endDate)
21950 this.endDate = endDate || Infinity;
21951 if (this.endDate !== Infinity) {
21952 this.endDate = this.parseDate(this.endDate);
21955 this.updateNavArrows();
21958 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21960 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21961 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21962 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21964 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21965 return parseInt(d, 10);
21968 this.updateNavArrows();
21971 updateNavArrows: function()
21973 if(this.singleMode){
21977 var d = new Date(this.viewDate),
21978 year = d.getUTCFullYear(),
21979 month = d.getUTCMonth();
21981 Roo.each(this.picker().select('.prev', true).elements, function(v){
21983 switch (this.viewMode) {
21986 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21992 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21999 Roo.each(this.picker().select('.next', true).elements, function(v){
22001 switch (this.viewMode) {
22004 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22010 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22018 moveMonth: function(date, dir)
22023 var new_date = new Date(date.valueOf()),
22024 day = new_date.getUTCDate(),
22025 month = new_date.getUTCMonth(),
22026 mag = Math.abs(dir),
22028 dir = dir > 0 ? 1 : -1;
22031 // If going back one month, make sure month is not current month
22032 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22034 return new_date.getUTCMonth() == month;
22036 // If going forward one month, make sure month is as expected
22037 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22039 return new_date.getUTCMonth() != new_month;
22041 new_month = month + dir;
22042 new_date.setUTCMonth(new_month);
22043 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22044 if (new_month < 0 || new_month > 11) {
22045 new_month = (new_month + 12) % 12;
22048 // For magnitudes >1, move one month at a time...
22049 for (var i=0; i<mag; i++) {
22050 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22051 new_date = this.moveMonth(new_date, dir);
22053 // ...then reset the day, keeping it in the new month
22054 new_month = new_date.getUTCMonth();
22055 new_date.setUTCDate(day);
22057 return new_month != new_date.getUTCMonth();
22060 // Common date-resetting loop -- if date is beyond end of month, make it
22063 new_date.setUTCDate(--day);
22064 new_date.setUTCMonth(new_month);
22069 moveYear: function(date, dir)
22071 return this.moveMonth(date, dir*12);
22074 dateWithinRange: function(date)
22076 return date >= this.startDate && date <= this.endDate;
22082 this.picker().remove();
22085 validateValue : function(value)
22087 if(this.getVisibilityEl().hasClass('hidden')){
22091 if(value.length < 1) {
22092 if(this.allowBlank){
22098 if(value.length < this.minLength){
22101 if(value.length > this.maxLength){
22105 var vt = Roo.form.VTypes;
22106 if(!vt[this.vtype](value, this)){
22110 if(typeof this.validator == "function"){
22111 var msg = this.validator(value);
22117 if(this.regex && !this.regex.test(value)){
22121 if(typeof(this.parseDate(value)) == 'undefined'){
22125 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22129 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22139 this.date = this.viewDate = '';
22141 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22146 Roo.apply(Roo.bootstrap.DateField, {
22157 html: '<i class="fa fa-arrow-left"/>'
22167 html: '<i class="fa fa-arrow-right"/>'
22209 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22210 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22211 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22212 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22213 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22226 navFnc: 'FullYear',
22231 navFnc: 'FullYear',
22236 Roo.apply(Roo.bootstrap.DateField, {
22240 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22244 cls: 'datepicker-days',
22248 cls: 'table-condensed',
22250 Roo.bootstrap.DateField.head,
22254 Roo.bootstrap.DateField.footer
22261 cls: 'datepicker-months',
22265 cls: 'table-condensed',
22267 Roo.bootstrap.DateField.head,
22268 Roo.bootstrap.DateField.content,
22269 Roo.bootstrap.DateField.footer
22276 cls: 'datepicker-years',
22280 cls: 'table-condensed',
22282 Roo.bootstrap.DateField.head,
22283 Roo.bootstrap.DateField.content,
22284 Roo.bootstrap.DateField.footer
22303 * @class Roo.bootstrap.TimeField
22304 * @extends Roo.bootstrap.Input
22305 * Bootstrap DateField class
22309 * Create a new TimeField
22310 * @param {Object} config The config object
22313 Roo.bootstrap.TimeField = function(config){
22314 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22318 * Fires when this field show.
22319 * @param {Roo.bootstrap.DateField} thisthis
22320 * @param {Mixed} date The date value
22325 * Fires when this field hide.
22326 * @param {Roo.bootstrap.DateField} this
22327 * @param {Mixed} date The date value
22332 * Fires when select a date.
22333 * @param {Roo.bootstrap.DateField} this
22334 * @param {Mixed} date The date value
22340 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22343 * @cfg {String} format
22344 * The default time format string which can be overriden for localization support. The format must be
22345 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22349 getAutoCreate : function()
22351 this.after = '<i class="fa far fa-clock"></i>';
22352 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22356 onRender: function(ct, position)
22359 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22361 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22363 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22365 this.pop = this.picker().select('>.datepicker-time',true).first();
22366 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22368 this.picker().on('mousedown', this.onMousedown, this);
22369 this.picker().on('click', this.onClick, this);
22371 this.picker().addClass('datepicker-dropdown');
22376 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22377 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22378 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22379 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22380 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22381 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22385 fireKey: function(e){
22386 if (!this.picker().isVisible()){
22387 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22393 e.preventDefault();
22401 this.onTogglePeriod();
22404 this.onIncrementMinutes();
22407 this.onDecrementMinutes();
22416 onClick: function(e) {
22417 e.stopPropagation();
22418 e.preventDefault();
22421 picker : function()
22423 return this.pickerEl;
22426 fillTime: function()
22428 var time = this.pop.select('tbody', true).first();
22430 time.dom.innerHTML = '';
22445 cls: 'hours-up fa fas fa-chevron-up'
22465 cls: 'minutes-up fa fas fa-chevron-up'
22486 cls: 'timepicker-hour',
22501 cls: 'timepicker-minute',
22516 cls: 'btn btn-primary period',
22538 cls: 'hours-down fa fas fa-chevron-down'
22558 cls: 'minutes-down fa fas fa-chevron-down'
22576 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22583 var hours = this.time.getHours();
22584 var minutes = this.time.getMinutes();
22597 hours = hours - 12;
22601 hours = '0' + hours;
22605 minutes = '0' + minutes;
22608 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22609 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22610 this.pop.select('button', true).first().dom.innerHTML = period;
22616 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22618 var cls = ['bottom'];
22620 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22627 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22631 //this.picker().setXY(20000,20000);
22632 this.picker().addClass(cls.join('-'));
22636 Roo.each(cls, function(c){
22641 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22642 //_this.picker().setTop(_this.inputEl().getHeight());
22646 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22648 //_this.picker().setTop(0 - _this.picker().getHeight());
22653 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22657 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22665 onFocus : function()
22667 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22671 onBlur : function()
22673 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22679 this.picker().show();
22684 this.fireEvent('show', this, this.date);
22689 this.picker().hide();
22692 this.fireEvent('hide', this, this.date);
22695 setTime : function()
22698 this.setValue(this.time.format(this.format));
22700 this.fireEvent('select', this, this.date);
22705 onMousedown: function(e){
22706 e.stopPropagation();
22707 e.preventDefault();
22710 onIncrementHours: function()
22712 Roo.log('onIncrementHours');
22713 this.time = this.time.add(Date.HOUR, 1);
22718 onDecrementHours: function()
22720 Roo.log('onDecrementHours');
22721 this.time = this.time.add(Date.HOUR, -1);
22725 onIncrementMinutes: function()
22727 Roo.log('onIncrementMinutes');
22728 this.time = this.time.add(Date.MINUTE, 1);
22732 onDecrementMinutes: function()
22734 Roo.log('onDecrementMinutes');
22735 this.time = this.time.add(Date.MINUTE, -1);
22739 onTogglePeriod: function()
22741 Roo.log('onTogglePeriod');
22742 this.time = this.time.add(Date.HOUR, 12);
22750 Roo.apply(Roo.bootstrap.TimeField, {
22754 cls: 'datepicker dropdown-menu',
22758 cls: 'datepicker-time',
22762 cls: 'table-condensed',
22791 cls: 'btn btn-info ok',
22819 * @class Roo.bootstrap.MonthField
22820 * @extends Roo.bootstrap.Input
22821 * Bootstrap MonthField class
22823 * @cfg {String} language default en
22826 * Create a new MonthField
22827 * @param {Object} config The config object
22830 Roo.bootstrap.MonthField = function(config){
22831 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22836 * Fires when this field show.
22837 * @param {Roo.bootstrap.MonthField} this
22838 * @param {Mixed} date The date value
22843 * Fires when this field hide.
22844 * @param {Roo.bootstrap.MonthField} this
22845 * @param {Mixed} date The date value
22850 * Fires when select a date.
22851 * @param {Roo.bootstrap.MonthField} this
22852 * @param {String} oldvalue The old value
22853 * @param {String} newvalue The new value
22859 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22861 onRender: function(ct, position)
22864 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22866 this.language = this.language || 'en';
22867 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22868 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22870 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22871 this.isInline = false;
22872 this.isInput = true;
22873 this.component = this.el.select('.add-on', true).first() || false;
22874 this.component = (this.component && this.component.length === 0) ? false : this.component;
22875 this.hasInput = this.component && this.inputEL().length;
22877 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22879 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22881 this.picker().on('mousedown', this.onMousedown, this);
22882 this.picker().on('click', this.onClick, this);
22884 this.picker().addClass('datepicker-dropdown');
22886 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22887 v.setStyle('width', '189px');
22894 if(this.isInline) {
22900 setValue: function(v, suppressEvent)
22902 var o = this.getValue();
22904 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22908 if(suppressEvent !== true){
22909 this.fireEvent('select', this, o, v);
22914 getValue: function()
22919 onClick: function(e)
22921 e.stopPropagation();
22922 e.preventDefault();
22924 var target = e.getTarget();
22926 if(target.nodeName.toLowerCase() === 'i'){
22927 target = Roo.get(target).dom.parentNode;
22930 var nodeName = target.nodeName;
22931 var className = target.className;
22932 var html = target.innerHTML;
22934 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22938 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22940 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22946 picker : function()
22948 return this.pickerEl;
22951 fillMonths: function()
22954 var months = this.picker().select('>.datepicker-months td', true).first();
22956 months.dom.innerHTML = '';
22962 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22965 months.createChild(month);
22974 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22975 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22978 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22979 e.removeClass('active');
22981 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22982 e.addClass('active');
22989 if(this.isInline) {
22993 this.picker().removeClass(['bottom', 'top']);
22995 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22997 * place to the top of element!
23001 this.picker().addClass('top');
23002 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23007 this.picker().addClass('bottom');
23009 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23012 onFocus : function()
23014 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23018 onBlur : function()
23020 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23022 var d = this.inputEl().getValue();
23031 this.picker().show();
23032 this.picker().select('>.datepicker-months', true).first().show();
23036 this.fireEvent('show', this, this.date);
23041 if(this.isInline) {
23044 this.picker().hide();
23045 this.fireEvent('hide', this, this.date);
23049 onMousedown: function(e)
23051 e.stopPropagation();
23052 e.preventDefault();
23057 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23061 fireKey: function(e)
23063 if (!this.picker().isVisible()){
23064 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23075 e.preventDefault();
23079 dir = e.keyCode == 37 ? -1 : 1;
23081 this.vIndex = this.vIndex + dir;
23083 if(this.vIndex < 0){
23087 if(this.vIndex > 11){
23091 if(isNaN(this.vIndex)){
23095 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23101 dir = e.keyCode == 38 ? -1 : 1;
23103 this.vIndex = this.vIndex + dir * 4;
23105 if(this.vIndex < 0){
23109 if(this.vIndex > 11){
23113 if(isNaN(this.vIndex)){
23117 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23122 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23123 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23127 e.preventDefault();
23130 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23131 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23147 this.picker().remove();
23152 Roo.apply(Roo.bootstrap.MonthField, {
23171 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23172 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23177 Roo.apply(Roo.bootstrap.MonthField, {
23181 cls: 'datepicker dropdown-menu roo-dynamic',
23185 cls: 'datepicker-months',
23189 cls: 'table-condensed',
23191 Roo.bootstrap.DateField.content
23211 * @class Roo.bootstrap.CheckBox
23212 * @extends Roo.bootstrap.Input
23213 * Bootstrap CheckBox class
23215 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23216 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23217 * @cfg {String} boxLabel The text that appears beside the checkbox
23218 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23219 * @cfg {Boolean} checked initnal the element
23220 * @cfg {Boolean} inline inline the element (default false)
23221 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23222 * @cfg {String} tooltip label tooltip
23225 * Create a new CheckBox
23226 * @param {Object} config The config object
23229 Roo.bootstrap.CheckBox = function(config){
23230 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23235 * Fires when the element is checked or unchecked.
23236 * @param {Roo.bootstrap.CheckBox} this This input
23237 * @param {Boolean} checked The new checked value
23242 * Fires when the element is click.
23243 * @param {Roo.bootstrap.CheckBox} this This input
23250 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23252 inputType: 'checkbox',
23261 // checkbox success does not make any sense really..
23266 getAutoCreate : function()
23268 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23274 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23277 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23283 type : this.inputType,
23284 value : this.inputValue,
23285 cls : 'roo-' + this.inputType, //'form-box',
23286 placeholder : this.placeholder || ''
23290 if(this.inputType != 'radio'){
23294 cls : 'roo-hidden-value',
23295 value : this.checked ? this.inputValue : this.valueOff
23300 if (this.weight) { // Validity check?
23301 cfg.cls += " " + this.inputType + "-" + this.weight;
23304 if (this.disabled) {
23305 input.disabled=true;
23309 input.checked = this.checked;
23314 input.name = this.name;
23316 if(this.inputType != 'radio'){
23317 hidden.name = this.name;
23318 input.name = '_hidden_' + this.name;
23323 input.cls += ' input-' + this.size;
23328 ['xs','sm','md','lg'].map(function(size){
23329 if (settings[size]) {
23330 cfg.cls += ' col-' + size + '-' + settings[size];
23334 var inputblock = input;
23336 if (this.before || this.after) {
23339 cls : 'input-group',
23344 inputblock.cn.push({
23346 cls : 'input-group-addon',
23351 inputblock.cn.push(input);
23353 if(this.inputType != 'radio'){
23354 inputblock.cn.push(hidden);
23358 inputblock.cn.push({
23360 cls : 'input-group-addon',
23366 var boxLabelCfg = false;
23372 //'for': id, // box label is handled by onclick - so no for...
23374 html: this.boxLabel
23377 boxLabelCfg.tooltip = this.tooltip;
23383 if (align ==='left' && this.fieldLabel.length) {
23384 // Roo.log("left and has label");
23389 cls : 'control-label',
23390 html : this.fieldLabel
23401 cfg.cn[1].cn.push(boxLabelCfg);
23404 if(this.labelWidth > 12){
23405 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23408 if(this.labelWidth < 13 && this.labelmd == 0){
23409 this.labelmd = this.labelWidth;
23412 if(this.labellg > 0){
23413 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23414 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23417 if(this.labelmd > 0){
23418 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23419 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23422 if(this.labelsm > 0){
23423 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23424 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23427 if(this.labelxs > 0){
23428 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23429 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23432 } else if ( this.fieldLabel.length) {
23433 // Roo.log(" label");
23437 tag: this.boxLabel ? 'span' : 'label',
23439 cls: 'control-label box-input-label',
23440 //cls : 'input-group-addon',
23441 html : this.fieldLabel
23448 cfg.cn.push(boxLabelCfg);
23453 // Roo.log(" no label && no align");
23454 cfg.cn = [ inputblock ] ;
23456 cfg.cn.push(boxLabelCfg);
23464 if(this.inputType != 'radio'){
23465 cfg.cn.push(hidden);
23473 * return the real input element.
23475 inputEl: function ()
23477 return this.el.select('input.roo-' + this.inputType,true).first();
23479 hiddenEl: function ()
23481 return this.el.select('input.roo-hidden-value',true).first();
23484 labelEl: function()
23486 return this.el.select('label.control-label',true).first();
23488 /* depricated... */
23492 return this.labelEl();
23495 boxLabelEl: function()
23497 return this.el.select('label.box-label',true).first();
23500 initEvents : function()
23502 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23504 this.inputEl().on('click', this.onClick, this);
23506 if (this.boxLabel) {
23507 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23510 this.startValue = this.getValue();
23513 Roo.bootstrap.CheckBox.register(this);
23517 onClick : function(e)
23519 if(this.fireEvent('click', this, e) !== false){
23520 this.setChecked(!this.checked);
23525 setChecked : function(state,suppressEvent)
23527 this.startValue = this.getValue();
23529 if(this.inputType == 'radio'){
23531 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23532 e.dom.checked = false;
23535 this.inputEl().dom.checked = true;
23537 this.inputEl().dom.value = this.inputValue;
23539 if(suppressEvent !== true){
23540 this.fireEvent('check', this, true);
23548 this.checked = state;
23550 this.inputEl().dom.checked = state;
23553 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23555 if(suppressEvent !== true){
23556 this.fireEvent('check', this, state);
23562 getValue : function()
23564 if(this.inputType == 'radio'){
23565 return this.getGroupValue();
23568 return this.hiddenEl().dom.value;
23572 getGroupValue : function()
23574 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23578 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23581 setValue : function(v,suppressEvent)
23583 if(this.inputType == 'radio'){
23584 this.setGroupValue(v, suppressEvent);
23588 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23593 setGroupValue : function(v, suppressEvent)
23595 this.startValue = this.getValue();
23597 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23598 e.dom.checked = false;
23600 if(e.dom.value == v){
23601 e.dom.checked = true;
23605 if(suppressEvent !== true){
23606 this.fireEvent('check', this, true);
23614 validate : function()
23616 if(this.getVisibilityEl().hasClass('hidden')){
23622 (this.inputType == 'radio' && this.validateRadio()) ||
23623 (this.inputType == 'checkbox' && this.validateCheckbox())
23629 this.markInvalid();
23633 validateRadio : function()
23635 if(this.getVisibilityEl().hasClass('hidden')){
23639 if(this.allowBlank){
23645 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23646 if(!e.dom.checked){
23658 validateCheckbox : function()
23661 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23662 //return (this.getValue() == this.inputValue) ? true : false;
23665 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23673 for(var i in group){
23674 if(group[i].el.isVisible(true)){
23682 for(var i in group){
23687 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23694 * Mark this field as valid
23696 markValid : function()
23700 this.fireEvent('valid', this);
23702 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23705 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23712 if(this.inputType == 'radio'){
23713 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23714 var fg = e.findParent('.form-group', false, true);
23715 if (Roo.bootstrap.version == 3) {
23716 fg.removeClass([_this.invalidClass, _this.validClass]);
23717 fg.addClass(_this.validClass);
23719 fg.removeClass(['is-valid', 'is-invalid']);
23720 fg.addClass('is-valid');
23728 var fg = this.el.findParent('.form-group', false, true);
23729 if (Roo.bootstrap.version == 3) {
23730 fg.removeClass([this.invalidClass, this.validClass]);
23731 fg.addClass(this.validClass);
23733 fg.removeClass(['is-valid', 'is-invalid']);
23734 fg.addClass('is-valid');
23739 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23745 for(var i in group){
23746 var fg = group[i].el.findParent('.form-group', false, true);
23747 if (Roo.bootstrap.version == 3) {
23748 fg.removeClass([this.invalidClass, this.validClass]);
23749 fg.addClass(this.validClass);
23751 fg.removeClass(['is-valid', 'is-invalid']);
23752 fg.addClass('is-valid');
23758 * Mark this field as invalid
23759 * @param {String} msg The validation message
23761 markInvalid : function(msg)
23763 if(this.allowBlank){
23769 this.fireEvent('invalid', this, msg);
23771 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23774 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23778 label.markInvalid();
23781 if(this.inputType == 'radio'){
23783 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23784 var fg = e.findParent('.form-group', false, true);
23785 if (Roo.bootstrap.version == 3) {
23786 fg.removeClass([_this.invalidClass, _this.validClass]);
23787 fg.addClass(_this.invalidClass);
23789 fg.removeClass(['is-invalid', 'is-valid']);
23790 fg.addClass('is-invalid');
23798 var fg = this.el.findParent('.form-group', false, true);
23799 if (Roo.bootstrap.version == 3) {
23800 fg.removeClass([_this.invalidClass, _this.validClass]);
23801 fg.addClass(_this.invalidClass);
23803 fg.removeClass(['is-invalid', 'is-valid']);
23804 fg.addClass('is-invalid');
23809 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23815 for(var i in group){
23816 var fg = group[i].el.findParent('.form-group', false, true);
23817 if (Roo.bootstrap.version == 3) {
23818 fg.removeClass([_this.invalidClass, _this.validClass]);
23819 fg.addClass(_this.invalidClass);
23821 fg.removeClass(['is-invalid', 'is-valid']);
23822 fg.addClass('is-invalid');
23828 clearInvalid : function()
23830 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23832 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23834 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23836 if (label && label.iconEl) {
23837 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23838 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23842 disable : function()
23844 if(this.inputType != 'radio'){
23845 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23852 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23853 _this.getActionEl().addClass(this.disabledClass);
23854 e.dom.disabled = true;
23858 this.disabled = true;
23859 this.fireEvent("disable", this);
23863 enable : function()
23865 if(this.inputType != 'radio'){
23866 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23873 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23874 _this.getActionEl().removeClass(this.disabledClass);
23875 e.dom.disabled = false;
23879 this.disabled = false;
23880 this.fireEvent("enable", this);
23884 setBoxLabel : function(v)
23889 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23895 Roo.apply(Roo.bootstrap.CheckBox, {
23900 * register a CheckBox Group
23901 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23903 register : function(checkbox)
23905 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23906 this.groups[checkbox.groupId] = {};
23909 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23913 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23917 * fetch a CheckBox Group based on the group ID
23918 * @param {string} the group ID
23919 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23921 get: function(groupId) {
23922 if (typeof(this.groups[groupId]) == 'undefined') {
23926 return this.groups[groupId] ;
23939 * @class Roo.bootstrap.Radio
23940 * @extends Roo.bootstrap.Component
23941 * Bootstrap Radio class
23942 * @cfg {String} boxLabel - the label associated
23943 * @cfg {String} value - the value of radio
23946 * Create a new Radio
23947 * @param {Object} config The config object
23949 Roo.bootstrap.Radio = function(config){
23950 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23954 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23960 getAutoCreate : function()
23964 cls : 'form-group radio',
23969 html : this.boxLabel
23977 initEvents : function()
23979 this.parent().register(this);
23981 this.el.on('click', this.onClick, this);
23985 onClick : function(e)
23987 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23988 this.setChecked(true);
23992 setChecked : function(state, suppressEvent)
23994 this.parent().setValue(this.value, suppressEvent);
23998 setBoxLabel : function(v)
24003 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24018 * @class Roo.bootstrap.SecurePass
24019 * @extends Roo.bootstrap.Input
24020 * Bootstrap SecurePass class
24024 * Create a new SecurePass
24025 * @param {Object} config The config object
24028 Roo.bootstrap.SecurePass = function (config) {
24029 // these go here, so the translation tool can replace them..
24031 PwdEmpty: "Please type a password, and then retype it to confirm.",
24032 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24033 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24034 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24035 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24036 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24037 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24038 TooWeak: "Your password is Too Weak."
24040 this.meterLabel = "Password strength:";
24041 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24042 this.meterClass = [
24043 "roo-password-meter-tooweak",
24044 "roo-password-meter-weak",
24045 "roo-password-meter-medium",
24046 "roo-password-meter-strong",
24047 "roo-password-meter-grey"
24052 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24055 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24057 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24059 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24060 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24061 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24062 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24063 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24064 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24065 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24075 * @cfg {String/Object} Label for the strength meter (defaults to
24076 * 'Password strength:')
24081 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24082 * ['Weak', 'Medium', 'Strong'])
24085 pwdStrengths: false,
24098 initEvents: function ()
24100 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24102 if (this.el.is('input[type=password]') && Roo.isSafari) {
24103 this.el.on('keydown', this.SafariOnKeyDown, this);
24106 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24109 onRender: function (ct, position)
24111 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24112 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24113 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24115 this.trigger.createChild({
24120 cls: 'roo-password-meter-grey col-xs-12',
24123 //width: this.meterWidth + 'px'
24127 cls: 'roo-password-meter-text'
24133 if (this.hideTrigger) {
24134 this.trigger.setDisplayed(false);
24136 this.setSize(this.width || '', this.height || '');
24139 onDestroy: function ()
24141 if (this.trigger) {
24142 this.trigger.removeAllListeners();
24143 this.trigger.remove();
24146 this.wrap.remove();
24148 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24151 checkStrength: function ()
24153 var pwd = this.inputEl().getValue();
24154 if (pwd == this._lastPwd) {
24159 if (this.ClientSideStrongPassword(pwd)) {
24161 } else if (this.ClientSideMediumPassword(pwd)) {
24163 } else if (this.ClientSideWeakPassword(pwd)) {
24169 Roo.log('strength1: ' + strength);
24171 //var pm = this.trigger.child('div/div/div').dom;
24172 var pm = this.trigger.child('div/div');
24173 pm.removeClass(this.meterClass);
24174 pm.addClass(this.meterClass[strength]);
24177 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24179 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24181 this._lastPwd = pwd;
24185 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24187 this._lastPwd = '';
24189 var pm = this.trigger.child('div/div');
24190 pm.removeClass(this.meterClass);
24191 pm.addClass('roo-password-meter-grey');
24194 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24197 this.inputEl().dom.type='password';
24200 validateValue: function (value)
24202 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24205 if (value.length == 0) {
24206 if (this.allowBlank) {
24207 this.clearInvalid();
24211 this.markInvalid(this.errors.PwdEmpty);
24212 this.errorMsg = this.errors.PwdEmpty;
24220 if (!value.match(/[\x21-\x7e]+/)) {
24221 this.markInvalid(this.errors.PwdBadChar);
24222 this.errorMsg = this.errors.PwdBadChar;
24225 if (value.length < 6) {
24226 this.markInvalid(this.errors.PwdShort);
24227 this.errorMsg = this.errors.PwdShort;
24230 if (value.length > 16) {
24231 this.markInvalid(this.errors.PwdLong);
24232 this.errorMsg = this.errors.PwdLong;
24236 if (this.ClientSideStrongPassword(value)) {
24238 } else if (this.ClientSideMediumPassword(value)) {
24240 } else if (this.ClientSideWeakPassword(value)) {
24247 if (strength < 2) {
24248 //this.markInvalid(this.errors.TooWeak);
24249 this.errorMsg = this.errors.TooWeak;
24254 console.log('strength2: ' + strength);
24256 //var pm = this.trigger.child('div/div/div').dom;
24258 var pm = this.trigger.child('div/div');
24259 pm.removeClass(this.meterClass);
24260 pm.addClass(this.meterClass[strength]);
24262 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24264 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24266 this.errorMsg = '';
24270 CharacterSetChecks: function (type)
24273 this.fResult = false;
24276 isctype: function (character, type)
24279 case this.kCapitalLetter:
24280 if (character >= 'A' && character <= 'Z') {
24285 case this.kSmallLetter:
24286 if (character >= 'a' && character <= 'z') {
24292 if (character >= '0' && character <= '9') {
24297 case this.kPunctuation:
24298 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24309 IsLongEnough: function (pwd, size)
24311 return !(pwd == null || isNaN(size) || pwd.length < size);
24314 SpansEnoughCharacterSets: function (word, nb)
24316 if (!this.IsLongEnough(word, nb))
24321 var characterSetChecks = new Array(
24322 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24323 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24326 for (var index = 0; index < word.length; ++index) {
24327 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24328 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24329 characterSetChecks[nCharSet].fResult = true;
24336 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24337 if (characterSetChecks[nCharSet].fResult) {
24342 if (nCharSets < nb) {
24348 ClientSideStrongPassword: function (pwd)
24350 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24353 ClientSideMediumPassword: function (pwd)
24355 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24358 ClientSideWeakPassword: function (pwd)
24360 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24363 })//<script type="text/javascript">
24366 * Based Ext JS Library 1.1.1
24367 * Copyright(c) 2006-2007, Ext JS, LLC.
24373 * @class Roo.HtmlEditorCore
24374 * @extends Roo.Component
24375 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24377 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24380 Roo.HtmlEditorCore = function(config){
24383 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24388 * @event initialize
24389 * Fires when the editor is fully initialized (including the iframe)
24390 * @param {Roo.HtmlEditorCore} this
24395 * Fires when the editor is first receives the focus. Any insertion must wait
24396 * until after this event.
24397 * @param {Roo.HtmlEditorCore} this
24401 * @event beforesync
24402 * Fires before the textarea is updated with content from the editor iframe. Return false
24403 * to cancel the sync.
24404 * @param {Roo.HtmlEditorCore} this
24405 * @param {String} html
24409 * @event beforepush
24410 * Fires before the iframe editor is updated with content from the textarea. Return false
24411 * to cancel the push.
24412 * @param {Roo.HtmlEditorCore} this
24413 * @param {String} html
24418 * Fires when the textarea is updated with content from the editor iframe.
24419 * @param {Roo.HtmlEditorCore} this
24420 * @param {String} html
24425 * Fires when the iframe editor is updated with content from the textarea.
24426 * @param {Roo.HtmlEditorCore} this
24427 * @param {String} html
24432 * @event editorevent
24433 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24434 * @param {Roo.HtmlEditorCore} this
24440 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24442 // defaults : white / black...
24443 this.applyBlacklists();
24450 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24454 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24460 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24465 * @cfg {Number} height (in pixels)
24469 * @cfg {Number} width (in pixels)
24474 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24477 stylesheets: false,
24482 // private properties
24483 validationEvent : false,
24485 initialized : false,
24487 sourceEditMode : false,
24488 onFocus : Roo.emptyFn,
24490 hideMode:'offsets',
24494 // blacklist + whitelisted elements..
24501 * Protected method that will not generally be called directly. It
24502 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24503 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24505 getDocMarkup : function(){
24509 // inherit styels from page...??
24510 if (this.stylesheets === false) {
24512 Roo.get(document.head).select('style').each(function(node) {
24513 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24516 Roo.get(document.head).select('link').each(function(node) {
24517 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24520 } else if (!this.stylesheets.length) {
24522 st = '<style type="text/css">' +
24523 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24526 for (var i in this.stylesheets) {
24527 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24532 st += '<style type="text/css">' +
24533 'IMG { cursor: pointer } ' +
24536 var cls = 'roo-htmleditor-body';
24538 if(this.bodyCls.length){
24539 cls += ' ' + this.bodyCls;
24542 return '<html><head>' + st +
24543 //<style type="text/css">' +
24544 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24546 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24550 onRender : function(ct, position)
24553 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24554 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24557 this.el.dom.style.border = '0 none';
24558 this.el.dom.setAttribute('tabIndex', -1);
24559 this.el.addClass('x-hidden hide');
24563 if(Roo.isIE){ // fix IE 1px bogus margin
24564 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24568 this.frameId = Roo.id();
24572 var iframe = this.owner.wrap.createChild({
24574 cls: 'form-control', // bootstrap..
24576 name: this.frameId,
24577 frameBorder : 'no',
24578 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24583 this.iframe = iframe.dom;
24585 this.assignDocWin();
24587 this.doc.designMode = 'on';
24590 this.doc.write(this.getDocMarkup());
24594 var task = { // must defer to wait for browser to be ready
24596 //console.log("run task?" + this.doc.readyState);
24597 this.assignDocWin();
24598 if(this.doc.body || this.doc.readyState == 'complete'){
24600 this.doc.designMode="on";
24604 Roo.TaskMgr.stop(task);
24605 this.initEditor.defer(10, this);
24612 Roo.TaskMgr.start(task);
24617 onResize : function(w, h)
24619 Roo.log('resize: ' +w + ',' + h );
24620 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24624 if(typeof w == 'number'){
24626 this.iframe.style.width = w + 'px';
24628 if(typeof h == 'number'){
24630 this.iframe.style.height = h + 'px';
24632 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24639 * Toggles the editor between standard and source edit mode.
24640 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24642 toggleSourceEdit : function(sourceEditMode){
24644 this.sourceEditMode = sourceEditMode === true;
24646 if(this.sourceEditMode){
24648 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24651 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24652 //this.iframe.className = '';
24655 //this.setSize(this.owner.wrap.getSize());
24656 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24663 * Protected method that will not generally be called directly. If you need/want
24664 * custom HTML cleanup, this is the method you should override.
24665 * @param {String} html The HTML to be cleaned
24666 * return {String} The cleaned HTML
24668 cleanHtml : function(html){
24669 html = String(html);
24670 if(html.length > 5){
24671 if(Roo.isSafari){ // strip safari nonsense
24672 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24675 if(html == ' '){
24682 * HTML Editor -> Textarea
24683 * Protected method that will not generally be called directly. Syncs the contents
24684 * of the editor iframe with the textarea.
24686 syncValue : function(){
24687 if(this.initialized){
24688 var bd = (this.doc.body || this.doc.documentElement);
24689 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24690 var html = bd.innerHTML;
24692 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24693 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24695 html = '<div style="'+m[0]+'">' + html + '</div>';
24698 html = this.cleanHtml(html);
24699 // fix up the special chars.. normaly like back quotes in word...
24700 // however we do not want to do this with chinese..
24701 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24703 var cc = match.charCodeAt();
24705 // Get the character value, handling surrogate pairs
24706 if (match.length == 2) {
24707 // It's a surrogate pair, calculate the Unicode code point
24708 var high = match.charCodeAt(0) - 0xD800;
24709 var low = match.charCodeAt(1) - 0xDC00;
24710 cc = (high * 0x400) + low + 0x10000;
24712 (cc >= 0x4E00 && cc < 0xA000 ) ||
24713 (cc >= 0x3400 && cc < 0x4E00 ) ||
24714 (cc >= 0xf900 && cc < 0xfb00 )
24719 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24720 return "&#" + cc + ";";
24727 if(this.owner.fireEvent('beforesync', this, html) !== false){
24728 this.el.dom.value = html;
24729 this.owner.fireEvent('sync', this, html);
24735 * Protected method that will not generally be called directly. Pushes the value of the textarea
24736 * into the iframe editor.
24738 pushValue : function(){
24739 if(this.initialized){
24740 var v = this.el.dom.value.trim();
24742 // if(v.length < 1){
24746 if(this.owner.fireEvent('beforepush', this, v) !== false){
24747 var d = (this.doc.body || this.doc.documentElement);
24749 this.cleanUpPaste();
24750 this.el.dom.value = d.innerHTML;
24751 this.owner.fireEvent('push', this, v);
24757 deferFocus : function(){
24758 this.focus.defer(10, this);
24762 focus : function(){
24763 if(this.win && !this.sourceEditMode){
24770 assignDocWin: function()
24772 var iframe = this.iframe;
24775 this.doc = iframe.contentWindow.document;
24776 this.win = iframe.contentWindow;
24778 // if (!Roo.get(this.frameId)) {
24781 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24782 // this.win = Roo.get(this.frameId).dom.contentWindow;
24784 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24788 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24789 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24794 initEditor : function(){
24795 //console.log("INIT EDITOR");
24796 this.assignDocWin();
24800 this.doc.designMode="on";
24802 this.doc.write(this.getDocMarkup());
24805 var dbody = (this.doc.body || this.doc.documentElement);
24806 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24807 // this copies styles from the containing element into thsi one..
24808 // not sure why we need all of this..
24809 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24811 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24812 //ss['background-attachment'] = 'fixed'; // w3c
24813 dbody.bgProperties = 'fixed'; // ie
24814 //Roo.DomHelper.applyStyles(dbody, ss);
24815 Roo.EventManager.on(this.doc, {
24816 //'mousedown': this.onEditorEvent,
24817 'mouseup': this.onEditorEvent,
24818 'dblclick': this.onEditorEvent,
24819 'click': this.onEditorEvent,
24820 'keyup': this.onEditorEvent,
24825 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24827 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24828 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24830 this.initialized = true;
24832 this.owner.fireEvent('initialize', this);
24837 onDestroy : function(){
24843 //for (var i =0; i < this.toolbars.length;i++) {
24844 // // fixme - ask toolbars for heights?
24845 // this.toolbars[i].onDestroy();
24848 //this.wrap.dom.innerHTML = '';
24849 //this.wrap.remove();
24854 onFirstFocus : function(){
24856 this.assignDocWin();
24859 this.activated = true;
24862 if(Roo.isGecko){ // prevent silly gecko errors
24864 var s = this.win.getSelection();
24865 if(!s.focusNode || s.focusNode.nodeType != 3){
24866 var r = s.getRangeAt(0);
24867 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24872 this.execCmd('useCSS', true);
24873 this.execCmd('styleWithCSS', false);
24876 this.owner.fireEvent('activate', this);
24880 adjustFont: function(btn){
24881 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24882 //if(Roo.isSafari){ // safari
24885 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24886 if(Roo.isSafari){ // safari
24887 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24888 v = (v < 10) ? 10 : v;
24889 v = (v > 48) ? 48 : v;
24890 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24895 v = Math.max(1, v+adjust);
24897 this.execCmd('FontSize', v );
24900 onEditorEvent : function(e)
24902 this.owner.fireEvent('editorevent', this, e);
24903 // this.updateToolbar();
24904 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24907 insertTag : function(tg)
24909 // could be a bit smarter... -> wrap the current selected tRoo..
24910 if (tg.toLowerCase() == 'span' ||
24911 tg.toLowerCase() == 'code' ||
24912 tg.toLowerCase() == 'sup' ||
24913 tg.toLowerCase() == 'sub'
24916 range = this.createRange(this.getSelection());
24917 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24918 wrappingNode.appendChild(range.extractContents());
24919 range.insertNode(wrappingNode);
24926 this.execCmd("formatblock", tg);
24930 insertText : function(txt)
24934 var range = this.createRange();
24935 range.deleteContents();
24936 //alert(Sender.getAttribute('label'));
24938 range.insertNode(this.doc.createTextNode(txt));
24944 * Executes a Midas editor command on the editor document and performs necessary focus and
24945 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24946 * @param {String} cmd The Midas command
24947 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24949 relayCmd : function(cmd, value){
24951 this.execCmd(cmd, value);
24952 this.owner.fireEvent('editorevent', this);
24953 //this.updateToolbar();
24954 this.owner.deferFocus();
24958 * Executes a Midas editor command directly on the editor document.
24959 * For visual commands, you should use {@link #relayCmd} instead.
24960 * <b>This should only be called after the editor is initialized.</b>
24961 * @param {String} cmd The Midas command
24962 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24964 execCmd : function(cmd, value){
24965 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24972 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24974 * @param {String} text | dom node..
24976 insertAtCursor : function(text)
24979 if(!this.activated){
24985 var r = this.doc.selection.createRange();
24996 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25000 // from jquery ui (MIT licenced)
25002 var win = this.win;
25004 if (win.getSelection && win.getSelection().getRangeAt) {
25005 range = win.getSelection().getRangeAt(0);
25006 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25007 range.insertNode(node);
25008 } else if (win.document.selection && win.document.selection.createRange) {
25009 // no firefox support
25010 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25011 win.document.selection.createRange().pasteHTML(txt);
25013 // no firefox support
25014 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25015 this.execCmd('InsertHTML', txt);
25024 mozKeyPress : function(e){
25026 var c = e.getCharCode(), cmd;
25029 c = String.fromCharCode(c).toLowerCase();
25043 this.cleanUpPaste.defer(100, this);
25051 e.preventDefault();
25059 fixKeys : function(){ // load time branching for fastest keydown performance
25061 return function(e){
25062 var k = e.getKey(), r;
25065 r = this.doc.selection.createRange();
25068 r.pasteHTML('    ');
25075 r = this.doc.selection.createRange();
25077 var target = r.parentElement();
25078 if(!target || target.tagName.toLowerCase() != 'li'){
25080 r.pasteHTML('<br />');
25086 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25087 this.cleanUpPaste.defer(100, this);
25093 }else if(Roo.isOpera){
25094 return function(e){
25095 var k = e.getKey();
25099 this.execCmd('InsertHTML','    ');
25102 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25103 this.cleanUpPaste.defer(100, this);
25108 }else if(Roo.isSafari){
25109 return function(e){
25110 var k = e.getKey();
25114 this.execCmd('InsertText','\t');
25118 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25119 this.cleanUpPaste.defer(100, this);
25127 getAllAncestors: function()
25129 var p = this.getSelectedNode();
25132 a.push(p); // push blank onto stack..
25133 p = this.getParentElement();
25137 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25141 a.push(this.doc.body);
25145 lastSelNode : false,
25148 getSelection : function()
25150 this.assignDocWin();
25151 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25154 getSelectedNode: function()
25156 // this may only work on Gecko!!!
25158 // should we cache this!!!!
25163 var range = this.createRange(this.getSelection()).cloneRange();
25166 var parent = range.parentElement();
25168 var testRange = range.duplicate();
25169 testRange.moveToElementText(parent);
25170 if (testRange.inRange(range)) {
25173 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25176 parent = parent.parentElement;
25181 // is ancestor a text element.
25182 var ac = range.commonAncestorContainer;
25183 if (ac.nodeType == 3) {
25184 ac = ac.parentNode;
25187 var ar = ac.childNodes;
25190 var other_nodes = [];
25191 var has_other_nodes = false;
25192 for (var i=0;i<ar.length;i++) {
25193 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25196 // fullly contained node.
25198 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25203 // probably selected..
25204 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25205 other_nodes.push(ar[i]);
25209 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25214 has_other_nodes = true;
25216 if (!nodes.length && other_nodes.length) {
25217 nodes= other_nodes;
25219 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25225 createRange: function(sel)
25227 // this has strange effects when using with
25228 // top toolbar - not sure if it's a great idea.
25229 //this.editor.contentWindow.focus();
25230 if (typeof sel != "undefined") {
25232 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25234 return this.doc.createRange();
25237 return this.doc.createRange();
25240 getParentElement: function()
25243 this.assignDocWin();
25244 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25246 var range = this.createRange(sel);
25249 var p = range.commonAncestorContainer;
25250 while (p.nodeType == 3) { // text node
25261 * Range intersection.. the hard stuff...
25265 * [ -- selected range --- ]
25269 * if end is before start or hits it. fail.
25270 * if start is after end or hits it fail.
25272 * if either hits (but other is outside. - then it's not
25278 // @see http://www.thismuchiknow.co.uk/?p=64.
25279 rangeIntersectsNode : function(range, node)
25281 var nodeRange = node.ownerDocument.createRange();
25283 nodeRange.selectNode(node);
25285 nodeRange.selectNodeContents(node);
25288 var rangeStartRange = range.cloneRange();
25289 rangeStartRange.collapse(true);
25291 var rangeEndRange = range.cloneRange();
25292 rangeEndRange.collapse(false);
25294 var nodeStartRange = nodeRange.cloneRange();
25295 nodeStartRange.collapse(true);
25297 var nodeEndRange = nodeRange.cloneRange();
25298 nodeEndRange.collapse(false);
25300 return rangeStartRange.compareBoundaryPoints(
25301 Range.START_TO_START, nodeEndRange) == -1 &&
25302 rangeEndRange.compareBoundaryPoints(
25303 Range.START_TO_START, nodeStartRange) == 1;
25307 rangeCompareNode : function(range, node)
25309 var nodeRange = node.ownerDocument.createRange();
25311 nodeRange.selectNode(node);
25313 nodeRange.selectNodeContents(node);
25317 range.collapse(true);
25319 nodeRange.collapse(true);
25321 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25322 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25324 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25326 var nodeIsBefore = ss == 1;
25327 var nodeIsAfter = ee == -1;
25329 if (nodeIsBefore && nodeIsAfter) {
25332 if (!nodeIsBefore && nodeIsAfter) {
25333 return 1; //right trailed.
25336 if (nodeIsBefore && !nodeIsAfter) {
25337 return 2; // left trailed.
25343 // private? - in a new class?
25344 cleanUpPaste : function()
25346 // cleans up the whole document..
25347 Roo.log('cleanuppaste');
25349 this.cleanUpChildren(this.doc.body);
25350 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25351 if (clean != this.doc.body.innerHTML) {
25352 this.doc.body.innerHTML = clean;
25357 cleanWordChars : function(input) {// change the chars to hex code
25358 var he = Roo.HtmlEditorCore;
25360 var output = input;
25361 Roo.each(he.swapCodes, function(sw) {
25362 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25364 output = output.replace(swapper, sw[1]);
25371 cleanUpChildren : function (n)
25373 if (!n.childNodes.length) {
25376 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25377 this.cleanUpChild(n.childNodes[i]);
25384 cleanUpChild : function (node)
25387 //console.log(node);
25388 if (node.nodeName == "#text") {
25389 // clean up silly Windows -- stuff?
25392 if (node.nodeName == "#comment") {
25393 node.parentNode.removeChild(node);
25394 // clean up silly Windows -- stuff?
25397 var lcname = node.tagName.toLowerCase();
25398 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25399 // whitelist of tags..
25401 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25403 node.parentNode.removeChild(node);
25408 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25410 // spans with no attributes - just remove them..
25411 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25412 remove_keep_children = true;
25415 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25416 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25418 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25419 // remove_keep_children = true;
25422 if (remove_keep_children) {
25423 this.cleanUpChildren(node);
25424 // inserts everything just before this node...
25425 while (node.childNodes.length) {
25426 var cn = node.childNodes[0];
25427 node.removeChild(cn);
25428 node.parentNode.insertBefore(cn, node);
25430 node.parentNode.removeChild(node);
25434 if (!node.attributes || !node.attributes.length) {
25439 this.cleanUpChildren(node);
25443 function cleanAttr(n,v)
25446 if (v.match(/^\./) || v.match(/^\//)) {
25449 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25452 if (v.match(/^#/)) {
25455 if (v.match(/^\{/)) { // allow template editing.
25458 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25459 node.removeAttribute(n);
25463 var cwhite = this.cwhite;
25464 var cblack = this.cblack;
25466 function cleanStyle(n,v)
25468 if (v.match(/expression/)) { //XSS?? should we even bother..
25469 node.removeAttribute(n);
25473 var parts = v.split(/;/);
25476 Roo.each(parts, function(p) {
25477 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25481 var l = p.split(':').shift().replace(/\s+/g,'');
25482 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25484 if ( cwhite.length && cblack.indexOf(l) > -1) {
25485 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25486 //node.removeAttribute(n);
25490 // only allow 'c whitelisted system attributes'
25491 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25492 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25493 //node.removeAttribute(n);
25503 if (clean.length) {
25504 node.setAttribute(n, clean.join(';'));
25506 node.removeAttribute(n);
25512 for (var i = node.attributes.length-1; i > -1 ; i--) {
25513 var a = node.attributes[i];
25516 if (a.name.toLowerCase().substr(0,2)=='on') {
25517 node.removeAttribute(a.name);
25520 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25521 node.removeAttribute(a.name);
25524 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25525 cleanAttr(a.name,a.value); // fixme..
25528 if (a.name == 'style') {
25529 cleanStyle(a.name,a.value);
25532 /// clean up MS crap..
25533 // tecnically this should be a list of valid class'es..
25536 if (a.name == 'class') {
25537 if (a.value.match(/^Mso/)) {
25538 node.removeAttribute('class');
25541 if (a.value.match(/^body$/)) {
25542 node.removeAttribute('class');
25553 this.cleanUpChildren(node);
25559 * Clean up MS wordisms...
25561 cleanWord : function(node)
25564 this.cleanWord(this.doc.body);
25569 node.nodeName == 'SPAN' &&
25570 !node.hasAttributes() &&
25571 node.childNodes.length == 1 &&
25572 node.firstChild.nodeName == "#text"
25574 var textNode = node.firstChild;
25575 node.removeChild(textNode);
25576 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25577 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25579 node.parentNode.insertBefore(textNode, node);
25580 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25581 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25583 node.parentNode.removeChild(node);
25586 if (node.nodeName == "#text") {
25587 // clean up silly Windows -- stuff?
25590 if (node.nodeName == "#comment") {
25591 node.parentNode.removeChild(node);
25592 // clean up silly Windows -- stuff?
25596 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25597 node.parentNode.removeChild(node);
25600 //Roo.log(node.tagName);
25601 // remove - but keep children..
25602 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25603 //Roo.log('-- removed');
25604 while (node.childNodes.length) {
25605 var cn = node.childNodes[0];
25606 node.removeChild(cn);
25607 node.parentNode.insertBefore(cn, node);
25608 // move node to parent - and clean it..
25609 this.cleanWord(cn);
25611 node.parentNode.removeChild(node);
25612 /// no need to iterate chidlren = it's got none..
25613 //this.iterateChildren(node, this.cleanWord);
25617 if (node.className.length) {
25619 var cn = node.className.split(/\W+/);
25621 Roo.each(cn, function(cls) {
25622 if (cls.match(/Mso[a-zA-Z]+/)) {
25627 node.className = cna.length ? cna.join(' ') : '';
25629 node.removeAttribute("class");
25633 if (node.hasAttribute("lang")) {
25634 node.removeAttribute("lang");
25637 if (node.hasAttribute("style")) {
25639 var styles = node.getAttribute("style").split(";");
25641 Roo.each(styles, function(s) {
25642 if (!s.match(/:/)) {
25645 var kv = s.split(":");
25646 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25649 // what ever is left... we allow.
25652 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25653 if (!nstyle.length) {
25654 node.removeAttribute('style');
25657 this.iterateChildren(node, this.cleanWord);
25663 * iterateChildren of a Node, calling fn each time, using this as the scole..
25664 * @param {DomNode} node node to iterate children of.
25665 * @param {Function} fn method of this class to call on each item.
25667 iterateChildren : function(node, fn)
25669 if (!node.childNodes.length) {
25672 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25673 fn.call(this, node.childNodes[i])
25679 * cleanTableWidths.
25681 * Quite often pasting from word etc.. results in tables with column and widths.
25682 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25685 cleanTableWidths : function(node)
25690 this.cleanTableWidths(this.doc.body);
25695 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25698 Roo.log(node.tagName);
25699 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25700 this.iterateChildren(node, this.cleanTableWidths);
25703 if (node.hasAttribute('width')) {
25704 node.removeAttribute('width');
25708 if (node.hasAttribute("style")) {
25711 var styles = node.getAttribute("style").split(";");
25713 Roo.each(styles, function(s) {
25714 if (!s.match(/:/)) {
25717 var kv = s.split(":");
25718 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25721 // what ever is left... we allow.
25724 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25725 if (!nstyle.length) {
25726 node.removeAttribute('style');
25730 this.iterateChildren(node, this.cleanTableWidths);
25738 domToHTML : function(currentElement, depth, nopadtext) {
25740 depth = depth || 0;
25741 nopadtext = nopadtext || false;
25743 if (!currentElement) {
25744 return this.domToHTML(this.doc.body);
25747 //Roo.log(currentElement);
25749 var allText = false;
25750 var nodeName = currentElement.nodeName;
25751 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25753 if (nodeName == '#text') {
25755 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25760 if (nodeName != 'BODY') {
25763 // Prints the node tagName, such as <A>, <IMG>, etc
25766 for(i = 0; i < currentElement.attributes.length;i++) {
25768 var aname = currentElement.attributes.item(i).name;
25769 if (!currentElement.attributes.item(i).value.length) {
25772 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25775 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25784 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25787 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25792 // Traverse the tree
25794 var currentElementChild = currentElement.childNodes.item(i);
25795 var allText = true;
25796 var innerHTML = '';
25798 while (currentElementChild) {
25799 // Formatting code (indent the tree so it looks nice on the screen)
25800 var nopad = nopadtext;
25801 if (lastnode == 'SPAN') {
25805 if (currentElementChild.nodeName == '#text') {
25806 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25807 toadd = nopadtext ? toadd : toadd.trim();
25808 if (!nopad && toadd.length > 80) {
25809 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25811 innerHTML += toadd;
25814 currentElementChild = currentElement.childNodes.item(i);
25820 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25822 // Recursively traverse the tree structure of the child node
25823 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25824 lastnode = currentElementChild.nodeName;
25826 currentElementChild=currentElement.childNodes.item(i);
25832 // The remaining code is mostly for formatting the tree
25833 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25838 ret+= "</"+tagName+">";
25844 applyBlacklists : function()
25846 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25847 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25851 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25852 if (b.indexOf(tag) > -1) {
25855 this.white.push(tag);
25859 Roo.each(w, function(tag) {
25860 if (b.indexOf(tag) > -1) {
25863 if (this.white.indexOf(tag) > -1) {
25866 this.white.push(tag);
25871 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25872 if (w.indexOf(tag) > -1) {
25875 this.black.push(tag);
25879 Roo.each(b, function(tag) {
25880 if (w.indexOf(tag) > -1) {
25883 if (this.black.indexOf(tag) > -1) {
25886 this.black.push(tag);
25891 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25892 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25896 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25897 if (b.indexOf(tag) > -1) {
25900 this.cwhite.push(tag);
25904 Roo.each(w, function(tag) {
25905 if (b.indexOf(tag) > -1) {
25908 if (this.cwhite.indexOf(tag) > -1) {
25911 this.cwhite.push(tag);
25916 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25917 if (w.indexOf(tag) > -1) {
25920 this.cblack.push(tag);
25924 Roo.each(b, function(tag) {
25925 if (w.indexOf(tag) > -1) {
25928 if (this.cblack.indexOf(tag) > -1) {
25931 this.cblack.push(tag);
25936 setStylesheets : function(stylesheets)
25938 if(typeof(stylesheets) == 'string'){
25939 Roo.get(this.iframe.contentDocument.head).createChild({
25941 rel : 'stylesheet',
25950 Roo.each(stylesheets, function(s) {
25955 Roo.get(_this.iframe.contentDocument.head).createChild({
25957 rel : 'stylesheet',
25966 removeStylesheets : function()
25970 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25975 setStyle : function(style)
25977 Roo.get(this.iframe.contentDocument.head).createChild({
25986 // hide stuff that is not compatible
26000 * @event specialkey
26004 * @cfg {String} fieldClass @hide
26007 * @cfg {String} focusClass @hide
26010 * @cfg {String} autoCreate @hide
26013 * @cfg {String} inputType @hide
26016 * @cfg {String} invalidClass @hide
26019 * @cfg {String} invalidText @hide
26022 * @cfg {String} msgFx @hide
26025 * @cfg {String} validateOnBlur @hide
26029 Roo.HtmlEditorCore.white = [
26030 'area', 'br', 'img', 'input', 'hr', 'wbr',
26032 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26033 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26034 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26035 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26036 'table', 'ul', 'xmp',
26038 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26041 'dir', 'menu', 'ol', 'ul', 'dl',
26047 Roo.HtmlEditorCore.black = [
26048 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26050 'base', 'basefont', 'bgsound', 'blink', 'body',
26051 'frame', 'frameset', 'head', 'html', 'ilayer',
26052 'iframe', 'layer', 'link', 'meta', 'object',
26053 'script', 'style' ,'title', 'xml' // clean later..
26055 Roo.HtmlEditorCore.clean = [
26056 'script', 'style', 'title', 'xml'
26058 Roo.HtmlEditorCore.remove = [
26063 Roo.HtmlEditorCore.ablack = [
26067 Roo.HtmlEditorCore.aclean = [
26068 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26072 Roo.HtmlEditorCore.pwhite= [
26073 'http', 'https', 'mailto'
26076 // white listed style attributes.
26077 Roo.HtmlEditorCore.cwhite= [
26078 // 'text-align', /// default is to allow most things..
26084 // black listed style attributes.
26085 Roo.HtmlEditorCore.cblack= [
26086 // 'font-size' -- this can be set by the project
26090 Roo.HtmlEditorCore.swapCodes =[
26091 [ 8211, "–" ],
26092 [ 8212, "—" ],
26109 * @class Roo.bootstrap.HtmlEditor
26110 * @extends Roo.bootstrap.TextArea
26111 * Bootstrap HtmlEditor class
26114 * Create a new HtmlEditor
26115 * @param {Object} config The config object
26118 Roo.bootstrap.HtmlEditor = function(config){
26119 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26120 if (!this.toolbars) {
26121 this.toolbars = [];
26124 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26127 * @event initialize
26128 * Fires when the editor is fully initialized (including the iframe)
26129 * @param {HtmlEditor} this
26134 * Fires when the editor is first receives the focus. Any insertion must wait
26135 * until after this event.
26136 * @param {HtmlEditor} this
26140 * @event beforesync
26141 * Fires before the textarea is updated with content from the editor iframe. Return false
26142 * to cancel the sync.
26143 * @param {HtmlEditor} this
26144 * @param {String} html
26148 * @event beforepush
26149 * Fires before the iframe editor is updated with content from the textarea. Return false
26150 * to cancel the push.
26151 * @param {HtmlEditor} this
26152 * @param {String} html
26157 * Fires when the textarea is updated with content from the editor iframe.
26158 * @param {HtmlEditor} this
26159 * @param {String} html
26164 * Fires when the iframe editor is updated with content from the textarea.
26165 * @param {HtmlEditor} this
26166 * @param {String} html
26170 * @event editmodechange
26171 * Fires when the editor switches edit modes
26172 * @param {HtmlEditor} this
26173 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26175 editmodechange: true,
26177 * @event editorevent
26178 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26179 * @param {HtmlEditor} this
26183 * @event firstfocus
26184 * Fires when on first focus - needed by toolbars..
26185 * @param {HtmlEditor} this
26190 * Auto save the htmlEditor value as a file into Events
26191 * @param {HtmlEditor} this
26195 * @event savedpreview
26196 * preview the saved version of htmlEditor
26197 * @param {HtmlEditor} this
26204 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26208 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26213 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26218 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26223 * @cfg {Number} height (in pixels)
26227 * @cfg {Number} width (in pixels)
26232 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26235 stylesheets: false,
26240 // private properties
26241 validationEvent : false,
26243 initialized : false,
26246 onFocus : Roo.emptyFn,
26248 hideMode:'offsets',
26250 tbContainer : false,
26254 toolbarContainer :function() {
26255 return this.wrap.select('.x-html-editor-tb',true).first();
26259 * Protected method that will not generally be called directly. It
26260 * is called when the editor creates its toolbar. Override this method if you need to
26261 * add custom toolbar buttons.
26262 * @param {HtmlEditor} editor
26264 createToolbar : function(){
26265 Roo.log('renewing');
26266 Roo.log("create toolbars");
26268 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26269 this.toolbars[0].render(this.toolbarContainer());
26273 // if (!editor.toolbars || !editor.toolbars.length) {
26274 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26277 // for (var i =0 ; i < editor.toolbars.length;i++) {
26278 // editor.toolbars[i] = Roo.factory(
26279 // typeof(editor.toolbars[i]) == 'string' ?
26280 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26281 // Roo.bootstrap.HtmlEditor);
26282 // editor.toolbars[i].init(editor);
26288 onRender : function(ct, position)
26290 // Roo.log("Call onRender: " + this.xtype);
26292 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26294 this.wrap = this.inputEl().wrap({
26295 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26298 this.editorcore.onRender(ct, position);
26300 if (this.resizable) {
26301 this.resizeEl = new Roo.Resizable(this.wrap, {
26305 minHeight : this.height,
26306 height: this.height,
26307 handles : this.resizable,
26310 resize : function(r, w, h) {
26311 _t.onResize(w,h); // -something
26317 this.createToolbar(this);
26320 if(!this.width && this.resizable){
26321 this.setSize(this.wrap.getSize());
26323 if (this.resizeEl) {
26324 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26325 // should trigger onReize..
26331 onResize : function(w, h)
26333 Roo.log('resize: ' +w + ',' + h );
26334 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26338 if(this.inputEl() ){
26339 if(typeof w == 'number'){
26340 var aw = w - this.wrap.getFrameWidth('lr');
26341 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26344 if(typeof h == 'number'){
26345 var tbh = -11; // fixme it needs to tool bar size!
26346 for (var i =0; i < this.toolbars.length;i++) {
26347 // fixme - ask toolbars for heights?
26348 tbh += this.toolbars[i].el.getHeight();
26349 //if (this.toolbars[i].footer) {
26350 // tbh += this.toolbars[i].footer.el.getHeight();
26358 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26359 ah -= 5; // knock a few pixes off for look..
26360 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26364 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26365 this.editorcore.onResize(ew,eh);
26370 * Toggles the editor between standard and source edit mode.
26371 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26373 toggleSourceEdit : function(sourceEditMode)
26375 this.editorcore.toggleSourceEdit(sourceEditMode);
26377 if(this.editorcore.sourceEditMode){
26378 Roo.log('editor - showing textarea');
26381 // Roo.log(this.syncValue());
26383 this.inputEl().removeClass(['hide', 'x-hidden']);
26384 this.inputEl().dom.removeAttribute('tabIndex');
26385 this.inputEl().focus();
26387 Roo.log('editor - hiding textarea');
26389 // Roo.log(this.pushValue());
26392 this.inputEl().addClass(['hide', 'x-hidden']);
26393 this.inputEl().dom.setAttribute('tabIndex', -1);
26394 //this.deferFocus();
26397 if(this.resizable){
26398 this.setSize(this.wrap.getSize());
26401 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26404 // private (for BoxComponent)
26405 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26407 // private (for BoxComponent)
26408 getResizeEl : function(){
26412 // private (for BoxComponent)
26413 getPositionEl : function(){
26418 initEvents : function(){
26419 this.originalValue = this.getValue();
26423 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26426 // markInvalid : Roo.emptyFn,
26428 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26431 // clearInvalid : Roo.emptyFn,
26433 setValue : function(v){
26434 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26435 this.editorcore.pushValue();
26440 deferFocus : function(){
26441 this.focus.defer(10, this);
26445 focus : function(){
26446 this.editorcore.focus();
26452 onDestroy : function(){
26458 for (var i =0; i < this.toolbars.length;i++) {
26459 // fixme - ask toolbars for heights?
26460 this.toolbars[i].onDestroy();
26463 this.wrap.dom.innerHTML = '';
26464 this.wrap.remove();
26469 onFirstFocus : function(){
26470 //Roo.log("onFirstFocus");
26471 this.editorcore.onFirstFocus();
26472 for (var i =0; i < this.toolbars.length;i++) {
26473 this.toolbars[i].onFirstFocus();
26479 syncValue : function()
26481 this.editorcore.syncValue();
26484 pushValue : function()
26486 this.editorcore.pushValue();
26490 // hide stuff that is not compatible
26504 * @event specialkey
26508 * @cfg {String} fieldClass @hide
26511 * @cfg {String} focusClass @hide
26514 * @cfg {String} autoCreate @hide
26517 * @cfg {String} inputType @hide
26521 * @cfg {String} invalidText @hide
26524 * @cfg {String} msgFx @hide
26527 * @cfg {String} validateOnBlur @hide
26536 Roo.namespace('Roo.bootstrap.htmleditor');
26538 * @class Roo.bootstrap.HtmlEditorToolbar1
26544 new Roo.bootstrap.HtmlEditor({
26547 new Roo.bootstrap.HtmlEditorToolbar1({
26548 disable : { fonts: 1 , format: 1, ..., ... , ...],
26554 * @cfg {Object} disable List of elements to disable..
26555 * @cfg {Array} btns List of additional buttons.
26559 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26562 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26565 Roo.apply(this, config);
26567 // default disabled, based on 'good practice'..
26568 this.disable = this.disable || {};
26569 Roo.applyIf(this.disable, {
26572 specialElements : true
26574 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26576 this.editor = config.editor;
26577 this.editorcore = config.editor.editorcore;
26579 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26581 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26582 // dont call parent... till later.
26584 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26589 editorcore : false,
26594 "h1","h2","h3","h4","h5","h6",
26596 "abbr", "acronym", "address", "cite", "samp", "var",
26600 onRender : function(ct, position)
26602 // Roo.log("Call onRender: " + this.xtype);
26604 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26606 this.el.dom.style.marginBottom = '0';
26608 var editorcore = this.editorcore;
26609 var editor= this.editor;
26612 var btn = function(id,cmd , toggle, handler, html){
26614 var event = toggle ? 'toggle' : 'click';
26619 xns: Roo.bootstrap,
26623 enableToggle:toggle !== false,
26625 pressed : toggle ? false : null,
26628 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26629 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26635 // var cb_box = function...
26640 xns: Roo.bootstrap,
26645 xns: Roo.bootstrap,
26649 Roo.each(this.formats, function(f) {
26650 style.menu.items.push({
26652 xns: Roo.bootstrap,
26653 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26658 editorcore.insertTag(this.tagname);
26665 children.push(style);
26667 btn('bold',false,true);
26668 btn('italic',false,true);
26669 btn('align-left', 'justifyleft',true);
26670 btn('align-center', 'justifycenter',true);
26671 btn('align-right' , 'justifyright',true);
26672 btn('link', false, false, function(btn) {
26673 //Roo.log("create link?");
26674 var url = prompt(this.createLinkText, this.defaultLinkValue);
26675 if(url && url != 'http:/'+'/'){
26676 this.editorcore.relayCmd('createlink', url);
26679 btn('list','insertunorderedlist',true);
26680 btn('pencil', false,true, function(btn){
26682 this.toggleSourceEdit(btn.pressed);
26685 if (this.editor.btns.length > 0) {
26686 for (var i = 0; i<this.editor.btns.length; i++) {
26687 children.push(this.editor.btns[i]);
26695 xns: Roo.bootstrap,
26700 xns: Roo.bootstrap,
26705 cog.menu.items.push({
26707 xns: Roo.bootstrap,
26708 html : Clean styles,
26713 editorcore.insertTag(this.tagname);
26722 this.xtype = 'NavSimplebar';
26724 for(var i=0;i< children.length;i++) {
26726 this.buttons.add(this.addxtypeChild(children[i]));
26730 editor.on('editorevent', this.updateToolbar, this);
26732 onBtnClick : function(id)
26734 this.editorcore.relayCmd(id);
26735 this.editorcore.focus();
26739 * Protected method that will not generally be called directly. It triggers
26740 * a toolbar update by reading the markup state of the current selection in the editor.
26742 updateToolbar: function(){
26744 if(!this.editorcore.activated){
26745 this.editor.onFirstFocus(); // is this neeed?
26749 var btns = this.buttons;
26750 var doc = this.editorcore.doc;
26751 btns.get('bold').setActive(doc.queryCommandState('bold'));
26752 btns.get('italic').setActive(doc.queryCommandState('italic'));
26753 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26755 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26756 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26757 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26759 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26760 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26763 var ans = this.editorcore.getAllAncestors();
26764 if (this.formatCombo) {
26767 var store = this.formatCombo.store;
26768 this.formatCombo.setValue("");
26769 for (var i =0; i < ans.length;i++) {
26770 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26772 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26780 // hides menus... - so this cant be on a menu...
26781 Roo.bootstrap.MenuMgr.hideAll();
26783 Roo.bootstrap.MenuMgr.hideAll();
26784 //this.editorsyncValue();
26786 onFirstFocus: function() {
26787 this.buttons.each(function(item){
26791 toggleSourceEdit : function(sourceEditMode){
26794 if(sourceEditMode){
26795 Roo.log("disabling buttons");
26796 this.buttons.each( function(item){
26797 if(item.cmd != 'pencil'){
26803 Roo.log("enabling buttons");
26804 if(this.editorcore.initialized){
26805 this.buttons.each( function(item){
26811 Roo.log("calling toggole on editor");
26812 // tell the editor that it's been pressed..
26813 this.editor.toggleSourceEdit(sourceEditMode);
26827 * @class Roo.bootstrap.Markdown
26828 * @extends Roo.bootstrap.TextArea
26829 * Bootstrap Showdown editable area
26830 * @cfg {string} content
26833 * Create a new Showdown
26836 Roo.bootstrap.Markdown = function(config){
26837 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26841 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26845 initEvents : function()
26848 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26849 this.markdownEl = this.el.createChild({
26850 cls : 'roo-markdown-area'
26852 this.inputEl().addClass('d-none');
26853 if (this.getValue() == '') {
26854 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26857 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26859 this.markdownEl.on('click', this.toggleTextEdit, this);
26860 this.on('blur', this.toggleTextEdit, this);
26861 this.on('specialkey', this.resizeTextArea, this);
26864 toggleTextEdit : function()
26866 var sh = this.markdownEl.getHeight();
26867 this.inputEl().addClass('d-none');
26868 this.markdownEl.addClass('d-none');
26869 if (!this.editing) {
26871 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26872 this.inputEl().removeClass('d-none');
26873 this.inputEl().focus();
26874 this.editing = true;
26877 // show showdown...
26878 this.updateMarkdown();
26879 this.markdownEl.removeClass('d-none');
26880 this.editing = false;
26883 updateMarkdown : function()
26885 if (this.getValue() == '') {
26886 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26890 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26893 resizeTextArea: function () {
26896 Roo.log([sh, this.getValue().split("\n").length * 30]);
26897 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26899 setValue : function(val)
26901 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26902 if (!this.editing) {
26903 this.updateMarkdown();
26909 if (!this.editing) {
26910 this.toggleTextEdit();
26918 * @class Roo.bootstrap.Table.AbstractSelectionModel
26919 * @extends Roo.util.Observable
26920 * Abstract base class for grid SelectionModels. It provides the interface that should be
26921 * implemented by descendant classes. This class should not be directly instantiated.
26924 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26925 this.locked = false;
26926 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26930 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26931 /** @ignore Called by the grid automatically. Do not call directly. */
26932 init : function(grid){
26938 * Locks the selections.
26941 this.locked = true;
26945 * Unlocks the selections.
26947 unlock : function(){
26948 this.locked = false;
26952 * Returns true if the selections are locked.
26953 * @return {Boolean}
26955 isLocked : function(){
26956 return this.locked;
26960 initEvents : function ()
26966 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26967 * @class Roo.bootstrap.Table.RowSelectionModel
26968 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26969 * It supports multiple selections and keyboard selection/navigation.
26971 * @param {Object} config
26974 Roo.bootstrap.Table.RowSelectionModel = function(config){
26975 Roo.apply(this, config);
26976 this.selections = new Roo.util.MixedCollection(false, function(o){
26981 this.lastActive = false;
26985 * @event selectionchange
26986 * Fires when the selection changes
26987 * @param {SelectionModel} this
26989 "selectionchange" : true,
26991 * @event afterselectionchange
26992 * Fires after the selection changes (eg. by key press or clicking)
26993 * @param {SelectionModel} this
26995 "afterselectionchange" : true,
26997 * @event beforerowselect
26998 * Fires when a row is selected being selected, return false to cancel.
26999 * @param {SelectionModel} this
27000 * @param {Number} rowIndex The selected index
27001 * @param {Boolean} keepExisting False if other selections will be cleared
27003 "beforerowselect" : true,
27006 * Fires when a row is selected.
27007 * @param {SelectionModel} this
27008 * @param {Number} rowIndex The selected index
27009 * @param {Roo.data.Record} r The record
27011 "rowselect" : true,
27013 * @event rowdeselect
27014 * Fires when a row is deselected.
27015 * @param {SelectionModel} this
27016 * @param {Number} rowIndex The selected index
27018 "rowdeselect" : true
27020 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27021 this.locked = false;
27024 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27026 * @cfg {Boolean} singleSelect
27027 * True to allow selection of only one row at a time (defaults to false)
27029 singleSelect : false,
27032 initEvents : function()
27035 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27036 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27037 //}else{ // allow click to work like normal
27038 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27040 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27041 this.grid.on("rowclick", this.handleMouseDown, this);
27043 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27044 "up" : function(e){
27046 this.selectPrevious(e.shiftKey);
27047 }else if(this.last !== false && this.lastActive !== false){
27048 var last = this.last;
27049 this.selectRange(this.last, this.lastActive-1);
27050 this.grid.getView().focusRow(this.lastActive);
27051 if(last !== false){
27055 this.selectFirstRow();
27057 this.fireEvent("afterselectionchange", this);
27059 "down" : function(e){
27061 this.selectNext(e.shiftKey);
27062 }else if(this.last !== false && this.lastActive !== false){
27063 var last = this.last;
27064 this.selectRange(this.last, this.lastActive+1);
27065 this.grid.getView().focusRow(this.lastActive);
27066 if(last !== false){
27070 this.selectFirstRow();
27072 this.fireEvent("afterselectionchange", this);
27076 this.grid.store.on('load', function(){
27077 this.selections.clear();
27080 var view = this.grid.view;
27081 view.on("refresh", this.onRefresh, this);
27082 view.on("rowupdated", this.onRowUpdated, this);
27083 view.on("rowremoved", this.onRemove, this);
27088 onRefresh : function()
27090 var ds = this.grid.store, i, v = this.grid.view;
27091 var s = this.selections;
27092 s.each(function(r){
27093 if((i = ds.indexOfId(r.id)) != -1){
27102 onRemove : function(v, index, r){
27103 this.selections.remove(r);
27107 onRowUpdated : function(v, index, r){
27108 if(this.isSelected(r)){
27109 v.onRowSelect(index);
27115 * @param {Array} records The records to select
27116 * @param {Boolean} keepExisting (optional) True to keep existing selections
27118 selectRecords : function(records, keepExisting)
27121 this.clearSelections();
27123 var ds = this.grid.store;
27124 for(var i = 0, len = records.length; i < len; i++){
27125 this.selectRow(ds.indexOf(records[i]), true);
27130 * Gets the number of selected rows.
27133 getCount : function(){
27134 return this.selections.length;
27138 * Selects the first row in the grid.
27140 selectFirstRow : function(){
27145 * Select the last row.
27146 * @param {Boolean} keepExisting (optional) True to keep existing selections
27148 selectLastRow : function(keepExisting){
27149 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27150 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27154 * Selects the row immediately following the last selected row.
27155 * @param {Boolean} keepExisting (optional) True to keep existing selections
27157 selectNext : function(keepExisting)
27159 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27160 this.selectRow(this.last+1, keepExisting);
27161 this.grid.getView().focusRow(this.last);
27166 * Selects the row that precedes the last selected row.
27167 * @param {Boolean} keepExisting (optional) True to keep existing selections
27169 selectPrevious : function(keepExisting){
27171 this.selectRow(this.last-1, keepExisting);
27172 this.grid.getView().focusRow(this.last);
27177 * Returns the selected records
27178 * @return {Array} Array of selected records
27180 getSelections : function(){
27181 return [].concat(this.selections.items);
27185 * Returns the first selected record.
27188 getSelected : function(){
27189 return this.selections.itemAt(0);
27194 * Clears all selections.
27196 clearSelections : function(fast)
27202 var ds = this.grid.store;
27203 var s = this.selections;
27204 s.each(function(r){
27205 this.deselectRow(ds.indexOfId(r.id));
27209 this.selections.clear();
27216 * Selects all rows.
27218 selectAll : function(){
27222 this.selections.clear();
27223 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27224 this.selectRow(i, true);
27229 * Returns True if there is a selection.
27230 * @return {Boolean}
27232 hasSelection : function(){
27233 return this.selections.length > 0;
27237 * Returns True if the specified row is selected.
27238 * @param {Number/Record} record The record or index of the record to check
27239 * @return {Boolean}
27241 isSelected : function(index){
27242 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27243 return (r && this.selections.key(r.id) ? true : false);
27247 * Returns True if the specified record id is selected.
27248 * @param {String} id The id of record to check
27249 * @return {Boolean}
27251 isIdSelected : function(id){
27252 return (this.selections.key(id) ? true : false);
27257 handleMouseDBClick : function(e, t){
27261 handleMouseDown : function(e, t)
27263 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27264 if(this.isLocked() || rowIndex < 0 ){
27267 if(e.shiftKey && this.last !== false){
27268 var last = this.last;
27269 this.selectRange(last, rowIndex, e.ctrlKey);
27270 this.last = last; // reset the last
27274 var isSelected = this.isSelected(rowIndex);
27275 //Roo.log("select row:" + rowIndex);
27277 this.deselectRow(rowIndex);
27279 this.selectRow(rowIndex, true);
27283 if(e.button !== 0 && isSelected){
27284 alert('rowIndex 2: ' + rowIndex);
27285 view.focusRow(rowIndex);
27286 }else if(e.ctrlKey && isSelected){
27287 this.deselectRow(rowIndex);
27288 }else if(!isSelected){
27289 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27290 view.focusRow(rowIndex);
27294 this.fireEvent("afterselectionchange", this);
27297 handleDragableRowClick : function(grid, rowIndex, e)
27299 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27300 this.selectRow(rowIndex, false);
27301 grid.view.focusRow(rowIndex);
27302 this.fireEvent("afterselectionchange", this);
27307 * Selects multiple rows.
27308 * @param {Array} rows Array of the indexes of the row to select
27309 * @param {Boolean} keepExisting (optional) True to keep existing selections
27311 selectRows : function(rows, keepExisting){
27313 this.clearSelections();
27315 for(var i = 0, len = rows.length; i < len; i++){
27316 this.selectRow(rows[i], true);
27321 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27322 * @param {Number} startRow The index of the first row in the range
27323 * @param {Number} endRow The index of the last row in the range
27324 * @param {Boolean} keepExisting (optional) True to retain existing selections
27326 selectRange : function(startRow, endRow, keepExisting){
27331 this.clearSelections();
27333 if(startRow <= endRow){
27334 for(var i = startRow; i <= endRow; i++){
27335 this.selectRow(i, true);
27338 for(var i = startRow; i >= endRow; i--){
27339 this.selectRow(i, true);
27345 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27346 * @param {Number} startRow The index of the first row in the range
27347 * @param {Number} endRow The index of the last row in the range
27349 deselectRange : function(startRow, endRow, preventViewNotify){
27353 for(var i = startRow; i <= endRow; i++){
27354 this.deselectRow(i, preventViewNotify);
27360 * @param {Number} row The index of the row to select
27361 * @param {Boolean} keepExisting (optional) True to keep existing selections
27363 selectRow : function(index, keepExisting, preventViewNotify)
27365 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27368 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27369 if(!keepExisting || this.singleSelect){
27370 this.clearSelections();
27373 var r = this.grid.store.getAt(index);
27374 //console.log('selectRow - record id :' + r.id);
27376 this.selections.add(r);
27377 this.last = this.lastActive = index;
27378 if(!preventViewNotify){
27379 var proxy = new Roo.Element(
27380 this.grid.getRowDom(index)
27382 proxy.addClass('bg-info info');
27384 this.fireEvent("rowselect", this, index, r);
27385 this.fireEvent("selectionchange", this);
27391 * @param {Number} row The index of the row to deselect
27393 deselectRow : function(index, preventViewNotify)
27398 if(this.last == index){
27401 if(this.lastActive == index){
27402 this.lastActive = false;
27405 var r = this.grid.store.getAt(index);
27410 this.selections.remove(r);
27411 //.console.log('deselectRow - record id :' + r.id);
27412 if(!preventViewNotify){
27414 var proxy = new Roo.Element(
27415 this.grid.getRowDom(index)
27417 proxy.removeClass('bg-info info');
27419 this.fireEvent("rowdeselect", this, index);
27420 this.fireEvent("selectionchange", this);
27424 restoreLast : function(){
27426 this.last = this._last;
27431 acceptsNav : function(row, col, cm){
27432 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27436 onEditorKey : function(field, e){
27437 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27442 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27444 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27446 }else if(k == e.ENTER && !e.ctrlKey){
27450 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27452 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27454 }else if(k == e.ESC){
27458 g.startEditing(newCell[0], newCell[1]);
27464 * Ext JS Library 1.1.1
27465 * Copyright(c) 2006-2007, Ext JS, LLC.
27467 * Originally Released Under LGPL - original licence link has changed is not relivant.
27470 * <script type="text/javascript">
27474 * @class Roo.bootstrap.PagingToolbar
27475 * @extends Roo.bootstrap.NavSimplebar
27476 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27478 * Create a new PagingToolbar
27479 * @param {Object} config The config object
27480 * @param {Roo.data.Store} store
27482 Roo.bootstrap.PagingToolbar = function(config)
27484 // old args format still supported... - xtype is prefered..
27485 // created from xtype...
27487 this.ds = config.dataSource;
27489 if (config.store && !this.ds) {
27490 this.store= Roo.factory(config.store, Roo.data);
27491 this.ds = this.store;
27492 this.ds.xmodule = this.xmodule || false;
27495 this.toolbarItems = [];
27496 if (config.items) {
27497 this.toolbarItems = config.items;
27500 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27505 this.bind(this.ds);
27508 if (Roo.bootstrap.version == 4) {
27509 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27511 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27516 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27518 * @cfg {Roo.data.Store} dataSource
27519 * The underlying data store providing the paged data
27522 * @cfg {String/HTMLElement/Element} container
27523 * container The id or element that will contain the toolbar
27526 * @cfg {Boolean} displayInfo
27527 * True to display the displayMsg (defaults to false)
27530 * @cfg {Number} pageSize
27531 * The number of records to display per page (defaults to 20)
27535 * @cfg {String} displayMsg
27536 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27538 displayMsg : 'Displaying {0} - {1} of {2}',
27540 * @cfg {String} emptyMsg
27541 * The message to display when no records are found (defaults to "No data to display")
27543 emptyMsg : 'No data to display',
27545 * Customizable piece of the default paging text (defaults to "Page")
27548 beforePageText : "Page",
27550 * Customizable piece of the default paging text (defaults to "of %0")
27553 afterPageText : "of {0}",
27555 * Customizable piece of the default paging text (defaults to "First Page")
27558 firstText : "First Page",
27560 * Customizable piece of the default paging text (defaults to "Previous Page")
27563 prevText : "Previous Page",
27565 * Customizable piece of the default paging text (defaults to "Next Page")
27568 nextText : "Next Page",
27570 * Customizable piece of the default paging text (defaults to "Last Page")
27573 lastText : "Last Page",
27575 * Customizable piece of the default paging text (defaults to "Refresh")
27578 refreshText : "Refresh",
27582 onRender : function(ct, position)
27584 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27585 this.navgroup.parentId = this.id;
27586 this.navgroup.onRender(this.el, null);
27587 // add the buttons to the navgroup
27589 if(this.displayInfo){
27590 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27591 this.displayEl = this.el.select('.x-paging-info', true).first();
27592 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27593 // this.displayEl = navel.el.select('span',true).first();
27599 Roo.each(_this.buttons, function(e){ // this might need to use render????
27600 Roo.factory(e).render(_this.el);
27604 Roo.each(_this.toolbarItems, function(e) {
27605 _this.navgroup.addItem(e);
27609 this.first = this.navgroup.addItem({
27610 tooltip: this.firstText,
27611 cls: "prev btn-outline-secondary",
27612 html : ' <i class="fa fa-step-backward"></i>',
27614 preventDefault: true,
27615 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27618 this.prev = this.navgroup.addItem({
27619 tooltip: this.prevText,
27620 cls: "prev btn-outline-secondary",
27621 html : ' <i class="fa fa-backward"></i>',
27623 preventDefault: true,
27624 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27626 //this.addSeparator();
27629 var field = this.navgroup.addItem( {
27631 cls : 'x-paging-position btn-outline-secondary',
27633 html : this.beforePageText +
27634 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27635 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27638 this.field = field.el.select('input', true).first();
27639 this.field.on("keydown", this.onPagingKeydown, this);
27640 this.field.on("focus", function(){this.dom.select();});
27643 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27644 //this.field.setHeight(18);
27645 //this.addSeparator();
27646 this.next = this.navgroup.addItem({
27647 tooltip: this.nextText,
27648 cls: "next btn-outline-secondary",
27649 html : ' <i class="fa fa-forward"></i>',
27651 preventDefault: true,
27652 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27654 this.last = this.navgroup.addItem({
27655 tooltip: this.lastText,
27656 html : ' <i class="fa fa-step-forward"></i>',
27657 cls: "next btn-outline-secondary",
27659 preventDefault: true,
27660 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27662 //this.addSeparator();
27663 this.loading = this.navgroup.addItem({
27664 tooltip: this.refreshText,
27665 cls: "btn-outline-secondary",
27666 html : ' <i class="fa fa-refresh"></i>',
27667 preventDefault: true,
27668 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27674 updateInfo : function(){
27675 if(this.displayEl){
27676 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27677 var msg = count == 0 ?
27681 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27683 this.displayEl.update(msg);
27688 onLoad : function(ds, r, o)
27690 this.cursor = o.params && o.params.start ? o.params.start : 0;
27692 var d = this.getPageData(),
27697 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27698 this.field.dom.value = ap;
27699 this.first.setDisabled(ap == 1);
27700 this.prev.setDisabled(ap == 1);
27701 this.next.setDisabled(ap == ps);
27702 this.last.setDisabled(ap == ps);
27703 this.loading.enable();
27708 getPageData : function(){
27709 var total = this.ds.getTotalCount();
27712 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27713 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27718 onLoadError : function(){
27719 this.loading.enable();
27723 onPagingKeydown : function(e){
27724 var k = e.getKey();
27725 var d = this.getPageData();
27727 var v = this.field.dom.value, pageNum;
27728 if(!v || isNaN(pageNum = parseInt(v, 10))){
27729 this.field.dom.value = d.activePage;
27732 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27733 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27736 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))
27738 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27739 this.field.dom.value = pageNum;
27740 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27743 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27745 var v = this.field.dom.value, pageNum;
27746 var increment = (e.shiftKey) ? 10 : 1;
27747 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27750 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27751 this.field.dom.value = d.activePage;
27754 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27756 this.field.dom.value = parseInt(v, 10) + increment;
27757 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27758 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27765 beforeLoad : function(){
27767 this.loading.disable();
27772 onClick : function(which){
27781 ds.load({params:{start: 0, limit: this.pageSize}});
27784 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27787 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27790 var total = ds.getTotalCount();
27791 var extra = total % this.pageSize;
27792 var lastStart = extra ? (total - extra) : total-this.pageSize;
27793 ds.load({params:{start: lastStart, limit: this.pageSize}});
27796 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27802 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27803 * @param {Roo.data.Store} store The data store to unbind
27805 unbind : function(ds){
27806 ds.un("beforeload", this.beforeLoad, this);
27807 ds.un("load", this.onLoad, this);
27808 ds.un("loadexception", this.onLoadError, this);
27809 ds.un("remove", this.updateInfo, this);
27810 ds.un("add", this.updateInfo, this);
27811 this.ds = undefined;
27815 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27816 * @param {Roo.data.Store} store The data store to bind
27818 bind : function(ds){
27819 ds.on("beforeload", this.beforeLoad, this);
27820 ds.on("load", this.onLoad, this);
27821 ds.on("loadexception", this.onLoadError, this);
27822 ds.on("remove", this.updateInfo, this);
27823 ds.on("add", this.updateInfo, this);
27834 * @class Roo.bootstrap.MessageBar
27835 * @extends Roo.bootstrap.Component
27836 * Bootstrap MessageBar class
27837 * @cfg {String} html contents of the MessageBar
27838 * @cfg {String} weight (info | success | warning | danger) default info
27839 * @cfg {String} beforeClass insert the bar before the given class
27840 * @cfg {Boolean} closable (true | false) default false
27841 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27844 * Create a new Element
27845 * @param {Object} config The config object
27848 Roo.bootstrap.MessageBar = function(config){
27849 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27852 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27858 beforeClass: 'bootstrap-sticky-wrap',
27860 getAutoCreate : function(){
27864 cls: 'alert alert-dismissable alert-' + this.weight,
27869 html: this.html || ''
27875 cfg.cls += ' alert-messages-fixed';
27889 onRender : function(ct, position)
27891 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27894 var cfg = Roo.apply({}, this.getAutoCreate());
27898 cfg.cls += ' ' + this.cls;
27901 cfg.style = this.style;
27903 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27905 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27908 this.el.select('>button.close').on('click', this.hide, this);
27914 if (!this.rendered) {
27920 this.fireEvent('show', this);
27926 if (!this.rendered) {
27932 this.fireEvent('hide', this);
27935 update : function()
27937 // var e = this.el.dom.firstChild;
27939 // if(this.closable){
27940 // e = e.nextSibling;
27943 // e.data = this.html || '';
27945 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27961 * @class Roo.bootstrap.Graph
27962 * @extends Roo.bootstrap.Component
27963 * Bootstrap Graph class
27967 @cfg {String} graphtype bar | vbar | pie
27968 @cfg {number} g_x coodinator | centre x (pie)
27969 @cfg {number} g_y coodinator | centre y (pie)
27970 @cfg {number} g_r radius (pie)
27971 @cfg {number} g_height height of the chart (respected by all elements in the set)
27972 @cfg {number} g_width width of the chart (respected by all elements in the set)
27973 @cfg {Object} title The title of the chart
27976 -opts (object) options for the chart
27978 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27979 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27981 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.
27982 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27984 o stretch (boolean)
27986 -opts (object) options for the pie
27989 o startAngle (number)
27990 o endAngle (number)
27994 * Create a new Input
27995 * @param {Object} config The config object
27998 Roo.bootstrap.Graph = function(config){
27999 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28005 * The img click event for the img.
28006 * @param {Roo.EventObject} e
28012 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28023 //g_colors: this.colors,
28030 getAutoCreate : function(){
28041 onRender : function(ct,position){
28044 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28046 if (typeof(Raphael) == 'undefined') {
28047 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28051 this.raphael = Raphael(this.el.dom);
28053 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28054 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28055 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28056 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28058 r.text(160, 10, "Single Series Chart").attr(txtattr);
28059 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28060 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28061 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28063 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28064 r.barchart(330, 10, 300, 220, data1);
28065 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28066 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28069 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28070 // r.barchart(30, 30, 560, 250, xdata, {
28071 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28072 // axis : "0 0 1 1",
28073 // axisxlabels : xdata
28074 // //yvalues : cols,
28077 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28079 // this.load(null,xdata,{
28080 // axis : "0 0 1 1",
28081 // axisxlabels : xdata
28086 load : function(graphtype,xdata,opts)
28088 this.raphael.clear();
28090 graphtype = this.graphtype;
28095 var r = this.raphael,
28096 fin = function () {
28097 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28099 fout = function () {
28100 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28102 pfin = function() {
28103 this.sector.stop();
28104 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28107 this.label[0].stop();
28108 this.label[0].attr({ r: 7.5 });
28109 this.label[1].attr({ "font-weight": 800 });
28112 pfout = function() {
28113 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28116 this.label[0].animate({ r: 5 }, 500, "bounce");
28117 this.label[1].attr({ "font-weight": 400 });
28123 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28126 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28129 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28130 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28132 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28139 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28144 setTitle: function(o)
28149 initEvents: function() {
28152 this.el.on('click', this.onClick, this);
28156 onClick : function(e)
28158 Roo.log('img onclick');
28159 this.fireEvent('click', this, e);
28171 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28174 * @class Roo.bootstrap.dash.NumberBox
28175 * @extends Roo.bootstrap.Component
28176 * Bootstrap NumberBox class
28177 * @cfg {String} headline Box headline
28178 * @cfg {String} content Box content
28179 * @cfg {String} icon Box icon
28180 * @cfg {String} footer Footer text
28181 * @cfg {String} fhref Footer href
28184 * Create a new NumberBox
28185 * @param {Object} config The config object
28189 Roo.bootstrap.dash.NumberBox = function(config){
28190 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28194 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28203 getAutoCreate : function(){
28207 cls : 'small-box ',
28215 cls : 'roo-headline',
28216 html : this.headline
28220 cls : 'roo-content',
28221 html : this.content
28235 cls : 'ion ' + this.icon
28244 cls : 'small-box-footer',
28245 href : this.fhref || '#',
28249 cfg.cn.push(footer);
28256 onRender : function(ct,position){
28257 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28264 setHeadline: function (value)
28266 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28269 setFooter: function (value, href)
28271 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28274 this.el.select('a.small-box-footer',true).first().attr('href', href);
28279 setContent: function (value)
28281 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28284 initEvents: function()
28298 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28301 * @class Roo.bootstrap.dash.TabBox
28302 * @extends Roo.bootstrap.Component
28303 * Bootstrap TabBox class
28304 * @cfg {String} title Title of the TabBox
28305 * @cfg {String} icon Icon of the TabBox
28306 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28307 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28310 * Create a new TabBox
28311 * @param {Object} config The config object
28315 Roo.bootstrap.dash.TabBox = function(config){
28316 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28321 * When a pane is added
28322 * @param {Roo.bootstrap.dash.TabPane} pane
28326 * @event activatepane
28327 * When a pane is activated
28328 * @param {Roo.bootstrap.dash.TabPane} pane
28330 "activatepane" : true
28338 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28343 tabScrollable : false,
28345 getChildContainer : function()
28347 return this.el.select('.tab-content', true).first();
28350 getAutoCreate : function(){
28354 cls: 'pull-left header',
28362 cls: 'fa ' + this.icon
28368 cls: 'nav nav-tabs pull-right',
28374 if(this.tabScrollable){
28381 cls: 'nav nav-tabs pull-right',
28392 cls: 'nav-tabs-custom',
28397 cls: 'tab-content no-padding',
28405 initEvents : function()
28407 //Roo.log('add add pane handler');
28408 this.on('addpane', this.onAddPane, this);
28411 * Updates the box title
28412 * @param {String} html to set the title to.
28414 setTitle : function(value)
28416 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28418 onAddPane : function(pane)
28420 this.panes.push(pane);
28421 //Roo.log('addpane');
28423 // tabs are rendere left to right..
28424 if(!this.showtabs){
28428 var ctr = this.el.select('.nav-tabs', true).first();
28431 var existing = ctr.select('.nav-tab',true);
28432 var qty = existing.getCount();;
28435 var tab = ctr.createChild({
28437 cls : 'nav-tab' + (qty ? '' : ' active'),
28445 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28448 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28450 pane.el.addClass('active');
28455 onTabClick : function(ev,un,ob,pane)
28457 //Roo.log('tab - prev default');
28458 ev.preventDefault();
28461 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28462 pane.tab.addClass('active');
28463 //Roo.log(pane.title);
28464 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28465 // technically we should have a deactivate event.. but maybe add later.
28466 // and it should not de-activate the selected tab...
28467 this.fireEvent('activatepane', pane);
28468 pane.el.addClass('active');
28469 pane.fireEvent('activate');
28474 getActivePane : function()
28477 Roo.each(this.panes, function(p) {
28478 if(p.el.hasClass('active')){
28499 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28501 * @class Roo.bootstrap.TabPane
28502 * @extends Roo.bootstrap.Component
28503 * Bootstrap TabPane class
28504 * @cfg {Boolean} active (false | true) Default false
28505 * @cfg {String} title title of panel
28509 * Create a new TabPane
28510 * @param {Object} config The config object
28513 Roo.bootstrap.dash.TabPane = function(config){
28514 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28520 * When a pane is activated
28521 * @param {Roo.bootstrap.dash.TabPane} pane
28528 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28533 // the tabBox that this is attached to.
28536 getAutoCreate : function()
28544 cfg.cls += ' active';
28549 initEvents : function()
28551 //Roo.log('trigger add pane handler');
28552 this.parent().fireEvent('addpane', this)
28556 * Updates the tab title
28557 * @param {String} html to set the title to.
28559 setTitle: function(str)
28565 this.tab.select('a', true).first().dom.innerHTML = str;
28582 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28585 * @class Roo.bootstrap.menu.Menu
28586 * @extends Roo.bootstrap.Component
28587 * Bootstrap Menu class - container for Menu
28588 * @cfg {String} html Text of the menu
28589 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28590 * @cfg {String} icon Font awesome icon
28591 * @cfg {String} pos Menu align to (top | bottom) default bottom
28595 * Create a new Menu
28596 * @param {Object} config The config object
28600 Roo.bootstrap.menu.Menu = function(config){
28601 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28605 * @event beforeshow
28606 * Fires before this menu is displayed
28607 * @param {Roo.bootstrap.menu.Menu} this
28611 * @event beforehide
28612 * Fires before this menu is hidden
28613 * @param {Roo.bootstrap.menu.Menu} this
28618 * Fires after this menu is displayed
28619 * @param {Roo.bootstrap.menu.Menu} this
28624 * Fires after this menu is hidden
28625 * @param {Roo.bootstrap.menu.Menu} this
28630 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28631 * @param {Roo.bootstrap.menu.Menu} this
28632 * @param {Roo.EventObject} e
28639 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28643 weight : 'default',
28648 getChildContainer : function() {
28649 if(this.isSubMenu){
28653 return this.el.select('ul.dropdown-menu', true).first();
28656 getAutoCreate : function()
28661 cls : 'roo-menu-text',
28669 cls : 'fa ' + this.icon
28680 cls : 'dropdown-button btn btn-' + this.weight,
28685 cls : 'dropdown-toggle btn btn-' + this.weight,
28695 cls : 'dropdown-menu'
28701 if(this.pos == 'top'){
28702 cfg.cls += ' dropup';
28705 if(this.isSubMenu){
28708 cls : 'dropdown-menu'
28715 onRender : function(ct, position)
28717 this.isSubMenu = ct.hasClass('dropdown-submenu');
28719 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28722 initEvents : function()
28724 if(this.isSubMenu){
28728 this.hidden = true;
28730 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28731 this.triggerEl.on('click', this.onTriggerPress, this);
28733 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28734 this.buttonEl.on('click', this.onClick, this);
28740 if(this.isSubMenu){
28744 return this.el.select('ul.dropdown-menu', true).first();
28747 onClick : function(e)
28749 this.fireEvent("click", this, e);
28752 onTriggerPress : function(e)
28754 if (this.isVisible()) {
28761 isVisible : function(){
28762 return !this.hidden;
28767 this.fireEvent("beforeshow", this);
28769 this.hidden = false;
28770 this.el.addClass('open');
28772 Roo.get(document).on("mouseup", this.onMouseUp, this);
28774 this.fireEvent("show", this);
28781 this.fireEvent("beforehide", this);
28783 this.hidden = true;
28784 this.el.removeClass('open');
28786 Roo.get(document).un("mouseup", this.onMouseUp);
28788 this.fireEvent("hide", this);
28791 onMouseUp : function()
28805 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28808 * @class Roo.bootstrap.menu.Item
28809 * @extends Roo.bootstrap.Component
28810 * Bootstrap MenuItem class
28811 * @cfg {Boolean} submenu (true | false) default false
28812 * @cfg {String} html text of the item
28813 * @cfg {String} href the link
28814 * @cfg {Boolean} disable (true | false) default false
28815 * @cfg {Boolean} preventDefault (true | false) default true
28816 * @cfg {String} icon Font awesome icon
28817 * @cfg {String} pos Submenu align to (left | right) default right
28821 * Create a new Item
28822 * @param {Object} config The config object
28826 Roo.bootstrap.menu.Item = function(config){
28827 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28831 * Fires when the mouse is hovering over this menu
28832 * @param {Roo.bootstrap.menu.Item} this
28833 * @param {Roo.EventObject} e
28838 * Fires when the mouse exits this menu
28839 * @param {Roo.bootstrap.menu.Item} this
28840 * @param {Roo.EventObject} e
28846 * The raw click event for the entire grid.
28847 * @param {Roo.EventObject} e
28853 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28858 preventDefault: true,
28863 getAutoCreate : function()
28868 cls : 'roo-menu-item-text',
28876 cls : 'fa ' + this.icon
28885 href : this.href || '#',
28892 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28896 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28898 if(this.pos == 'left'){
28899 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28906 initEvents : function()
28908 this.el.on('mouseover', this.onMouseOver, this);
28909 this.el.on('mouseout', this.onMouseOut, this);
28911 this.el.select('a', true).first().on('click', this.onClick, this);
28915 onClick : function(e)
28917 if(this.preventDefault){
28918 e.preventDefault();
28921 this.fireEvent("click", this, e);
28924 onMouseOver : function(e)
28926 if(this.submenu && this.pos == 'left'){
28927 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28930 this.fireEvent("mouseover", this, e);
28933 onMouseOut : function(e)
28935 this.fireEvent("mouseout", this, e);
28947 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28950 * @class Roo.bootstrap.menu.Separator
28951 * @extends Roo.bootstrap.Component
28952 * Bootstrap Separator class
28955 * Create a new Separator
28956 * @param {Object} config The config object
28960 Roo.bootstrap.menu.Separator = function(config){
28961 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28964 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28966 getAutoCreate : function(){
28969 cls: 'dropdown-divider divider'
28987 * @class Roo.bootstrap.Tooltip
28988 * Bootstrap Tooltip class
28989 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28990 * to determine which dom element triggers the tooltip.
28992 * It needs to add support for additional attributes like tooltip-position
28995 * Create a new Toolti
28996 * @param {Object} config The config object
28999 Roo.bootstrap.Tooltip = function(config){
29000 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29002 this.alignment = Roo.bootstrap.Tooltip.alignment;
29004 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29005 this.alignment = config.alignment;
29010 Roo.apply(Roo.bootstrap.Tooltip, {
29012 * @function init initialize tooltip monitoring.
29016 currentTip : false,
29017 currentRegion : false,
29023 Roo.get(document).on('mouseover', this.enter ,this);
29024 Roo.get(document).on('mouseout', this.leave, this);
29027 this.currentTip = new Roo.bootstrap.Tooltip();
29030 enter : function(ev)
29032 var dom = ev.getTarget();
29034 //Roo.log(['enter',dom]);
29035 var el = Roo.fly(dom);
29036 if (this.currentEl) {
29038 //Roo.log(this.currentEl);
29039 //Roo.log(this.currentEl.contains(dom));
29040 if (this.currentEl == el) {
29043 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29049 if (this.currentTip.el) {
29050 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29054 if(!el || el.dom == document){
29060 // you can not look for children, as if el is the body.. then everythign is the child..
29061 if (!el.attr('tooltip')) { //
29062 if (!el.select("[tooltip]").elements.length) {
29065 // is the mouse over this child...?
29066 bindEl = el.select("[tooltip]").first();
29067 var xy = ev.getXY();
29068 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29069 //Roo.log("not in region.");
29072 //Roo.log("child element over..");
29075 this.currentEl = bindEl;
29076 this.currentTip.bind(bindEl);
29077 this.currentRegion = Roo.lib.Region.getRegion(dom);
29078 this.currentTip.enter();
29081 leave : function(ev)
29083 var dom = ev.getTarget();
29084 //Roo.log(['leave',dom]);
29085 if (!this.currentEl) {
29090 if (dom != this.currentEl.dom) {
29093 var xy = ev.getXY();
29094 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29097 // only activate leave if mouse cursor is outside... bounding box..
29102 if (this.currentTip) {
29103 this.currentTip.leave();
29105 //Roo.log('clear currentEl');
29106 this.currentEl = false;
29111 'left' : ['r-l', [-2,0], 'right'],
29112 'right' : ['l-r', [2,0], 'left'],
29113 'bottom' : ['t-b', [0,2], 'top'],
29114 'top' : [ 'b-t', [0,-2], 'bottom']
29120 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29125 delay : null, // can be { show : 300 , hide: 500}
29129 hoverState : null, //???
29131 placement : 'bottom',
29135 getAutoCreate : function(){
29142 cls : 'tooltip-arrow arrow'
29145 cls : 'tooltip-inner'
29152 bind : function(el)
29157 initEvents : function()
29159 this.arrowEl = this.el.select('.arrow', true).first();
29160 this.innerEl = this.el.select('.tooltip-inner', true).first();
29163 enter : function () {
29165 if (this.timeout != null) {
29166 clearTimeout(this.timeout);
29169 this.hoverState = 'in';
29170 //Roo.log("enter - show");
29171 if (!this.delay || !this.delay.show) {
29176 this.timeout = setTimeout(function () {
29177 if (_t.hoverState == 'in') {
29180 }, this.delay.show);
29184 clearTimeout(this.timeout);
29186 this.hoverState = 'out';
29187 if (!this.delay || !this.delay.hide) {
29193 this.timeout = setTimeout(function () {
29194 //Roo.log("leave - timeout");
29196 if (_t.hoverState == 'out') {
29198 Roo.bootstrap.Tooltip.currentEl = false;
29203 show : function (msg)
29206 this.render(document.body);
29209 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29211 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29213 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29215 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29216 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29218 var placement = typeof this.placement == 'function' ?
29219 this.placement.call(this, this.el, on_el) :
29222 var autoToken = /\s?auto?\s?/i;
29223 var autoPlace = autoToken.test(placement);
29225 placement = placement.replace(autoToken, '') || 'top';
29229 //this.el.setXY([0,0]);
29231 //this.el.dom.style.display='block';
29233 //this.el.appendTo(on_el);
29235 var p = this.getPosition();
29236 var box = this.el.getBox();
29242 var align = this.alignment[placement];
29244 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29246 if(placement == 'top' || placement == 'bottom'){
29248 placement = 'right';
29251 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29252 placement = 'left';
29255 var scroll = Roo.select('body', true).first().getScroll();
29257 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29261 align = this.alignment[placement];
29263 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29267 this.el.alignTo(this.bindEl, align[0],align[1]);
29268 //var arrow = this.el.select('.arrow',true).first();
29269 //arrow.set(align[2],
29271 this.el.addClass(placement);
29272 this.el.addClass("bs-tooltip-"+ placement);
29274 this.el.addClass('in fade show');
29276 this.hoverState = null;
29278 if (this.el.hasClass('fade')) {
29293 //this.el.setXY([0,0]);
29294 this.el.removeClass(['show', 'in']);
29310 * @class Roo.bootstrap.LocationPicker
29311 * @extends Roo.bootstrap.Component
29312 * Bootstrap LocationPicker class
29313 * @cfg {Number} latitude Position when init default 0
29314 * @cfg {Number} longitude Position when init default 0
29315 * @cfg {Number} zoom default 15
29316 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29317 * @cfg {Boolean} mapTypeControl default false
29318 * @cfg {Boolean} disableDoubleClickZoom default false
29319 * @cfg {Boolean} scrollwheel default true
29320 * @cfg {Boolean} streetViewControl default false
29321 * @cfg {Number} radius default 0
29322 * @cfg {String} locationName
29323 * @cfg {Boolean} draggable default true
29324 * @cfg {Boolean} enableAutocomplete default false
29325 * @cfg {Boolean} enableReverseGeocode default true
29326 * @cfg {String} markerTitle
29329 * Create a new LocationPicker
29330 * @param {Object} config The config object
29334 Roo.bootstrap.LocationPicker = function(config){
29336 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29341 * Fires when the picker initialized.
29342 * @param {Roo.bootstrap.LocationPicker} this
29343 * @param {Google Location} location
29347 * @event positionchanged
29348 * Fires when the picker position changed.
29349 * @param {Roo.bootstrap.LocationPicker} this
29350 * @param {Google Location} location
29352 positionchanged : true,
29355 * Fires when the map resize.
29356 * @param {Roo.bootstrap.LocationPicker} this
29361 * Fires when the map show.
29362 * @param {Roo.bootstrap.LocationPicker} this
29367 * Fires when the map hide.
29368 * @param {Roo.bootstrap.LocationPicker} this
29373 * Fires when click the map.
29374 * @param {Roo.bootstrap.LocationPicker} this
29375 * @param {Map event} e
29379 * @event mapRightClick
29380 * Fires when right click the map.
29381 * @param {Roo.bootstrap.LocationPicker} this
29382 * @param {Map event} e
29384 mapRightClick : true,
29386 * @event markerClick
29387 * Fires when click the marker.
29388 * @param {Roo.bootstrap.LocationPicker} this
29389 * @param {Map event} e
29391 markerClick : true,
29393 * @event markerRightClick
29394 * Fires when right click the marker.
29395 * @param {Roo.bootstrap.LocationPicker} this
29396 * @param {Map event} e
29398 markerRightClick : true,
29400 * @event OverlayViewDraw
29401 * Fires when OverlayView Draw
29402 * @param {Roo.bootstrap.LocationPicker} this
29404 OverlayViewDraw : true,
29406 * @event OverlayViewOnAdd
29407 * Fires when OverlayView Draw
29408 * @param {Roo.bootstrap.LocationPicker} this
29410 OverlayViewOnAdd : true,
29412 * @event OverlayViewOnRemove
29413 * Fires when OverlayView Draw
29414 * @param {Roo.bootstrap.LocationPicker} this
29416 OverlayViewOnRemove : true,
29418 * @event OverlayViewShow
29419 * Fires when OverlayView Draw
29420 * @param {Roo.bootstrap.LocationPicker} this
29421 * @param {Pixel} cpx
29423 OverlayViewShow : true,
29425 * @event OverlayViewHide
29426 * Fires when OverlayView Draw
29427 * @param {Roo.bootstrap.LocationPicker} this
29429 OverlayViewHide : true,
29431 * @event loadexception
29432 * Fires when load google lib failed.
29433 * @param {Roo.bootstrap.LocationPicker} this
29435 loadexception : true
29440 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29442 gMapContext: false,
29448 mapTypeControl: false,
29449 disableDoubleClickZoom: false,
29451 streetViewControl: false,
29455 enableAutocomplete: false,
29456 enableReverseGeocode: true,
29459 getAutoCreate: function()
29464 cls: 'roo-location-picker'
29470 initEvents: function(ct, position)
29472 if(!this.el.getWidth() || this.isApplied()){
29476 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29481 initial: function()
29483 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29484 this.fireEvent('loadexception', this);
29488 if(!this.mapTypeId){
29489 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29492 this.gMapContext = this.GMapContext();
29494 this.initOverlayView();
29496 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29500 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29501 _this.setPosition(_this.gMapContext.marker.position);
29504 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29505 _this.fireEvent('mapClick', this, event);
29509 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29510 _this.fireEvent('mapRightClick', this, event);
29514 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29515 _this.fireEvent('markerClick', this, event);
29519 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29520 _this.fireEvent('markerRightClick', this, event);
29524 this.setPosition(this.gMapContext.location);
29526 this.fireEvent('initial', this, this.gMapContext.location);
29529 initOverlayView: function()
29533 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29537 _this.fireEvent('OverlayViewDraw', _this);
29542 _this.fireEvent('OverlayViewOnAdd', _this);
29545 onRemove: function()
29547 _this.fireEvent('OverlayViewOnRemove', _this);
29550 show: function(cpx)
29552 _this.fireEvent('OverlayViewShow', _this, cpx);
29557 _this.fireEvent('OverlayViewHide', _this);
29563 fromLatLngToContainerPixel: function(event)
29565 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29568 isApplied: function()
29570 return this.getGmapContext() == false ? false : true;
29573 getGmapContext: function()
29575 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29578 GMapContext: function()
29580 var position = new google.maps.LatLng(this.latitude, this.longitude);
29582 var _map = new google.maps.Map(this.el.dom, {
29585 mapTypeId: this.mapTypeId,
29586 mapTypeControl: this.mapTypeControl,
29587 disableDoubleClickZoom: this.disableDoubleClickZoom,
29588 scrollwheel: this.scrollwheel,
29589 streetViewControl: this.streetViewControl,
29590 locationName: this.locationName,
29591 draggable: this.draggable,
29592 enableAutocomplete: this.enableAutocomplete,
29593 enableReverseGeocode: this.enableReverseGeocode
29596 var _marker = new google.maps.Marker({
29597 position: position,
29599 title: this.markerTitle,
29600 draggable: this.draggable
29607 location: position,
29608 radius: this.radius,
29609 locationName: this.locationName,
29610 addressComponents: {
29611 formatted_address: null,
29612 addressLine1: null,
29613 addressLine2: null,
29615 streetNumber: null,
29619 stateOrProvince: null
29622 domContainer: this.el.dom,
29623 geodecoder: new google.maps.Geocoder()
29627 drawCircle: function(center, radius, options)
29629 if (this.gMapContext.circle != null) {
29630 this.gMapContext.circle.setMap(null);
29634 options = Roo.apply({}, options, {
29635 strokeColor: "#0000FF",
29636 strokeOpacity: .35,
29638 fillColor: "#0000FF",
29642 options.map = this.gMapContext.map;
29643 options.radius = radius;
29644 options.center = center;
29645 this.gMapContext.circle = new google.maps.Circle(options);
29646 return this.gMapContext.circle;
29652 setPosition: function(location)
29654 this.gMapContext.location = location;
29655 this.gMapContext.marker.setPosition(location);
29656 this.gMapContext.map.panTo(location);
29657 this.drawCircle(location, this.gMapContext.radius, {});
29661 if (this.gMapContext.settings.enableReverseGeocode) {
29662 this.gMapContext.geodecoder.geocode({
29663 latLng: this.gMapContext.location
29664 }, function(results, status) {
29666 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29667 _this.gMapContext.locationName = results[0].formatted_address;
29668 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29670 _this.fireEvent('positionchanged', this, location);
29677 this.fireEvent('positionchanged', this, location);
29682 google.maps.event.trigger(this.gMapContext.map, "resize");
29684 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29686 this.fireEvent('resize', this);
29689 setPositionByLatLng: function(latitude, longitude)
29691 this.setPosition(new google.maps.LatLng(latitude, longitude));
29694 getCurrentPosition: function()
29697 latitude: this.gMapContext.location.lat(),
29698 longitude: this.gMapContext.location.lng()
29702 getAddressName: function()
29704 return this.gMapContext.locationName;
29707 getAddressComponents: function()
29709 return this.gMapContext.addressComponents;
29712 address_component_from_google_geocode: function(address_components)
29716 for (var i = 0; i < address_components.length; i++) {
29717 var component = address_components[i];
29718 if (component.types.indexOf("postal_code") >= 0) {
29719 result.postalCode = component.short_name;
29720 } else if (component.types.indexOf("street_number") >= 0) {
29721 result.streetNumber = component.short_name;
29722 } else if (component.types.indexOf("route") >= 0) {
29723 result.streetName = component.short_name;
29724 } else if (component.types.indexOf("neighborhood") >= 0) {
29725 result.city = component.short_name;
29726 } else if (component.types.indexOf("locality") >= 0) {
29727 result.city = component.short_name;
29728 } else if (component.types.indexOf("sublocality") >= 0) {
29729 result.district = component.short_name;
29730 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29731 result.stateOrProvince = component.short_name;
29732 } else if (component.types.indexOf("country") >= 0) {
29733 result.country = component.short_name;
29737 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29738 result.addressLine2 = "";
29742 setZoomLevel: function(zoom)
29744 this.gMapContext.map.setZoom(zoom);
29757 this.fireEvent('show', this);
29768 this.fireEvent('hide', this);
29773 Roo.apply(Roo.bootstrap.LocationPicker, {
29775 OverlayView : function(map, options)
29777 options = options || {};
29784 * @class Roo.bootstrap.Alert
29785 * @extends Roo.bootstrap.Component
29786 * Bootstrap Alert class - shows an alert area box
29788 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29789 Enter a valid email address
29792 * @cfg {String} title The title of alert
29793 * @cfg {String} html The content of alert
29794 * @cfg {String} weight ( success | info | warning | danger )
29795 * @cfg {String} faicon font-awesomeicon
29798 * Create a new alert
29799 * @param {Object} config The config object
29803 Roo.bootstrap.Alert = function(config){
29804 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29808 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29815 getAutoCreate : function()
29824 cls : 'roo-alert-icon'
29829 cls : 'roo-alert-title',
29834 cls : 'roo-alert-text',
29841 cfg.cn[0].cls += ' fa ' + this.faicon;
29845 cfg.cls += ' alert-' + this.weight;
29851 initEvents: function()
29853 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29856 setTitle : function(str)
29858 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29861 setText : function(str)
29863 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29866 setWeight : function(weight)
29869 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29872 this.weight = weight;
29874 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29877 setIcon : function(icon)
29880 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29883 this.faicon = icon;
29885 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29906 * @class Roo.bootstrap.UploadCropbox
29907 * @extends Roo.bootstrap.Component
29908 * Bootstrap UploadCropbox class
29909 * @cfg {String} emptyText show when image has been loaded
29910 * @cfg {String} rotateNotify show when image too small to rotate
29911 * @cfg {Number} errorTimeout default 3000
29912 * @cfg {Number} minWidth default 300
29913 * @cfg {Number} minHeight default 300
29914 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29915 * @cfg {Boolean} isDocument (true|false) default false
29916 * @cfg {String} url action url
29917 * @cfg {String} paramName default 'imageUpload'
29918 * @cfg {String} method default POST
29919 * @cfg {Boolean} loadMask (true|false) default true
29920 * @cfg {Boolean} loadingText default 'Loading...'
29923 * Create a new UploadCropbox
29924 * @param {Object} config The config object
29927 Roo.bootstrap.UploadCropbox = function(config){
29928 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29932 * @event beforeselectfile
29933 * Fire before select file
29934 * @param {Roo.bootstrap.UploadCropbox} this
29936 "beforeselectfile" : true,
29939 * Fire after initEvent
29940 * @param {Roo.bootstrap.UploadCropbox} this
29945 * Fire after initEvent
29946 * @param {Roo.bootstrap.UploadCropbox} this
29947 * @param {String} data
29952 * Fire when preparing the file data
29953 * @param {Roo.bootstrap.UploadCropbox} this
29954 * @param {Object} file
29959 * Fire when get exception
29960 * @param {Roo.bootstrap.UploadCropbox} this
29961 * @param {XMLHttpRequest} xhr
29963 "exception" : true,
29965 * @event beforeloadcanvas
29966 * Fire before load the canvas
29967 * @param {Roo.bootstrap.UploadCropbox} this
29968 * @param {String} src
29970 "beforeloadcanvas" : true,
29973 * Fire when trash image
29974 * @param {Roo.bootstrap.UploadCropbox} this
29979 * Fire when download the image
29980 * @param {Roo.bootstrap.UploadCropbox} this
29984 * @event footerbuttonclick
29985 * Fire when footerbuttonclick
29986 * @param {Roo.bootstrap.UploadCropbox} this
29987 * @param {String} type
29989 "footerbuttonclick" : true,
29993 * @param {Roo.bootstrap.UploadCropbox} this
29998 * Fire when rotate the image
29999 * @param {Roo.bootstrap.UploadCropbox} this
30000 * @param {String} pos
30005 * Fire when inspect the file
30006 * @param {Roo.bootstrap.UploadCropbox} this
30007 * @param {Object} file
30012 * Fire when xhr upload the file
30013 * @param {Roo.bootstrap.UploadCropbox} this
30014 * @param {Object} data
30019 * Fire when arrange the file data
30020 * @param {Roo.bootstrap.UploadCropbox} this
30021 * @param {Object} formData
30026 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30029 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30031 emptyText : 'Click to upload image',
30032 rotateNotify : 'Image is too small to rotate',
30033 errorTimeout : 3000,
30047 cropType : 'image/jpeg',
30049 canvasLoaded : false,
30050 isDocument : false,
30052 paramName : 'imageUpload',
30054 loadingText : 'Loading...',
30057 getAutoCreate : function()
30061 cls : 'roo-upload-cropbox',
30065 cls : 'roo-upload-cropbox-selector',
30070 cls : 'roo-upload-cropbox-body',
30071 style : 'cursor:pointer',
30075 cls : 'roo-upload-cropbox-preview'
30079 cls : 'roo-upload-cropbox-thumb'
30083 cls : 'roo-upload-cropbox-empty-notify',
30084 html : this.emptyText
30088 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30089 html : this.rotateNotify
30095 cls : 'roo-upload-cropbox-footer',
30098 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30108 onRender : function(ct, position)
30110 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30112 if (this.buttons.length) {
30114 Roo.each(this.buttons, function(bb) {
30116 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30118 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30124 this.maskEl = this.el;
30128 initEvents : function()
30130 this.urlAPI = (window.createObjectURL && window) ||
30131 (window.URL && URL.revokeObjectURL && URL) ||
30132 (window.webkitURL && webkitURL);
30134 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30135 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30137 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30138 this.selectorEl.hide();
30140 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30141 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30143 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30144 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145 this.thumbEl.hide();
30147 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30148 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30150 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30151 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30152 this.errorEl.hide();
30154 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30155 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30156 this.footerEl.hide();
30158 this.setThumbBoxSize();
30164 this.fireEvent('initial', this);
30171 window.addEventListener("resize", function() { _this.resize(); } );
30173 this.bodyEl.on('click', this.beforeSelectFile, this);
30176 this.bodyEl.on('touchstart', this.onTouchStart, this);
30177 this.bodyEl.on('touchmove', this.onTouchMove, this);
30178 this.bodyEl.on('touchend', this.onTouchEnd, this);
30182 this.bodyEl.on('mousedown', this.onMouseDown, this);
30183 this.bodyEl.on('mousemove', this.onMouseMove, this);
30184 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30185 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30186 Roo.get(document).on('mouseup', this.onMouseUp, this);
30189 this.selectorEl.on('change', this.onFileSelected, this);
30195 this.baseScale = 1;
30197 this.baseRotate = 1;
30198 this.dragable = false;
30199 this.pinching = false;
30202 this.cropData = false;
30203 this.notifyEl.dom.innerHTML = this.emptyText;
30205 this.selectorEl.dom.value = '';
30209 resize : function()
30211 if(this.fireEvent('resize', this) != false){
30212 this.setThumbBoxPosition();
30213 this.setCanvasPosition();
30217 onFooterButtonClick : function(e, el, o, type)
30220 case 'rotate-left' :
30221 this.onRotateLeft(e);
30223 case 'rotate-right' :
30224 this.onRotateRight(e);
30227 this.beforeSelectFile(e);
30242 this.fireEvent('footerbuttonclick', this, type);
30245 beforeSelectFile : function(e)
30247 e.preventDefault();
30249 if(this.fireEvent('beforeselectfile', this) != false){
30250 this.selectorEl.dom.click();
30254 onFileSelected : function(e)
30256 e.preventDefault();
30258 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30262 var file = this.selectorEl.dom.files[0];
30264 if(this.fireEvent('inspect', this, file) != false){
30265 this.prepare(file);
30270 trash : function(e)
30272 this.fireEvent('trash', this);
30275 download : function(e)
30277 this.fireEvent('download', this);
30280 loadCanvas : function(src)
30282 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30286 this.imageEl = document.createElement('img');
30290 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30292 this.imageEl.src = src;
30296 onLoadCanvas : function()
30298 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30299 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30301 this.bodyEl.un('click', this.beforeSelectFile, this);
30303 this.notifyEl.hide();
30304 this.thumbEl.show();
30305 this.footerEl.show();
30307 this.baseRotateLevel();
30309 if(this.isDocument){
30310 this.setThumbBoxSize();
30313 this.setThumbBoxPosition();
30315 this.baseScaleLevel();
30321 this.canvasLoaded = true;
30324 this.maskEl.unmask();
30329 setCanvasPosition : function()
30331 if(!this.canvasEl){
30335 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30336 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30338 this.previewEl.setLeft(pw);
30339 this.previewEl.setTop(ph);
30343 onMouseDown : function(e)
30347 this.dragable = true;
30348 this.pinching = false;
30350 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30351 this.dragable = false;
30355 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30356 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30360 onMouseMove : function(e)
30364 if(!this.canvasLoaded){
30368 if (!this.dragable){
30372 var minX = Math.ceil(this.thumbEl.getLeft(true));
30373 var minY = Math.ceil(this.thumbEl.getTop(true));
30375 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30376 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30378 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30379 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30381 x = x - this.mouseX;
30382 y = y - this.mouseY;
30384 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30385 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30387 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30388 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30390 this.previewEl.setLeft(bgX);
30391 this.previewEl.setTop(bgY);
30393 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30394 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30397 onMouseUp : function(e)
30401 this.dragable = false;
30404 onMouseWheel : function(e)
30408 this.startScale = this.scale;
30410 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30412 if(!this.zoomable()){
30413 this.scale = this.startScale;
30422 zoomable : function()
30424 var minScale = this.thumbEl.getWidth() / this.minWidth;
30426 if(this.minWidth < this.minHeight){
30427 minScale = this.thumbEl.getHeight() / this.minHeight;
30430 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30431 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30435 (this.rotate == 0 || this.rotate == 180) &&
30437 width > this.imageEl.OriginWidth ||
30438 height > this.imageEl.OriginHeight ||
30439 (width < this.minWidth && height < this.minHeight)
30447 (this.rotate == 90 || this.rotate == 270) &&
30449 width > this.imageEl.OriginWidth ||
30450 height > this.imageEl.OriginHeight ||
30451 (width < this.minHeight && height < this.minWidth)
30458 !this.isDocument &&
30459 (this.rotate == 0 || this.rotate == 180) &&
30461 width < this.minWidth ||
30462 width > this.imageEl.OriginWidth ||
30463 height < this.minHeight ||
30464 height > this.imageEl.OriginHeight
30471 !this.isDocument &&
30472 (this.rotate == 90 || this.rotate == 270) &&
30474 width < this.minHeight ||
30475 width > this.imageEl.OriginWidth ||
30476 height < this.minWidth ||
30477 height > this.imageEl.OriginHeight
30487 onRotateLeft : function(e)
30489 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30491 var minScale = this.thumbEl.getWidth() / this.minWidth;
30493 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30494 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30496 this.startScale = this.scale;
30498 while (this.getScaleLevel() < minScale){
30500 this.scale = this.scale + 1;
30502 if(!this.zoomable()){
30507 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30508 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30513 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30520 this.scale = this.startScale;
30522 this.onRotateFail();
30527 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30529 if(this.isDocument){
30530 this.setThumbBoxSize();
30531 this.setThumbBoxPosition();
30532 this.setCanvasPosition();
30537 this.fireEvent('rotate', this, 'left');
30541 onRotateRight : function(e)
30543 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30545 var minScale = this.thumbEl.getWidth() / this.minWidth;
30547 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30548 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30550 this.startScale = this.scale;
30552 while (this.getScaleLevel() < minScale){
30554 this.scale = this.scale + 1;
30556 if(!this.zoomable()){
30561 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30562 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30567 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30574 this.scale = this.startScale;
30576 this.onRotateFail();
30581 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30583 if(this.isDocument){
30584 this.setThumbBoxSize();
30585 this.setThumbBoxPosition();
30586 this.setCanvasPosition();
30591 this.fireEvent('rotate', this, 'right');
30594 onRotateFail : function()
30596 this.errorEl.show(true);
30600 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30605 this.previewEl.dom.innerHTML = '';
30607 var canvasEl = document.createElement("canvas");
30609 var contextEl = canvasEl.getContext("2d");
30611 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30612 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30613 var center = this.imageEl.OriginWidth / 2;
30615 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30616 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30617 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30618 center = this.imageEl.OriginHeight / 2;
30621 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30623 contextEl.translate(center, center);
30624 contextEl.rotate(this.rotate * Math.PI / 180);
30626 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30628 this.canvasEl = document.createElement("canvas");
30630 this.contextEl = this.canvasEl.getContext("2d");
30632 switch (this.rotate) {
30635 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30636 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30638 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30643 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30644 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30646 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30647 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);
30651 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30656 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30657 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30659 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30660 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);
30664 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);
30669 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30670 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30672 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30673 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30677 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);
30684 this.previewEl.appendChild(this.canvasEl);
30686 this.setCanvasPosition();
30691 if(!this.canvasLoaded){
30695 var imageCanvas = document.createElement("canvas");
30697 var imageContext = imageCanvas.getContext("2d");
30699 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30700 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30702 var center = imageCanvas.width / 2;
30704 imageContext.translate(center, center);
30706 imageContext.rotate(this.rotate * Math.PI / 180);
30708 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30710 var canvas = document.createElement("canvas");
30712 var context = canvas.getContext("2d");
30714 canvas.width = this.minWidth;
30715 canvas.height = this.minHeight;
30717 switch (this.rotate) {
30720 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30721 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30723 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30724 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30726 var targetWidth = this.minWidth - 2 * x;
30727 var targetHeight = this.minHeight - 2 * y;
30731 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30732 scale = targetWidth / width;
30735 if(x > 0 && y == 0){
30736 scale = targetHeight / height;
30739 if(x > 0 && y > 0){
30740 scale = targetWidth / width;
30742 if(width < height){
30743 scale = targetHeight / height;
30747 context.scale(scale, scale);
30749 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30750 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30752 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30753 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30755 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30760 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30761 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30763 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30764 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30766 var targetWidth = this.minWidth - 2 * x;
30767 var targetHeight = this.minHeight - 2 * y;
30771 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30772 scale = targetWidth / width;
30775 if(x > 0 && y == 0){
30776 scale = targetHeight / height;
30779 if(x > 0 && y > 0){
30780 scale = targetWidth / width;
30782 if(width < height){
30783 scale = targetHeight / height;
30787 context.scale(scale, scale);
30789 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30790 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30792 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30793 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30795 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30797 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30802 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30803 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30805 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30806 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30808 var targetWidth = this.minWidth - 2 * x;
30809 var targetHeight = this.minHeight - 2 * y;
30813 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30814 scale = targetWidth / width;
30817 if(x > 0 && y == 0){
30818 scale = targetHeight / height;
30821 if(x > 0 && y > 0){
30822 scale = targetWidth / width;
30824 if(width < height){
30825 scale = targetHeight / height;
30829 context.scale(scale, scale);
30831 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30832 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30834 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30835 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30837 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30838 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30840 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30845 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30846 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30848 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30849 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30851 var targetWidth = this.minWidth - 2 * x;
30852 var targetHeight = this.minHeight - 2 * y;
30856 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30857 scale = targetWidth / width;
30860 if(x > 0 && y == 0){
30861 scale = targetHeight / height;
30864 if(x > 0 && y > 0){
30865 scale = targetWidth / width;
30867 if(width < height){
30868 scale = targetHeight / height;
30872 context.scale(scale, scale);
30874 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30875 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30877 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30878 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30880 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30882 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30889 this.cropData = canvas.toDataURL(this.cropType);
30891 if(this.fireEvent('crop', this, this.cropData) !== false){
30892 this.process(this.file, this.cropData);
30899 setThumbBoxSize : function()
30903 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30904 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30905 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30907 this.minWidth = width;
30908 this.minHeight = height;
30910 if(this.rotate == 90 || this.rotate == 270){
30911 this.minWidth = height;
30912 this.minHeight = width;
30917 width = Math.ceil(this.minWidth * height / this.minHeight);
30919 if(this.minWidth > this.minHeight){
30921 height = Math.ceil(this.minHeight * width / this.minWidth);
30924 this.thumbEl.setStyle({
30925 width : width + 'px',
30926 height : height + 'px'
30933 setThumbBoxPosition : function()
30935 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30936 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30938 this.thumbEl.setLeft(x);
30939 this.thumbEl.setTop(y);
30943 baseRotateLevel : function()
30945 this.baseRotate = 1;
30948 typeof(this.exif) != 'undefined' &&
30949 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30950 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30952 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30955 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30959 baseScaleLevel : function()
30963 if(this.isDocument){
30965 if(this.baseRotate == 6 || this.baseRotate == 8){
30967 height = this.thumbEl.getHeight();
30968 this.baseScale = height / this.imageEl.OriginWidth;
30970 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30971 width = this.thumbEl.getWidth();
30972 this.baseScale = width / this.imageEl.OriginHeight;
30978 height = this.thumbEl.getHeight();
30979 this.baseScale = height / this.imageEl.OriginHeight;
30981 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30982 width = this.thumbEl.getWidth();
30983 this.baseScale = width / this.imageEl.OriginWidth;
30989 if(this.baseRotate == 6 || this.baseRotate == 8){
30991 width = this.thumbEl.getHeight();
30992 this.baseScale = width / this.imageEl.OriginHeight;
30994 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30995 height = this.thumbEl.getWidth();
30996 this.baseScale = height / this.imageEl.OriginHeight;
30999 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31000 height = this.thumbEl.getWidth();
31001 this.baseScale = height / this.imageEl.OriginHeight;
31003 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31004 width = this.thumbEl.getHeight();
31005 this.baseScale = width / this.imageEl.OriginWidth;
31012 width = this.thumbEl.getWidth();
31013 this.baseScale = width / this.imageEl.OriginWidth;
31015 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31016 height = this.thumbEl.getHeight();
31017 this.baseScale = height / this.imageEl.OriginHeight;
31020 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31022 height = this.thumbEl.getHeight();
31023 this.baseScale = height / this.imageEl.OriginHeight;
31025 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31026 width = this.thumbEl.getWidth();
31027 this.baseScale = width / this.imageEl.OriginWidth;
31035 getScaleLevel : function()
31037 return this.baseScale * Math.pow(1.1, this.scale);
31040 onTouchStart : function(e)
31042 if(!this.canvasLoaded){
31043 this.beforeSelectFile(e);
31047 var touches = e.browserEvent.touches;
31053 if(touches.length == 1){
31054 this.onMouseDown(e);
31058 if(touches.length != 2){
31064 for(var i = 0, finger; finger = touches[i]; i++){
31065 coords.push(finger.pageX, finger.pageY);
31068 var x = Math.pow(coords[0] - coords[2], 2);
31069 var y = Math.pow(coords[1] - coords[3], 2);
31071 this.startDistance = Math.sqrt(x + y);
31073 this.startScale = this.scale;
31075 this.pinching = true;
31076 this.dragable = false;
31080 onTouchMove : function(e)
31082 if(!this.pinching && !this.dragable){
31086 var touches = e.browserEvent.touches;
31093 this.onMouseMove(e);
31099 for(var i = 0, finger; finger = touches[i]; i++){
31100 coords.push(finger.pageX, finger.pageY);
31103 var x = Math.pow(coords[0] - coords[2], 2);
31104 var y = Math.pow(coords[1] - coords[3], 2);
31106 this.endDistance = Math.sqrt(x + y);
31108 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31110 if(!this.zoomable()){
31111 this.scale = this.startScale;
31119 onTouchEnd : function(e)
31121 this.pinching = false;
31122 this.dragable = false;
31126 process : function(file, crop)
31129 this.maskEl.mask(this.loadingText);
31132 this.xhr = new XMLHttpRequest();
31134 file.xhr = this.xhr;
31136 this.xhr.open(this.method, this.url, true);
31139 "Accept": "application/json",
31140 "Cache-Control": "no-cache",
31141 "X-Requested-With": "XMLHttpRequest"
31144 for (var headerName in headers) {
31145 var headerValue = headers[headerName];
31147 this.xhr.setRequestHeader(headerName, headerValue);
31153 this.xhr.onload = function()
31155 _this.xhrOnLoad(_this.xhr);
31158 this.xhr.onerror = function()
31160 _this.xhrOnError(_this.xhr);
31163 var formData = new FormData();
31165 formData.append('returnHTML', 'NO');
31168 formData.append('crop', crop);
31171 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31172 formData.append(this.paramName, file, file.name);
31175 if(typeof(file.filename) != 'undefined'){
31176 formData.append('filename', file.filename);
31179 if(typeof(file.mimetype) != 'undefined'){
31180 formData.append('mimetype', file.mimetype);
31183 if(this.fireEvent('arrange', this, formData) != false){
31184 this.xhr.send(formData);
31188 xhrOnLoad : function(xhr)
31191 this.maskEl.unmask();
31194 if (xhr.readyState !== 4) {
31195 this.fireEvent('exception', this, xhr);
31199 var response = Roo.decode(xhr.responseText);
31201 if(!response.success){
31202 this.fireEvent('exception', this, xhr);
31206 var response = Roo.decode(xhr.responseText);
31208 this.fireEvent('upload', this, response);
31212 xhrOnError : function()
31215 this.maskEl.unmask();
31218 Roo.log('xhr on error');
31220 var response = Roo.decode(xhr.responseText);
31226 prepare : function(file)
31229 this.maskEl.mask(this.loadingText);
31235 if(typeof(file) === 'string'){
31236 this.loadCanvas(file);
31240 if(!file || !this.urlAPI){
31245 this.cropType = file.type;
31249 if(this.fireEvent('prepare', this, this.file) != false){
31251 var reader = new FileReader();
31253 reader.onload = function (e) {
31254 if (e.target.error) {
31255 Roo.log(e.target.error);
31259 var buffer = e.target.result,
31260 dataView = new DataView(buffer),
31262 maxOffset = dataView.byteLength - 4,
31266 if (dataView.getUint16(0) === 0xffd8) {
31267 while (offset < maxOffset) {
31268 markerBytes = dataView.getUint16(offset);
31270 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31271 markerLength = dataView.getUint16(offset + 2) + 2;
31272 if (offset + markerLength > dataView.byteLength) {
31273 Roo.log('Invalid meta data: Invalid segment size.');
31277 if(markerBytes == 0xffe1){
31278 _this.parseExifData(
31285 offset += markerLength;
31295 var url = _this.urlAPI.createObjectURL(_this.file);
31297 _this.loadCanvas(url);
31302 reader.readAsArrayBuffer(this.file);
31308 parseExifData : function(dataView, offset, length)
31310 var tiffOffset = offset + 10,
31314 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31315 // No Exif data, might be XMP data instead
31319 // Check for the ASCII code for "Exif" (0x45786966):
31320 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31321 // No Exif data, might be XMP data instead
31324 if (tiffOffset + 8 > dataView.byteLength) {
31325 Roo.log('Invalid Exif data: Invalid segment size.');
31328 // Check for the two null bytes:
31329 if (dataView.getUint16(offset + 8) !== 0x0000) {
31330 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31333 // Check the byte alignment:
31334 switch (dataView.getUint16(tiffOffset)) {
31336 littleEndian = true;
31339 littleEndian = false;
31342 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31345 // Check for the TIFF tag marker (0x002A):
31346 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31347 Roo.log('Invalid Exif data: Missing TIFF marker.');
31350 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31351 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31353 this.parseExifTags(
31356 tiffOffset + dirOffset,
31361 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31366 if (dirOffset + 6 > dataView.byteLength) {
31367 Roo.log('Invalid Exif data: Invalid directory offset.');
31370 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31371 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31372 if (dirEndOffset + 4 > dataView.byteLength) {
31373 Roo.log('Invalid Exif data: Invalid directory size.');
31376 for (i = 0; i < tagsNumber; i += 1) {
31380 dirOffset + 2 + 12 * i, // tag offset
31384 // Return the offset to the next directory:
31385 return dataView.getUint32(dirEndOffset, littleEndian);
31388 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31390 var tag = dataView.getUint16(offset, littleEndian);
31392 this.exif[tag] = this.getExifValue(
31396 dataView.getUint16(offset + 2, littleEndian), // tag type
31397 dataView.getUint32(offset + 4, littleEndian), // tag length
31402 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31404 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31413 Roo.log('Invalid Exif data: Invalid tag type.');
31417 tagSize = tagType.size * length;
31418 // Determine if the value is contained in the dataOffset bytes,
31419 // or if the value at the dataOffset is a pointer to the actual data:
31420 dataOffset = tagSize > 4 ?
31421 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31422 if (dataOffset + tagSize > dataView.byteLength) {
31423 Roo.log('Invalid Exif data: Invalid data offset.');
31426 if (length === 1) {
31427 return tagType.getValue(dataView, dataOffset, littleEndian);
31430 for (i = 0; i < length; i += 1) {
31431 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31434 if (tagType.ascii) {
31436 // Concatenate the chars:
31437 for (i = 0; i < values.length; i += 1) {
31439 // Ignore the terminating NULL byte(s):
31440 if (c === '\u0000') {
31452 Roo.apply(Roo.bootstrap.UploadCropbox, {
31454 'Orientation': 0x0112
31458 1: 0, //'top-left',
31460 3: 180, //'bottom-right',
31461 // 4: 'bottom-left',
31463 6: 90, //'right-top',
31464 // 7: 'right-bottom',
31465 8: 270 //'left-bottom'
31469 // byte, 8-bit unsigned int:
31471 getValue: function (dataView, dataOffset) {
31472 return dataView.getUint8(dataOffset);
31476 // ascii, 8-bit byte:
31478 getValue: function (dataView, dataOffset) {
31479 return String.fromCharCode(dataView.getUint8(dataOffset));
31484 // short, 16 bit int:
31486 getValue: function (dataView, dataOffset, littleEndian) {
31487 return dataView.getUint16(dataOffset, littleEndian);
31491 // long, 32 bit int:
31493 getValue: function (dataView, dataOffset, littleEndian) {
31494 return dataView.getUint32(dataOffset, littleEndian);
31498 // rational = two long values, first is numerator, second is denominator:
31500 getValue: function (dataView, dataOffset, littleEndian) {
31501 return dataView.getUint32(dataOffset, littleEndian) /
31502 dataView.getUint32(dataOffset + 4, littleEndian);
31506 // slong, 32 bit signed int:
31508 getValue: function (dataView, dataOffset, littleEndian) {
31509 return dataView.getInt32(dataOffset, littleEndian);
31513 // srational, two slongs, first is numerator, second is denominator:
31515 getValue: function (dataView, dataOffset, littleEndian) {
31516 return dataView.getInt32(dataOffset, littleEndian) /
31517 dataView.getInt32(dataOffset + 4, littleEndian);
31527 cls : 'btn-group roo-upload-cropbox-rotate-left',
31528 action : 'rotate-left',
31532 cls : 'btn btn-default',
31533 html : '<i class="fa fa-undo"></i>'
31539 cls : 'btn-group roo-upload-cropbox-picture',
31540 action : 'picture',
31544 cls : 'btn btn-default',
31545 html : '<i class="fa fa-picture-o"></i>'
31551 cls : 'btn-group roo-upload-cropbox-rotate-right',
31552 action : 'rotate-right',
31556 cls : 'btn btn-default',
31557 html : '<i class="fa fa-repeat"></i>'
31565 cls : 'btn-group roo-upload-cropbox-rotate-left',
31566 action : 'rotate-left',
31570 cls : 'btn btn-default',
31571 html : '<i class="fa fa-undo"></i>'
31577 cls : 'btn-group roo-upload-cropbox-download',
31578 action : 'download',
31582 cls : 'btn btn-default',
31583 html : '<i class="fa fa-download"></i>'
31589 cls : 'btn-group roo-upload-cropbox-crop',
31594 cls : 'btn btn-default',
31595 html : '<i class="fa fa-crop"></i>'
31601 cls : 'btn-group roo-upload-cropbox-trash',
31606 cls : 'btn btn-default',
31607 html : '<i class="fa fa-trash"></i>'
31613 cls : 'btn-group roo-upload-cropbox-rotate-right',
31614 action : 'rotate-right',
31618 cls : 'btn btn-default',
31619 html : '<i class="fa fa-repeat"></i>'
31627 cls : 'btn-group roo-upload-cropbox-rotate-left',
31628 action : 'rotate-left',
31632 cls : 'btn btn-default',
31633 html : '<i class="fa fa-undo"></i>'
31639 cls : 'btn-group roo-upload-cropbox-rotate-right',
31640 action : 'rotate-right',
31644 cls : 'btn btn-default',
31645 html : '<i class="fa fa-repeat"></i>'
31658 * @class Roo.bootstrap.DocumentManager
31659 * @extends Roo.bootstrap.Component
31660 * Bootstrap DocumentManager class
31661 * @cfg {String} paramName default 'imageUpload'
31662 * @cfg {String} toolTipName default 'filename'
31663 * @cfg {String} method default POST
31664 * @cfg {String} url action url
31665 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31666 * @cfg {Boolean} multiple multiple upload default true
31667 * @cfg {Number} thumbSize default 300
31668 * @cfg {String} fieldLabel
31669 * @cfg {Number} labelWidth default 4
31670 * @cfg {String} labelAlign (left|top) default left
31671 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31672 * @cfg {Number} labellg set the width of label (1-12)
31673 * @cfg {Number} labelmd set the width of label (1-12)
31674 * @cfg {Number} labelsm set the width of label (1-12)
31675 * @cfg {Number} labelxs set the width of label (1-12)
31678 * Create a new DocumentManager
31679 * @param {Object} config The config object
31682 Roo.bootstrap.DocumentManager = function(config){
31683 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31686 this.delegates = [];
31691 * Fire when initial the DocumentManager
31692 * @param {Roo.bootstrap.DocumentManager} this
31697 * inspect selected file
31698 * @param {Roo.bootstrap.DocumentManager} this
31699 * @param {File} file
31704 * Fire when xhr load exception
31705 * @param {Roo.bootstrap.DocumentManager} this
31706 * @param {XMLHttpRequest} xhr
31708 "exception" : true,
31710 * @event afterupload
31711 * Fire when xhr load exception
31712 * @param {Roo.bootstrap.DocumentManager} this
31713 * @param {XMLHttpRequest} xhr
31715 "afterupload" : true,
31718 * prepare the form data
31719 * @param {Roo.bootstrap.DocumentManager} this
31720 * @param {Object} formData
31725 * Fire when remove the file
31726 * @param {Roo.bootstrap.DocumentManager} this
31727 * @param {Object} file
31732 * Fire after refresh the file
31733 * @param {Roo.bootstrap.DocumentManager} this
31738 * Fire after click the image
31739 * @param {Roo.bootstrap.DocumentManager} this
31740 * @param {Object} file
31745 * Fire when upload a image and editable set to true
31746 * @param {Roo.bootstrap.DocumentManager} this
31747 * @param {Object} file
31751 * @event beforeselectfile
31752 * Fire before select file
31753 * @param {Roo.bootstrap.DocumentManager} this
31755 "beforeselectfile" : true,
31758 * Fire before process file
31759 * @param {Roo.bootstrap.DocumentManager} this
31760 * @param {Object} file
31764 * @event previewrendered
31765 * Fire when preview rendered
31766 * @param {Roo.bootstrap.DocumentManager} this
31767 * @param {Object} file
31769 "previewrendered" : true,
31772 "previewResize" : true
31777 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31786 paramName : 'imageUpload',
31787 toolTipName : 'filename',
31790 labelAlign : 'left',
31800 getAutoCreate : function()
31802 var managerWidget = {
31804 cls : 'roo-document-manager',
31808 cls : 'roo-document-manager-selector',
31813 cls : 'roo-document-manager-uploader',
31817 cls : 'roo-document-manager-upload-btn',
31818 html : '<i class="fa fa-plus"></i>'
31829 cls : 'column col-md-12',
31834 if(this.fieldLabel.length){
31839 cls : 'column col-md-12',
31840 html : this.fieldLabel
31844 cls : 'column col-md-12',
31849 if(this.labelAlign == 'left'){
31854 html : this.fieldLabel
31863 if(this.labelWidth > 12){
31864 content[0].style = "width: " + this.labelWidth + 'px';
31867 if(this.labelWidth < 13 && this.labelmd == 0){
31868 this.labelmd = this.labelWidth;
31871 if(this.labellg > 0){
31872 content[0].cls += ' col-lg-' + this.labellg;
31873 content[1].cls += ' col-lg-' + (12 - this.labellg);
31876 if(this.labelmd > 0){
31877 content[0].cls += ' col-md-' + this.labelmd;
31878 content[1].cls += ' col-md-' + (12 - this.labelmd);
31881 if(this.labelsm > 0){
31882 content[0].cls += ' col-sm-' + this.labelsm;
31883 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31886 if(this.labelxs > 0){
31887 content[0].cls += ' col-xs-' + this.labelxs;
31888 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31896 cls : 'row clearfix',
31904 initEvents : function()
31906 this.managerEl = this.el.select('.roo-document-manager', true).first();
31907 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31909 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31910 this.selectorEl.hide();
31913 this.selectorEl.attr('multiple', 'multiple');
31916 this.selectorEl.on('change', this.onFileSelected, this);
31918 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31919 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31921 this.uploader.on('click', this.onUploaderClick, this);
31923 this.renderProgressDialog();
31927 window.addEventListener("resize", function() { _this.refresh(); } );
31929 this.fireEvent('initial', this);
31932 renderProgressDialog : function()
31936 this.progressDialog = new Roo.bootstrap.Modal({
31937 cls : 'roo-document-manager-progress-dialog',
31938 allow_close : false,
31949 btnclick : function() {
31950 _this.uploadCancel();
31956 this.progressDialog.render(Roo.get(document.body));
31958 this.progress = new Roo.bootstrap.Progress({
31959 cls : 'roo-document-manager-progress',
31964 this.progress.render(this.progressDialog.getChildContainer());
31966 this.progressBar = new Roo.bootstrap.ProgressBar({
31967 cls : 'roo-document-manager-progress-bar',
31970 aria_valuemax : 12,
31974 this.progressBar.render(this.progress.getChildContainer());
31977 onUploaderClick : function(e)
31979 e.preventDefault();
31981 if(this.fireEvent('beforeselectfile', this) != false){
31982 this.selectorEl.dom.click();
31987 onFileSelected : function(e)
31989 e.preventDefault();
31991 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31995 Roo.each(this.selectorEl.dom.files, function(file){
31996 if(this.fireEvent('inspect', this, file) != false){
31997 this.files.push(file);
32007 this.selectorEl.dom.value = '';
32009 if(!this.files || !this.files.length){
32013 if(this.boxes > 0 && this.files.length > this.boxes){
32014 this.files = this.files.slice(0, this.boxes);
32017 this.uploader.show();
32019 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32020 this.uploader.hide();
32029 Roo.each(this.files, function(file){
32031 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32032 var f = this.renderPreview(file);
32037 if(file.type.indexOf('image') != -1){
32038 this.delegates.push(
32040 _this.process(file);
32041 }).createDelegate(this)
32049 _this.process(file);
32050 }).createDelegate(this)
32055 this.files = files;
32057 this.delegates = this.delegates.concat(docs);
32059 if(!this.delegates.length){
32064 this.progressBar.aria_valuemax = this.delegates.length;
32071 arrange : function()
32073 if(!this.delegates.length){
32074 this.progressDialog.hide();
32079 var delegate = this.delegates.shift();
32081 this.progressDialog.show();
32083 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32085 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32090 refresh : function()
32092 this.uploader.show();
32094 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32095 this.uploader.hide();
32098 Roo.isTouch ? this.closable(false) : this.closable(true);
32100 this.fireEvent('refresh', this);
32103 onRemove : function(e, el, o)
32105 e.preventDefault();
32107 this.fireEvent('remove', this, o);
32111 remove : function(o)
32115 Roo.each(this.files, function(file){
32116 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32125 this.files = files;
32132 Roo.each(this.files, function(file){
32137 file.target.remove();
32146 onClick : function(e, el, o)
32148 e.preventDefault();
32150 this.fireEvent('click', this, o);
32154 closable : function(closable)
32156 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32158 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32170 xhrOnLoad : function(xhr)
32172 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32176 if (xhr.readyState !== 4) {
32178 this.fireEvent('exception', this, xhr);
32182 var response = Roo.decode(xhr.responseText);
32184 if(!response.success){
32186 this.fireEvent('exception', this, xhr);
32190 var file = this.renderPreview(response.data);
32192 this.files.push(file);
32196 this.fireEvent('afterupload', this, xhr);
32200 xhrOnError : function(xhr)
32202 Roo.log('xhr on error');
32204 var response = Roo.decode(xhr.responseText);
32211 process : function(file)
32213 if(this.fireEvent('process', this, file) !== false){
32214 if(this.editable && file.type.indexOf('image') != -1){
32215 this.fireEvent('edit', this, file);
32219 this.uploadStart(file, false);
32226 uploadStart : function(file, crop)
32228 this.xhr = new XMLHttpRequest();
32230 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32235 file.xhr = this.xhr;
32237 this.managerEl.createChild({
32239 cls : 'roo-document-manager-loading',
32243 tooltip : file.name,
32244 cls : 'roo-document-manager-thumb',
32245 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32251 this.xhr.open(this.method, this.url, true);
32254 "Accept": "application/json",
32255 "Cache-Control": "no-cache",
32256 "X-Requested-With": "XMLHttpRequest"
32259 for (var headerName in headers) {
32260 var headerValue = headers[headerName];
32262 this.xhr.setRequestHeader(headerName, headerValue);
32268 this.xhr.onload = function()
32270 _this.xhrOnLoad(_this.xhr);
32273 this.xhr.onerror = function()
32275 _this.xhrOnError(_this.xhr);
32278 var formData = new FormData();
32280 formData.append('returnHTML', 'NO');
32283 formData.append('crop', crop);
32286 formData.append(this.paramName, file, file.name);
32293 if(this.fireEvent('prepare', this, formData, options) != false){
32295 if(options.manually){
32299 this.xhr.send(formData);
32303 this.uploadCancel();
32306 uploadCancel : function()
32312 this.delegates = [];
32314 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32321 renderPreview : function(file)
32323 if(typeof(file.target) != 'undefined' && file.target){
32327 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32329 var previewEl = this.managerEl.createChild({
32331 cls : 'roo-document-manager-preview',
32335 tooltip : file[this.toolTipName],
32336 cls : 'roo-document-manager-thumb',
32337 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32342 html : '<i class="fa fa-times-circle"></i>'
32347 var close = previewEl.select('button.close', true).first();
32349 close.on('click', this.onRemove, this, file);
32351 file.target = previewEl;
32353 var image = previewEl.select('img', true).first();
32357 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32359 image.on('click', this.onClick, this, file);
32361 this.fireEvent('previewrendered', this, file);
32367 onPreviewLoad : function(file, image)
32369 if(typeof(file.target) == 'undefined' || !file.target){
32373 var width = image.dom.naturalWidth || image.dom.width;
32374 var height = image.dom.naturalHeight || image.dom.height;
32376 if(!this.previewResize) {
32380 if(width > height){
32381 file.target.addClass('wide');
32385 file.target.addClass('tall');
32390 uploadFromSource : function(file, crop)
32392 this.xhr = new XMLHttpRequest();
32394 this.managerEl.createChild({
32396 cls : 'roo-document-manager-loading',
32400 tooltip : file.name,
32401 cls : 'roo-document-manager-thumb',
32402 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32408 this.xhr.open(this.method, this.url, true);
32411 "Accept": "application/json",
32412 "Cache-Control": "no-cache",
32413 "X-Requested-With": "XMLHttpRequest"
32416 for (var headerName in headers) {
32417 var headerValue = headers[headerName];
32419 this.xhr.setRequestHeader(headerName, headerValue);
32425 this.xhr.onload = function()
32427 _this.xhrOnLoad(_this.xhr);
32430 this.xhr.onerror = function()
32432 _this.xhrOnError(_this.xhr);
32435 var formData = new FormData();
32437 formData.append('returnHTML', 'NO');
32439 formData.append('crop', crop);
32441 if(typeof(file.filename) != 'undefined'){
32442 formData.append('filename', file.filename);
32445 if(typeof(file.mimetype) != 'undefined'){
32446 formData.append('mimetype', file.mimetype);
32451 if(this.fireEvent('prepare', this, formData) != false){
32452 this.xhr.send(formData);
32462 * @class Roo.bootstrap.DocumentViewer
32463 * @extends Roo.bootstrap.Component
32464 * Bootstrap DocumentViewer class
32465 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32466 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32469 * Create a new DocumentViewer
32470 * @param {Object} config The config object
32473 Roo.bootstrap.DocumentViewer = function(config){
32474 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32479 * Fire after initEvent
32480 * @param {Roo.bootstrap.DocumentViewer} this
32486 * @param {Roo.bootstrap.DocumentViewer} this
32491 * Fire after download button
32492 * @param {Roo.bootstrap.DocumentViewer} this
32497 * Fire after trash button
32498 * @param {Roo.bootstrap.DocumentViewer} this
32505 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32507 showDownload : true,
32511 getAutoCreate : function()
32515 cls : 'roo-document-viewer',
32519 cls : 'roo-document-viewer-body',
32523 cls : 'roo-document-viewer-thumb',
32527 cls : 'roo-document-viewer-image'
32535 cls : 'roo-document-viewer-footer',
32538 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32542 cls : 'btn-group roo-document-viewer-download',
32546 cls : 'btn btn-default',
32547 html : '<i class="fa fa-download"></i>'
32553 cls : 'btn-group roo-document-viewer-trash',
32557 cls : 'btn btn-default',
32558 html : '<i class="fa fa-trash"></i>'
32571 initEvents : function()
32573 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32574 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32576 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32577 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32579 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32580 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32582 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32583 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32585 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32586 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32588 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32589 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32591 this.bodyEl.on('click', this.onClick, this);
32592 this.downloadBtn.on('click', this.onDownload, this);
32593 this.trashBtn.on('click', this.onTrash, this);
32595 this.downloadBtn.hide();
32596 this.trashBtn.hide();
32598 if(this.showDownload){
32599 this.downloadBtn.show();
32602 if(this.showTrash){
32603 this.trashBtn.show();
32606 if(!this.showDownload && !this.showTrash) {
32607 this.footerEl.hide();
32612 initial : function()
32614 this.fireEvent('initial', this);
32618 onClick : function(e)
32620 e.preventDefault();
32622 this.fireEvent('click', this);
32625 onDownload : function(e)
32627 e.preventDefault();
32629 this.fireEvent('download', this);
32632 onTrash : function(e)
32634 e.preventDefault();
32636 this.fireEvent('trash', this);
32648 * @class Roo.bootstrap.NavProgressBar
32649 * @extends Roo.bootstrap.Component
32650 * Bootstrap NavProgressBar class
32653 * Create a new nav progress bar
32654 * @param {Object} config The config object
32657 Roo.bootstrap.NavProgressBar = function(config){
32658 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32660 this.bullets = this.bullets || [];
32662 // Roo.bootstrap.NavProgressBar.register(this);
32666 * Fires when the active item changes
32667 * @param {Roo.bootstrap.NavProgressBar} this
32668 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32669 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32676 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32681 getAutoCreate : function()
32683 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32687 cls : 'roo-navigation-bar-group',
32691 cls : 'roo-navigation-top-bar'
32695 cls : 'roo-navigation-bullets-bar',
32699 cls : 'roo-navigation-bar'
32706 cls : 'roo-navigation-bottom-bar'
32716 initEvents: function()
32721 onRender : function(ct, position)
32723 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32725 if(this.bullets.length){
32726 Roo.each(this.bullets, function(b){
32735 addItem : function(cfg)
32737 var item = new Roo.bootstrap.NavProgressItem(cfg);
32739 item.parentId = this.id;
32740 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32743 var top = new Roo.bootstrap.Element({
32745 cls : 'roo-navigation-bar-text'
32748 var bottom = new Roo.bootstrap.Element({
32750 cls : 'roo-navigation-bar-text'
32753 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32754 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32756 var topText = new Roo.bootstrap.Element({
32758 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32761 var bottomText = new Roo.bootstrap.Element({
32763 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32766 topText.onRender(top.el, null);
32767 bottomText.onRender(bottom.el, null);
32770 item.bottomEl = bottom;
32773 this.barItems.push(item);
32778 getActive : function()
32780 var active = false;
32782 Roo.each(this.barItems, function(v){
32784 if (!v.isActive()) {
32796 setActiveItem : function(item)
32800 Roo.each(this.barItems, function(v){
32801 if (v.rid == item.rid) {
32805 if (v.isActive()) {
32806 v.setActive(false);
32811 item.setActive(true);
32813 this.fireEvent('changed', this, item, prev);
32816 getBarItem: function(rid)
32820 Roo.each(this.barItems, function(e) {
32821 if (e.rid != rid) {
32832 indexOfItem : function(item)
32836 Roo.each(this.barItems, function(v, i){
32838 if (v.rid != item.rid) {
32849 setActiveNext : function()
32851 var i = this.indexOfItem(this.getActive());
32853 if (i > this.barItems.length) {
32857 this.setActiveItem(this.barItems[i+1]);
32860 setActivePrev : function()
32862 var i = this.indexOfItem(this.getActive());
32868 this.setActiveItem(this.barItems[i-1]);
32871 format : function()
32873 if(!this.barItems.length){
32877 var width = 100 / this.barItems.length;
32879 Roo.each(this.barItems, function(i){
32880 i.el.setStyle('width', width + '%');
32881 i.topEl.el.setStyle('width', width + '%');
32882 i.bottomEl.el.setStyle('width', width + '%');
32891 * Nav Progress Item
32896 * @class Roo.bootstrap.NavProgressItem
32897 * @extends Roo.bootstrap.Component
32898 * Bootstrap NavProgressItem class
32899 * @cfg {String} rid the reference id
32900 * @cfg {Boolean} active (true|false) Is item active default false
32901 * @cfg {Boolean} disabled (true|false) Is item active default false
32902 * @cfg {String} html
32903 * @cfg {String} position (top|bottom) text position default bottom
32904 * @cfg {String} icon show icon instead of number
32907 * Create a new NavProgressItem
32908 * @param {Object} config The config object
32910 Roo.bootstrap.NavProgressItem = function(config){
32911 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32916 * The raw click event for the entire grid.
32917 * @param {Roo.bootstrap.NavProgressItem} this
32918 * @param {Roo.EventObject} e
32925 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32931 position : 'bottom',
32934 getAutoCreate : function()
32936 var iconCls = 'roo-navigation-bar-item-icon';
32938 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32942 cls: 'roo-navigation-bar-item',
32952 cfg.cls += ' active';
32955 cfg.cls += ' disabled';
32961 disable : function()
32963 this.setDisabled(true);
32966 enable : function()
32968 this.setDisabled(false);
32971 initEvents: function()
32973 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32975 this.iconEl.on('click', this.onClick, this);
32978 onClick : function(e)
32980 e.preventDefault();
32986 if(this.fireEvent('click', this, e) === false){
32990 this.parent().setActiveItem(this);
32993 isActive: function ()
32995 return this.active;
32998 setActive : function(state)
33000 if(this.active == state){
33004 this.active = state;
33007 this.el.addClass('active');
33011 this.el.removeClass('active');
33016 setDisabled : function(state)
33018 if(this.disabled == state){
33022 this.disabled = state;
33025 this.el.addClass('disabled');
33029 this.el.removeClass('disabled');
33032 tooltipEl : function()
33034 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33047 * @class Roo.bootstrap.FieldLabel
33048 * @extends Roo.bootstrap.Component
33049 * Bootstrap FieldLabel class
33050 * @cfg {String} html contents of the element
33051 * @cfg {String} tag tag of the element default label
33052 * @cfg {String} cls class of the element
33053 * @cfg {String} target label target
33054 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33055 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33056 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33057 * @cfg {String} iconTooltip default "This field is required"
33058 * @cfg {String} indicatorpos (left|right) default left
33061 * Create a new FieldLabel
33062 * @param {Object} config The config object
33065 Roo.bootstrap.FieldLabel = function(config){
33066 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33071 * Fires after the field has been marked as invalid.
33072 * @param {Roo.form.FieldLabel} this
33073 * @param {String} msg The validation message
33078 * Fires after the field has been validated with no errors.
33079 * @param {Roo.form.FieldLabel} this
33085 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33092 invalidClass : 'has-warning',
33093 validClass : 'has-success',
33094 iconTooltip : 'This field is required',
33095 indicatorpos : 'left',
33097 getAutoCreate : function(){
33100 if (!this.allowBlank) {
33106 cls : 'roo-bootstrap-field-label ' + this.cls,
33111 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33112 tooltip : this.iconTooltip
33121 if(this.indicatorpos == 'right'){
33124 cls : 'roo-bootstrap-field-label ' + this.cls,
33133 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33134 tooltip : this.iconTooltip
33143 initEvents: function()
33145 Roo.bootstrap.Element.superclass.initEvents.call(this);
33147 this.indicator = this.indicatorEl();
33149 if(this.indicator){
33150 this.indicator.removeClass('visible');
33151 this.indicator.addClass('invisible');
33154 Roo.bootstrap.FieldLabel.register(this);
33157 indicatorEl : function()
33159 var indicator = this.el.select('i.roo-required-indicator',true).first();
33170 * Mark this field as valid
33172 markValid : function()
33174 if(this.indicator){
33175 this.indicator.removeClass('visible');
33176 this.indicator.addClass('invisible');
33178 if (Roo.bootstrap.version == 3) {
33179 this.el.removeClass(this.invalidClass);
33180 this.el.addClass(this.validClass);
33182 this.el.removeClass('is-invalid');
33183 this.el.addClass('is-valid');
33187 this.fireEvent('valid', this);
33191 * Mark this field as invalid
33192 * @param {String} msg The validation message
33194 markInvalid : function(msg)
33196 if(this.indicator){
33197 this.indicator.removeClass('invisible');
33198 this.indicator.addClass('visible');
33200 if (Roo.bootstrap.version == 3) {
33201 this.el.removeClass(this.validClass);
33202 this.el.addClass(this.invalidClass);
33204 this.el.removeClass('is-valid');
33205 this.el.addClass('is-invalid');
33209 this.fireEvent('invalid', this, msg);
33215 Roo.apply(Roo.bootstrap.FieldLabel, {
33220 * register a FieldLabel Group
33221 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33223 register : function(label)
33225 if(this.groups.hasOwnProperty(label.target)){
33229 this.groups[label.target] = label;
33233 * fetch a FieldLabel Group based on the target
33234 * @param {string} target
33235 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33237 get: function(target) {
33238 if (typeof(this.groups[target]) == 'undefined') {
33242 return this.groups[target] ;
33251 * page DateSplitField.
33257 * @class Roo.bootstrap.DateSplitField
33258 * @extends Roo.bootstrap.Component
33259 * Bootstrap DateSplitField class
33260 * @cfg {string} fieldLabel - the label associated
33261 * @cfg {Number} labelWidth set the width of label (0-12)
33262 * @cfg {String} labelAlign (top|left)
33263 * @cfg {Boolean} dayAllowBlank (true|false) default false
33264 * @cfg {Boolean} monthAllowBlank (true|false) default false
33265 * @cfg {Boolean} yearAllowBlank (true|false) default false
33266 * @cfg {string} dayPlaceholder
33267 * @cfg {string} monthPlaceholder
33268 * @cfg {string} yearPlaceholder
33269 * @cfg {string} dayFormat default 'd'
33270 * @cfg {string} monthFormat default 'm'
33271 * @cfg {string} yearFormat default 'Y'
33272 * @cfg {Number} labellg set the width of label (1-12)
33273 * @cfg {Number} labelmd set the width of label (1-12)
33274 * @cfg {Number} labelsm set the width of label (1-12)
33275 * @cfg {Number} labelxs set the width of label (1-12)
33279 * Create a new DateSplitField
33280 * @param {Object} config The config object
33283 Roo.bootstrap.DateSplitField = function(config){
33284 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33290 * getting the data of years
33291 * @param {Roo.bootstrap.DateSplitField} this
33292 * @param {Object} years
33297 * getting the data of days
33298 * @param {Roo.bootstrap.DateSplitField} this
33299 * @param {Object} days
33304 * Fires after the field has been marked as invalid.
33305 * @param {Roo.form.Field} this
33306 * @param {String} msg The validation message
33311 * Fires after the field has been validated with no errors.
33312 * @param {Roo.form.Field} this
33318 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33321 labelAlign : 'top',
33323 dayAllowBlank : false,
33324 monthAllowBlank : false,
33325 yearAllowBlank : false,
33326 dayPlaceholder : '',
33327 monthPlaceholder : '',
33328 yearPlaceholder : '',
33332 isFormField : true,
33338 getAutoCreate : function()
33342 cls : 'row roo-date-split-field-group',
33347 cls : 'form-hidden-field roo-date-split-field-group-value',
33353 var labelCls = 'col-md-12';
33354 var contentCls = 'col-md-4';
33356 if(this.fieldLabel){
33360 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33364 html : this.fieldLabel
33369 if(this.labelAlign == 'left'){
33371 if(this.labelWidth > 12){
33372 label.style = "width: " + this.labelWidth + 'px';
33375 if(this.labelWidth < 13 && this.labelmd == 0){
33376 this.labelmd = this.labelWidth;
33379 if(this.labellg > 0){
33380 labelCls = ' col-lg-' + this.labellg;
33381 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33384 if(this.labelmd > 0){
33385 labelCls = ' col-md-' + this.labelmd;
33386 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33389 if(this.labelsm > 0){
33390 labelCls = ' col-sm-' + this.labelsm;
33391 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33394 if(this.labelxs > 0){
33395 labelCls = ' col-xs-' + this.labelxs;
33396 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33400 label.cls += ' ' + labelCls;
33402 cfg.cn.push(label);
33405 Roo.each(['day', 'month', 'year'], function(t){
33408 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33415 inputEl: function ()
33417 return this.el.select('.roo-date-split-field-group-value', true).first();
33420 onRender : function(ct, position)
33424 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33426 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33428 this.dayField = new Roo.bootstrap.ComboBox({
33429 allowBlank : this.dayAllowBlank,
33430 alwaysQuery : true,
33431 displayField : 'value',
33434 forceSelection : true,
33436 placeholder : this.dayPlaceholder,
33437 selectOnFocus : true,
33438 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33439 triggerAction : 'all',
33441 valueField : 'value',
33442 store : new Roo.data.SimpleStore({
33443 data : (function() {
33445 _this.fireEvent('days', _this, days);
33448 fields : [ 'value' ]
33451 select : function (_self, record, index)
33453 _this.setValue(_this.getValue());
33458 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33460 this.monthField = new Roo.bootstrap.MonthField({
33461 after : '<i class=\"fa fa-calendar\"></i>',
33462 allowBlank : this.monthAllowBlank,
33463 placeholder : this.monthPlaceholder,
33466 render : function (_self)
33468 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33469 e.preventDefault();
33473 select : function (_self, oldvalue, newvalue)
33475 _this.setValue(_this.getValue());
33480 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33482 this.yearField = new Roo.bootstrap.ComboBox({
33483 allowBlank : this.yearAllowBlank,
33484 alwaysQuery : true,
33485 displayField : 'value',
33488 forceSelection : true,
33490 placeholder : this.yearPlaceholder,
33491 selectOnFocus : true,
33492 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33493 triggerAction : 'all',
33495 valueField : 'value',
33496 store : new Roo.data.SimpleStore({
33497 data : (function() {
33499 _this.fireEvent('years', _this, years);
33502 fields : [ 'value' ]
33505 select : function (_self, record, index)
33507 _this.setValue(_this.getValue());
33512 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33515 setValue : function(v, format)
33517 this.inputEl.dom.value = v;
33519 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33521 var d = Date.parseDate(v, f);
33528 this.setDay(d.format(this.dayFormat));
33529 this.setMonth(d.format(this.monthFormat));
33530 this.setYear(d.format(this.yearFormat));
33537 setDay : function(v)
33539 this.dayField.setValue(v);
33540 this.inputEl.dom.value = this.getValue();
33545 setMonth : function(v)
33547 this.monthField.setValue(v, true);
33548 this.inputEl.dom.value = this.getValue();
33553 setYear : function(v)
33555 this.yearField.setValue(v);
33556 this.inputEl.dom.value = this.getValue();
33561 getDay : function()
33563 return this.dayField.getValue();
33566 getMonth : function()
33568 return this.monthField.getValue();
33571 getYear : function()
33573 return this.yearField.getValue();
33576 getValue : function()
33578 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33580 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33590 this.inputEl.dom.value = '';
33595 validate : function()
33597 var d = this.dayField.validate();
33598 var m = this.monthField.validate();
33599 var y = this.yearField.validate();
33604 (!this.dayAllowBlank && !d) ||
33605 (!this.monthAllowBlank && !m) ||
33606 (!this.yearAllowBlank && !y)
33611 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33620 this.markInvalid();
33625 markValid : function()
33628 var label = this.el.select('label', true).first();
33629 var icon = this.el.select('i.fa-star', true).first();
33635 this.fireEvent('valid', this);
33639 * Mark this field as invalid
33640 * @param {String} msg The validation message
33642 markInvalid : function(msg)
33645 var label = this.el.select('label', true).first();
33646 var icon = this.el.select('i.fa-star', true).first();
33648 if(label && !icon){
33649 this.el.select('.roo-date-split-field-label', true).createChild({
33651 cls : 'text-danger fa fa-lg fa-star',
33652 tooltip : 'This field is required',
33653 style : 'margin-right:5px;'
33657 this.fireEvent('invalid', this, msg);
33660 clearInvalid : function()
33662 var label = this.el.select('label', true).first();
33663 var icon = this.el.select('i.fa-star', true).first();
33669 this.fireEvent('valid', this);
33672 getName: function()
33682 * http://masonry.desandro.com
33684 * The idea is to render all the bricks based on vertical width...
33686 * The original code extends 'outlayer' - we might need to use that....
33692 * @class Roo.bootstrap.LayoutMasonry
33693 * @extends Roo.bootstrap.Component
33694 * Bootstrap Layout Masonry class
33697 * Create a new Element
33698 * @param {Object} config The config object
33701 Roo.bootstrap.LayoutMasonry = function(config){
33703 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33707 Roo.bootstrap.LayoutMasonry.register(this);
33713 * Fire after layout the items
33714 * @param {Roo.bootstrap.LayoutMasonry} this
33715 * @param {Roo.EventObject} e
33722 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33725 * @cfg {Boolean} isLayoutInstant = no animation?
33727 isLayoutInstant : false, // needed?
33730 * @cfg {Number} boxWidth width of the columns
33735 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33740 * @cfg {Number} padWidth padding below box..
33745 * @cfg {Number} gutter gutter width..
33750 * @cfg {Number} maxCols maximum number of columns
33756 * @cfg {Boolean} isAutoInitial defalut true
33758 isAutoInitial : true,
33763 * @cfg {Boolean} isHorizontal defalut false
33765 isHorizontal : false,
33767 currentSize : null,
33773 bricks: null, //CompositeElement
33777 _isLayoutInited : false,
33779 // isAlternative : false, // only use for vertical layout...
33782 * @cfg {Number} alternativePadWidth padding below box..
33784 alternativePadWidth : 50,
33786 selectedBrick : [],
33788 getAutoCreate : function(){
33790 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33794 cls: 'blog-masonary-wrapper ' + this.cls,
33796 cls : 'mas-boxes masonary'
33803 getChildContainer: function( )
33805 if (this.boxesEl) {
33806 return this.boxesEl;
33809 this.boxesEl = this.el.select('.mas-boxes').first();
33811 return this.boxesEl;
33815 initEvents : function()
33819 if(this.isAutoInitial){
33820 Roo.log('hook children rendered');
33821 this.on('childrenrendered', function() {
33822 Roo.log('children rendered');
33828 initial : function()
33830 this.selectedBrick = [];
33832 this.currentSize = this.el.getBox(true);
33834 Roo.EventManager.onWindowResize(this.resize, this);
33836 if(!this.isAutoInitial){
33844 //this.layout.defer(500,this);
33848 resize : function()
33850 var cs = this.el.getBox(true);
33853 this.currentSize.width == cs.width &&
33854 this.currentSize.x == cs.x &&
33855 this.currentSize.height == cs.height &&
33856 this.currentSize.y == cs.y
33858 Roo.log("no change in with or X or Y");
33862 this.currentSize = cs;
33868 layout : function()
33870 this._resetLayout();
33872 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33874 this.layoutItems( isInstant );
33876 this._isLayoutInited = true;
33878 this.fireEvent('layout', this);
33882 _resetLayout : function()
33884 if(this.isHorizontal){
33885 this.horizontalMeasureColumns();
33889 this.verticalMeasureColumns();
33893 verticalMeasureColumns : function()
33895 this.getContainerWidth();
33897 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33898 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33902 var boxWidth = this.boxWidth + this.padWidth;
33904 if(this.containerWidth < this.boxWidth){
33905 boxWidth = this.containerWidth
33908 var containerWidth = this.containerWidth;
33910 var cols = Math.floor(containerWidth / boxWidth);
33912 this.cols = Math.max( cols, 1 );
33914 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33916 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33918 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33920 this.colWidth = boxWidth + avail - this.padWidth;
33922 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33923 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33926 horizontalMeasureColumns : function()
33928 this.getContainerWidth();
33930 var boxWidth = this.boxWidth;
33932 if(this.containerWidth < boxWidth){
33933 boxWidth = this.containerWidth;
33936 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33938 this.el.setHeight(boxWidth);
33942 getContainerWidth : function()
33944 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33947 layoutItems : function( isInstant )
33949 Roo.log(this.bricks);
33951 var items = Roo.apply([], this.bricks);
33953 if(this.isHorizontal){
33954 this._horizontalLayoutItems( items , isInstant );
33958 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33959 // this._verticalAlternativeLayoutItems( items , isInstant );
33963 this._verticalLayoutItems( items , isInstant );
33967 _verticalLayoutItems : function ( items , isInstant)
33969 if ( !items || !items.length ) {
33974 ['xs', 'xs', 'xs', 'tall'],
33975 ['xs', 'xs', 'tall'],
33976 ['xs', 'xs', 'sm'],
33977 ['xs', 'xs', 'xs'],
33983 ['sm', 'xs', 'xs'],
33987 ['tall', 'xs', 'xs', 'xs'],
33988 ['tall', 'xs', 'xs'],
34000 Roo.each(items, function(item, k){
34002 switch (item.size) {
34003 // these layouts take up a full box,
34014 boxes.push([item]);
34037 var filterPattern = function(box, length)
34045 var pattern = box.slice(0, length);
34049 Roo.each(pattern, function(i){
34050 format.push(i.size);
34053 Roo.each(standard, function(s){
34055 if(String(s) != String(format)){
34064 if(!match && length == 1){
34069 filterPattern(box, length - 1);
34073 queue.push(pattern);
34075 box = box.slice(length, box.length);
34077 filterPattern(box, 4);
34083 Roo.each(boxes, function(box, k){
34089 if(box.length == 1){
34094 filterPattern(box, 4);
34098 this._processVerticalLayoutQueue( queue, isInstant );
34102 // _verticalAlternativeLayoutItems : function( items , isInstant )
34104 // if ( !items || !items.length ) {
34108 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34112 _horizontalLayoutItems : function ( items , isInstant)
34114 if ( !items || !items.length || items.length < 3) {
34120 var eItems = items.slice(0, 3);
34122 items = items.slice(3, items.length);
34125 ['xs', 'xs', 'xs', 'wide'],
34126 ['xs', 'xs', 'wide'],
34127 ['xs', 'xs', 'sm'],
34128 ['xs', 'xs', 'xs'],
34134 ['sm', 'xs', 'xs'],
34138 ['wide', 'xs', 'xs', 'xs'],
34139 ['wide', 'xs', 'xs'],
34152 Roo.each(items, function(item, k){
34154 switch (item.size) {
34165 boxes.push([item]);
34189 var filterPattern = function(box, length)
34197 var pattern = box.slice(0, length);
34201 Roo.each(pattern, function(i){
34202 format.push(i.size);
34205 Roo.each(standard, function(s){
34207 if(String(s) != String(format)){
34216 if(!match && length == 1){
34221 filterPattern(box, length - 1);
34225 queue.push(pattern);
34227 box = box.slice(length, box.length);
34229 filterPattern(box, 4);
34235 Roo.each(boxes, function(box, k){
34241 if(box.length == 1){
34246 filterPattern(box, 4);
34253 var pos = this.el.getBox(true);
34257 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34259 var hit_end = false;
34261 Roo.each(queue, function(box){
34265 Roo.each(box, function(b){
34267 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34277 Roo.each(box, function(b){
34279 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34282 mx = Math.max(mx, b.x);
34286 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34290 Roo.each(box, function(b){
34292 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34306 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34309 /** Sets position of item in DOM
34310 * @param {Element} item
34311 * @param {Number} x - horizontal position
34312 * @param {Number} y - vertical position
34313 * @param {Boolean} isInstant - disables transitions
34315 _processVerticalLayoutQueue : function( queue, isInstant )
34317 var pos = this.el.getBox(true);
34322 for (var i = 0; i < this.cols; i++){
34326 Roo.each(queue, function(box, k){
34328 var col = k % this.cols;
34330 Roo.each(box, function(b,kk){
34332 b.el.position('absolute');
34334 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34335 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34337 if(b.size == 'md-left' || b.size == 'md-right'){
34338 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34339 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34342 b.el.setWidth(width);
34343 b.el.setHeight(height);
34345 b.el.select('iframe',true).setSize(width,height);
34349 for (var i = 0; i < this.cols; i++){
34351 if(maxY[i] < maxY[col]){
34356 col = Math.min(col, i);
34360 x = pos.x + col * (this.colWidth + this.padWidth);
34364 var positions = [];
34366 switch (box.length){
34368 positions = this.getVerticalOneBoxColPositions(x, y, box);
34371 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34374 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34377 positions = this.getVerticalFourBoxColPositions(x, y, box);
34383 Roo.each(box, function(b,kk){
34385 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34387 var sz = b.el.getSize();
34389 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34397 for (var i = 0; i < this.cols; i++){
34398 mY = Math.max(mY, maxY[i]);
34401 this.el.setHeight(mY - pos.y);
34405 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34407 // var pos = this.el.getBox(true);
34410 // var maxX = pos.right;
34412 // var maxHeight = 0;
34414 // Roo.each(items, function(item, k){
34418 // item.el.position('absolute');
34420 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34422 // item.el.setWidth(width);
34424 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34426 // item.el.setHeight(height);
34429 // item.el.setXY([x, y], isInstant ? false : true);
34431 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34434 // y = y + height + this.alternativePadWidth;
34436 // maxHeight = maxHeight + height + this.alternativePadWidth;
34440 // this.el.setHeight(maxHeight);
34444 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34446 var pos = this.el.getBox(true);
34451 var maxX = pos.right;
34453 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34455 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34457 Roo.each(queue, function(box, k){
34459 Roo.each(box, function(b, kk){
34461 b.el.position('absolute');
34463 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34464 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34466 if(b.size == 'md-left' || b.size == 'md-right'){
34467 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34468 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34471 b.el.setWidth(width);
34472 b.el.setHeight(height);
34480 var positions = [];
34482 switch (box.length){
34484 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34487 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34490 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34493 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34499 Roo.each(box, function(b,kk){
34501 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34503 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34511 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34513 Roo.each(eItems, function(b,k){
34515 b.size = (k == 0) ? 'sm' : 'xs';
34516 b.x = (k == 0) ? 2 : 1;
34517 b.y = (k == 0) ? 2 : 1;
34519 b.el.position('absolute');
34521 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34523 b.el.setWidth(width);
34525 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34527 b.el.setHeight(height);
34531 var positions = [];
34534 x : maxX - this.unitWidth * 2 - this.gutter,
34539 x : maxX - this.unitWidth,
34540 y : minY + (this.unitWidth + this.gutter) * 2
34544 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34548 Roo.each(eItems, function(b,k){
34550 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34556 getVerticalOneBoxColPositions : function(x, y, box)
34560 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34562 if(box[0].size == 'md-left'){
34566 if(box[0].size == 'md-right'){
34571 x : x + (this.unitWidth + this.gutter) * rand,
34578 getVerticalTwoBoxColPositions : function(x, y, box)
34582 if(box[0].size == 'xs'){
34586 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34590 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34604 x : x + (this.unitWidth + this.gutter) * 2,
34605 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34612 getVerticalThreeBoxColPositions : function(x, y, box)
34616 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34624 x : x + (this.unitWidth + this.gutter) * 1,
34629 x : x + (this.unitWidth + this.gutter) * 2,
34637 if(box[0].size == 'xs' && box[1].size == 'xs'){
34646 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34650 x : x + (this.unitWidth + this.gutter) * 1,
34664 x : x + (this.unitWidth + this.gutter) * 2,
34669 x : x + (this.unitWidth + this.gutter) * 2,
34670 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34677 getVerticalFourBoxColPositions : function(x, y, box)
34681 if(box[0].size == 'xs'){
34690 y : y + (this.unitHeight + this.gutter) * 1
34695 y : y + (this.unitHeight + this.gutter) * 2
34699 x : x + (this.unitWidth + this.gutter) * 1,
34713 x : x + (this.unitWidth + this.gutter) * 2,
34718 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34719 y : y + (this.unitHeight + this.gutter) * 1
34723 x : x + (this.unitWidth + this.gutter) * 2,
34724 y : y + (this.unitWidth + this.gutter) * 2
34731 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34735 if(box[0].size == 'md-left'){
34737 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34744 if(box[0].size == 'md-right'){
34746 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34747 y : minY + (this.unitWidth + this.gutter) * 1
34753 var rand = Math.floor(Math.random() * (4 - box[0].y));
34756 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34757 y : minY + (this.unitWidth + this.gutter) * rand
34764 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34768 if(box[0].size == 'xs'){
34771 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34776 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34777 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34785 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34790 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34791 y : minY + (this.unitWidth + this.gutter) * 2
34798 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34802 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34805 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34810 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34811 y : minY + (this.unitWidth + this.gutter) * 1
34815 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34816 y : minY + (this.unitWidth + this.gutter) * 2
34823 if(box[0].size == 'xs' && box[1].size == 'xs'){
34826 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34831 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34836 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34837 y : minY + (this.unitWidth + this.gutter) * 1
34845 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34850 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34851 y : minY + (this.unitWidth + this.gutter) * 2
34855 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34856 y : minY + (this.unitWidth + this.gutter) * 2
34863 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34867 if(box[0].size == 'xs'){
34870 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34880 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),
34885 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34886 y : minY + (this.unitWidth + this.gutter) * 1
34894 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34899 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34900 y : minY + (this.unitWidth + this.gutter) * 2
34904 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34905 y : minY + (this.unitWidth + this.gutter) * 2
34909 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),
34910 y : minY + (this.unitWidth + this.gutter) * 2
34918 * remove a Masonry Brick
34919 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34921 removeBrick : function(brick_id)
34927 for (var i = 0; i<this.bricks.length; i++) {
34928 if (this.bricks[i].id == brick_id) {
34929 this.bricks.splice(i,1);
34930 this.el.dom.removeChild(Roo.get(brick_id).dom);
34937 * adds a Masonry Brick
34938 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34940 addBrick : function(cfg)
34942 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34943 //this.register(cn);
34944 cn.parentId = this.id;
34945 cn.render(this.el);
34950 * register a Masonry Brick
34951 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34954 register : function(brick)
34956 this.bricks.push(brick);
34957 brick.masonryId = this.id;
34961 * clear all the Masonry Brick
34963 clearAll : function()
34966 //this.getChildContainer().dom.innerHTML = "";
34967 this.el.dom.innerHTML = '';
34970 getSelected : function()
34972 if (!this.selectedBrick) {
34976 return this.selectedBrick;
34980 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34984 * register a Masonry Layout
34985 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34988 register : function(layout)
34990 this.groups[layout.id] = layout;
34993 * fetch a Masonry Layout based on the masonry layout ID
34994 * @param {string} the masonry layout to add
34995 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34998 get: function(layout_id) {
34999 if (typeof(this.groups[layout_id]) == 'undefined') {
35002 return this.groups[layout_id] ;
35014 * http://masonry.desandro.com
35016 * The idea is to render all the bricks based on vertical width...
35018 * The original code extends 'outlayer' - we might need to use that....
35024 * @class Roo.bootstrap.LayoutMasonryAuto
35025 * @extends Roo.bootstrap.Component
35026 * Bootstrap Layout Masonry class
35029 * Create a new Element
35030 * @param {Object} config The config object
35033 Roo.bootstrap.LayoutMasonryAuto = function(config){
35034 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35037 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35040 * @cfg {Boolean} isFitWidth - resize the width..
35042 isFitWidth : false, // options..
35044 * @cfg {Boolean} isOriginLeft = left align?
35046 isOriginLeft : true,
35048 * @cfg {Boolean} isOriginTop = top align?
35050 isOriginTop : false,
35052 * @cfg {Boolean} isLayoutInstant = no animation?
35054 isLayoutInstant : false, // needed?
35056 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35058 isResizingContainer : true,
35060 * @cfg {Number} columnWidth width of the columns
35066 * @cfg {Number} maxCols maximum number of columns
35071 * @cfg {Number} padHeight padding below box..
35077 * @cfg {Boolean} isAutoInitial defalut true
35080 isAutoInitial : true,
35086 initialColumnWidth : 0,
35087 currentSize : null,
35089 colYs : null, // array.
35096 bricks: null, //CompositeElement
35097 cols : 0, // array?
35098 // element : null, // wrapped now this.el
35099 _isLayoutInited : null,
35102 getAutoCreate : function(){
35106 cls: 'blog-masonary-wrapper ' + this.cls,
35108 cls : 'mas-boxes masonary'
35115 getChildContainer: function( )
35117 if (this.boxesEl) {
35118 return this.boxesEl;
35121 this.boxesEl = this.el.select('.mas-boxes').first();
35123 return this.boxesEl;
35127 initEvents : function()
35131 if(this.isAutoInitial){
35132 Roo.log('hook children rendered');
35133 this.on('childrenrendered', function() {
35134 Roo.log('children rendered');
35141 initial : function()
35143 this.reloadItems();
35145 this.currentSize = this.el.getBox(true);
35147 /// was window resize... - let's see if this works..
35148 Roo.EventManager.onWindowResize(this.resize, this);
35150 if(!this.isAutoInitial){
35155 this.layout.defer(500,this);
35158 reloadItems: function()
35160 this.bricks = this.el.select('.masonry-brick', true);
35162 this.bricks.each(function(b) {
35163 //Roo.log(b.getSize());
35164 if (!b.attr('originalwidth')) {
35165 b.attr('originalwidth', b.getSize().width);
35170 Roo.log(this.bricks.elements.length);
35173 resize : function()
35176 var cs = this.el.getBox(true);
35178 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35179 Roo.log("no change in with or X");
35182 this.currentSize = cs;
35186 layout : function()
35189 this._resetLayout();
35190 //this._manageStamps();
35192 // don't animate first layout
35193 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35194 this.layoutItems( isInstant );
35196 // flag for initalized
35197 this._isLayoutInited = true;
35200 layoutItems : function( isInstant )
35202 //var items = this._getItemsForLayout( this.items );
35203 // original code supports filtering layout items.. we just ignore it..
35205 this._layoutItems( this.bricks , isInstant );
35207 this._postLayout();
35209 _layoutItems : function ( items , isInstant)
35211 //this.fireEvent( 'layout', this, items );
35214 if ( !items || !items.elements.length ) {
35215 // no items, emit event with empty array
35220 items.each(function(item) {
35221 Roo.log("layout item");
35223 // get x/y object from method
35224 var position = this._getItemLayoutPosition( item );
35226 position.item = item;
35227 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35228 queue.push( position );
35231 this._processLayoutQueue( queue );
35233 /** Sets position of item in DOM
35234 * @param {Element} item
35235 * @param {Number} x - horizontal position
35236 * @param {Number} y - vertical position
35237 * @param {Boolean} isInstant - disables transitions
35239 _processLayoutQueue : function( queue )
35241 for ( var i=0, len = queue.length; i < len; i++ ) {
35242 var obj = queue[i];
35243 obj.item.position('absolute');
35244 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35250 * Any logic you want to do after each layout,
35251 * i.e. size the container
35253 _postLayout : function()
35255 this.resizeContainer();
35258 resizeContainer : function()
35260 if ( !this.isResizingContainer ) {
35263 var size = this._getContainerSize();
35265 this.el.setSize(size.width,size.height);
35266 this.boxesEl.setSize(size.width,size.height);
35272 _resetLayout : function()
35274 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35275 this.colWidth = this.el.getWidth();
35276 //this.gutter = this.el.getWidth();
35278 this.measureColumns();
35284 this.colYs.push( 0 );
35290 measureColumns : function()
35292 this.getContainerWidth();
35293 // if columnWidth is 0, default to outerWidth of first item
35294 if ( !this.columnWidth ) {
35295 var firstItem = this.bricks.first();
35296 Roo.log(firstItem);
35297 this.columnWidth = this.containerWidth;
35298 if (firstItem && firstItem.attr('originalwidth') ) {
35299 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35301 // columnWidth fall back to item of first element
35302 Roo.log("set column width?");
35303 this.initialColumnWidth = this.columnWidth ;
35305 // if first elem has no width, default to size of container
35310 if (this.initialColumnWidth) {
35311 this.columnWidth = this.initialColumnWidth;
35316 // column width is fixed at the top - however if container width get's smaller we should
35319 // this bit calcs how man columns..
35321 var columnWidth = this.columnWidth += this.gutter;
35323 // calculate columns
35324 var containerWidth = this.containerWidth + this.gutter;
35326 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35327 // fix rounding errors, typically with gutters
35328 var excess = columnWidth - containerWidth % columnWidth;
35331 // if overshoot is less than a pixel, round up, otherwise floor it
35332 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35333 cols = Math[ mathMethod ]( cols );
35334 this.cols = Math.max( cols, 1 );
35335 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35337 // padding positioning..
35338 var totalColWidth = this.cols * this.columnWidth;
35339 var padavail = this.containerWidth - totalColWidth;
35340 // so for 2 columns - we need 3 'pads'
35342 var padNeeded = (1+this.cols) * this.padWidth;
35344 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35346 this.columnWidth += padExtra
35347 //this.padWidth = Math.floor(padavail / ( this.cols));
35349 // adjust colum width so that padding is fixed??
35351 // we have 3 columns ... total = width * 3
35352 // we have X left over... that should be used by
35354 //if (this.expandC) {
35362 getContainerWidth : function()
35364 /* // container is parent if fit width
35365 var container = this.isFitWidth ? this.element.parentNode : this.element;
35366 // check that this.size and size are there
35367 // IE8 triggers resize on body size change, so they might not be
35369 var size = getSize( container ); //FIXME
35370 this.containerWidth = size && size.innerWidth; //FIXME
35373 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35377 _getItemLayoutPosition : function( item ) // what is item?
35379 // we resize the item to our columnWidth..
35381 item.setWidth(this.columnWidth);
35382 item.autoBoxAdjust = false;
35384 var sz = item.getSize();
35386 // how many columns does this brick span
35387 var remainder = this.containerWidth % this.columnWidth;
35389 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35390 // round if off by 1 pixel, otherwise use ceil
35391 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35392 colSpan = Math.min( colSpan, this.cols );
35394 // normally this should be '1' as we dont' currently allow multi width columns..
35396 var colGroup = this._getColGroup( colSpan );
35397 // get the minimum Y value from the columns
35398 var minimumY = Math.min.apply( Math, colGroup );
35399 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35401 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35403 // position the brick
35405 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35406 y: this.currentSize.y + minimumY + this.padHeight
35410 // apply setHeight to necessary columns
35411 var setHeight = minimumY + sz.height + this.padHeight;
35412 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35414 var setSpan = this.cols + 1 - colGroup.length;
35415 for ( var i = 0; i < setSpan; i++ ) {
35416 this.colYs[ shortColIndex + i ] = setHeight ;
35423 * @param {Number} colSpan - number of columns the element spans
35424 * @returns {Array} colGroup
35426 _getColGroup : function( colSpan )
35428 if ( colSpan < 2 ) {
35429 // if brick spans only one column, use all the column Ys
35434 // how many different places could this brick fit horizontally
35435 var groupCount = this.cols + 1 - colSpan;
35436 // for each group potential horizontal position
35437 for ( var i = 0; i < groupCount; i++ ) {
35438 // make an array of colY values for that one group
35439 var groupColYs = this.colYs.slice( i, i + colSpan );
35440 // and get the max value of the array
35441 colGroup[i] = Math.max.apply( Math, groupColYs );
35446 _manageStamp : function( stamp )
35448 var stampSize = stamp.getSize();
35449 var offset = stamp.getBox();
35450 // get the columns that this stamp affects
35451 var firstX = this.isOriginLeft ? offset.x : offset.right;
35452 var lastX = firstX + stampSize.width;
35453 var firstCol = Math.floor( firstX / this.columnWidth );
35454 firstCol = Math.max( 0, firstCol );
35456 var lastCol = Math.floor( lastX / this.columnWidth );
35457 // lastCol should not go over if multiple of columnWidth #425
35458 lastCol -= lastX % this.columnWidth ? 0 : 1;
35459 lastCol = Math.min( this.cols - 1, lastCol );
35461 // set colYs to bottom of the stamp
35462 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35465 for ( var i = firstCol; i <= lastCol; i++ ) {
35466 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35471 _getContainerSize : function()
35473 this.maxY = Math.max.apply( Math, this.colYs );
35478 if ( this.isFitWidth ) {
35479 size.width = this._getContainerFitWidth();
35485 _getContainerFitWidth : function()
35487 var unusedCols = 0;
35488 // count unused columns
35491 if ( this.colYs[i] !== 0 ) {
35496 // fit container to columns that have been used
35497 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35500 needsResizeLayout : function()
35502 var previousWidth = this.containerWidth;
35503 this.getContainerWidth();
35504 return previousWidth !== this.containerWidth;
35519 * @class Roo.bootstrap.MasonryBrick
35520 * @extends Roo.bootstrap.Component
35521 * Bootstrap MasonryBrick class
35524 * Create a new MasonryBrick
35525 * @param {Object} config The config object
35528 Roo.bootstrap.MasonryBrick = function(config){
35530 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35532 Roo.bootstrap.MasonryBrick.register(this);
35538 * When a MasonryBrick is clcik
35539 * @param {Roo.bootstrap.MasonryBrick} this
35540 * @param {Roo.EventObject} e
35546 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35549 * @cfg {String} title
35553 * @cfg {String} html
35557 * @cfg {String} bgimage
35561 * @cfg {String} videourl
35565 * @cfg {String} cls
35569 * @cfg {String} href
35573 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35578 * @cfg {String} placetitle (center|bottom)
35583 * @cfg {Boolean} isFitContainer defalut true
35585 isFitContainer : true,
35588 * @cfg {Boolean} preventDefault defalut false
35590 preventDefault : false,
35593 * @cfg {Boolean} inverse defalut false
35595 maskInverse : false,
35597 getAutoCreate : function()
35599 if(!this.isFitContainer){
35600 return this.getSplitAutoCreate();
35603 var cls = 'masonry-brick masonry-brick-full';
35605 if(this.href.length){
35606 cls += ' masonry-brick-link';
35609 if(this.bgimage.length){
35610 cls += ' masonry-brick-image';
35613 if(this.maskInverse){
35614 cls += ' mask-inverse';
35617 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35618 cls += ' enable-mask';
35622 cls += ' masonry-' + this.size + '-brick';
35625 if(this.placetitle.length){
35627 switch (this.placetitle) {
35629 cls += ' masonry-center-title';
35632 cls += ' masonry-bottom-title';
35639 if(!this.html.length && !this.bgimage.length){
35640 cls += ' masonry-center-title';
35643 if(!this.html.length && this.bgimage.length){
35644 cls += ' masonry-bottom-title';
35649 cls += ' ' + this.cls;
35653 tag: (this.href.length) ? 'a' : 'div',
35658 cls: 'masonry-brick-mask'
35662 cls: 'masonry-brick-paragraph',
35668 if(this.href.length){
35669 cfg.href = this.href;
35672 var cn = cfg.cn[1].cn;
35674 if(this.title.length){
35677 cls: 'masonry-brick-title',
35682 if(this.html.length){
35685 cls: 'masonry-brick-text',
35690 if (!this.title.length && !this.html.length) {
35691 cfg.cn[1].cls += ' hide';
35694 if(this.bgimage.length){
35697 cls: 'masonry-brick-image-view',
35702 if(this.videourl.length){
35703 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35704 // youtube support only?
35707 cls: 'masonry-brick-image-view',
35710 allowfullscreen : true
35718 getSplitAutoCreate : function()
35720 var cls = 'masonry-brick masonry-brick-split';
35722 if(this.href.length){
35723 cls += ' masonry-brick-link';
35726 if(this.bgimage.length){
35727 cls += ' masonry-brick-image';
35731 cls += ' masonry-' + this.size + '-brick';
35734 switch (this.placetitle) {
35736 cls += ' masonry-center-title';
35739 cls += ' masonry-bottom-title';
35742 if(!this.bgimage.length){
35743 cls += ' masonry-center-title';
35746 if(this.bgimage.length){
35747 cls += ' masonry-bottom-title';
35753 cls += ' ' + this.cls;
35757 tag: (this.href.length) ? 'a' : 'div',
35762 cls: 'masonry-brick-split-head',
35766 cls: 'masonry-brick-paragraph',
35773 cls: 'masonry-brick-split-body',
35779 if(this.href.length){
35780 cfg.href = this.href;
35783 if(this.title.length){
35784 cfg.cn[0].cn[0].cn.push({
35786 cls: 'masonry-brick-title',
35791 if(this.html.length){
35792 cfg.cn[1].cn.push({
35794 cls: 'masonry-brick-text',
35799 if(this.bgimage.length){
35800 cfg.cn[0].cn.push({
35802 cls: 'masonry-brick-image-view',
35807 if(this.videourl.length){
35808 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35809 // youtube support only?
35810 cfg.cn[0].cn.cn.push({
35812 cls: 'masonry-brick-image-view',
35815 allowfullscreen : true
35822 initEvents: function()
35824 switch (this.size) {
35857 this.el.on('touchstart', this.onTouchStart, this);
35858 this.el.on('touchmove', this.onTouchMove, this);
35859 this.el.on('touchend', this.onTouchEnd, this);
35860 this.el.on('contextmenu', this.onContextMenu, this);
35862 this.el.on('mouseenter' ,this.enter, this);
35863 this.el.on('mouseleave', this.leave, this);
35864 this.el.on('click', this.onClick, this);
35867 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35868 this.parent().bricks.push(this);
35873 onClick: function(e, el)
35875 var time = this.endTimer - this.startTimer;
35876 // Roo.log(e.preventDefault());
35879 e.preventDefault();
35884 if(!this.preventDefault){
35888 e.preventDefault();
35890 if (this.activeClass != '') {
35891 this.selectBrick();
35894 this.fireEvent('click', this, e);
35897 enter: function(e, el)
35899 e.preventDefault();
35901 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35905 if(this.bgimage.length && this.html.length){
35906 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35910 leave: function(e, el)
35912 e.preventDefault();
35914 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35918 if(this.bgimage.length && this.html.length){
35919 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35923 onTouchStart: function(e, el)
35925 // e.preventDefault();
35927 this.touchmoved = false;
35929 if(!this.isFitContainer){
35933 if(!this.bgimage.length || !this.html.length){
35937 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35939 this.timer = new Date().getTime();
35943 onTouchMove: function(e, el)
35945 this.touchmoved = true;
35948 onContextMenu : function(e,el)
35950 e.preventDefault();
35951 e.stopPropagation();
35955 onTouchEnd: function(e, el)
35957 // e.preventDefault();
35959 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35966 if(!this.bgimage.length || !this.html.length){
35968 if(this.href.length){
35969 window.location.href = this.href;
35975 if(!this.isFitContainer){
35979 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35981 window.location.href = this.href;
35984 //selection on single brick only
35985 selectBrick : function() {
35987 if (!this.parentId) {
35991 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35992 var index = m.selectedBrick.indexOf(this.id);
35995 m.selectedBrick.splice(index,1);
35996 this.el.removeClass(this.activeClass);
36000 for(var i = 0; i < m.selectedBrick.length; i++) {
36001 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36002 b.el.removeClass(b.activeClass);
36005 m.selectedBrick = [];
36007 m.selectedBrick.push(this.id);
36008 this.el.addClass(this.activeClass);
36012 isSelected : function(){
36013 return this.el.hasClass(this.activeClass);
36018 Roo.apply(Roo.bootstrap.MasonryBrick, {
36021 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36023 * register a Masonry Brick
36024 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36027 register : function(brick)
36029 //this.groups[brick.id] = brick;
36030 this.groups.add(brick.id, brick);
36033 * fetch a masonry brick based on the masonry brick ID
36034 * @param {string} the masonry brick to add
36035 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36038 get: function(brick_id)
36040 // if (typeof(this.groups[brick_id]) == 'undefined') {
36043 // return this.groups[brick_id] ;
36045 if(this.groups.key(brick_id)) {
36046 return this.groups.key(brick_id);
36064 * @class Roo.bootstrap.Brick
36065 * @extends Roo.bootstrap.Component
36066 * Bootstrap Brick class
36069 * Create a new Brick
36070 * @param {Object} config The config object
36073 Roo.bootstrap.Brick = function(config){
36074 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36080 * When a Brick is click
36081 * @param {Roo.bootstrap.Brick} this
36082 * @param {Roo.EventObject} e
36088 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36091 * @cfg {String} title
36095 * @cfg {String} html
36099 * @cfg {String} bgimage
36103 * @cfg {String} cls
36107 * @cfg {String} href
36111 * @cfg {String} video
36115 * @cfg {Boolean} square
36119 getAutoCreate : function()
36121 var cls = 'roo-brick';
36123 if(this.href.length){
36124 cls += ' roo-brick-link';
36127 if(this.bgimage.length){
36128 cls += ' roo-brick-image';
36131 if(!this.html.length && !this.bgimage.length){
36132 cls += ' roo-brick-center-title';
36135 if(!this.html.length && this.bgimage.length){
36136 cls += ' roo-brick-bottom-title';
36140 cls += ' ' + this.cls;
36144 tag: (this.href.length) ? 'a' : 'div',
36149 cls: 'roo-brick-paragraph',
36155 if(this.href.length){
36156 cfg.href = this.href;
36159 var cn = cfg.cn[0].cn;
36161 if(this.title.length){
36164 cls: 'roo-brick-title',
36169 if(this.html.length){
36172 cls: 'roo-brick-text',
36179 if(this.bgimage.length){
36182 cls: 'roo-brick-image-view',
36190 initEvents: function()
36192 if(this.title.length || this.html.length){
36193 this.el.on('mouseenter' ,this.enter, this);
36194 this.el.on('mouseleave', this.leave, this);
36197 Roo.EventManager.onWindowResize(this.resize, this);
36199 if(this.bgimage.length){
36200 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36201 this.imageEl.on('load', this.onImageLoad, this);
36208 onImageLoad : function()
36213 resize : function()
36215 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36217 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36219 if(this.bgimage.length){
36220 var image = this.el.select('.roo-brick-image-view', true).first();
36222 image.setWidth(paragraph.getWidth());
36225 image.setHeight(paragraph.getWidth());
36228 this.el.setHeight(image.getHeight());
36229 paragraph.setHeight(image.getHeight());
36235 enter: function(e, el)
36237 e.preventDefault();
36239 if(this.bgimage.length){
36240 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36241 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36245 leave: function(e, el)
36247 e.preventDefault();
36249 if(this.bgimage.length){
36250 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36251 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36266 * @class Roo.bootstrap.NumberField
36267 * @extends Roo.bootstrap.Input
36268 * Bootstrap NumberField class
36274 * Create a new NumberField
36275 * @param {Object} config The config object
36278 Roo.bootstrap.NumberField = function(config){
36279 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36282 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36285 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36287 allowDecimals : true,
36289 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36291 decimalSeparator : ".",
36293 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36295 decimalPrecision : 2,
36297 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36299 allowNegative : true,
36302 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36306 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36308 minValue : Number.NEGATIVE_INFINITY,
36310 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36312 maxValue : Number.MAX_VALUE,
36314 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36316 minText : "The minimum value for this field is {0}",
36318 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36320 maxText : "The maximum value for this field is {0}",
36322 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36323 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36325 nanText : "{0} is not a valid number",
36327 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36329 thousandsDelimiter : false,
36331 * @cfg {String} valueAlign alignment of value
36333 valueAlign : "left",
36335 getAutoCreate : function()
36337 var hiddenInput = {
36341 cls: 'hidden-number-input'
36345 hiddenInput.name = this.name;
36350 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36352 this.name = hiddenInput.name;
36354 if(cfg.cn.length > 0) {
36355 cfg.cn.push(hiddenInput);
36362 initEvents : function()
36364 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36366 var allowed = "0123456789";
36368 if(this.allowDecimals){
36369 allowed += this.decimalSeparator;
36372 if(this.allowNegative){
36376 if(this.thousandsDelimiter) {
36380 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36382 var keyPress = function(e){
36384 var k = e.getKey();
36386 var c = e.getCharCode();
36389 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36390 allowed.indexOf(String.fromCharCode(c)) === -1
36396 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36400 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36405 this.el.on("keypress", keyPress, this);
36408 validateValue : function(value)
36411 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36415 var num = this.parseValue(value);
36418 this.markInvalid(String.format(this.nanText, value));
36422 if(num < this.minValue){
36423 this.markInvalid(String.format(this.minText, this.minValue));
36427 if(num > this.maxValue){
36428 this.markInvalid(String.format(this.maxText, this.maxValue));
36435 getValue : function()
36437 var v = this.hiddenEl().getValue();
36439 return this.fixPrecision(this.parseValue(v));
36442 parseValue : function(value)
36444 if(this.thousandsDelimiter) {
36446 r = new RegExp(",", "g");
36447 value = value.replace(r, "");
36450 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36451 return isNaN(value) ? '' : value;
36454 fixPrecision : function(value)
36456 if(this.thousandsDelimiter) {
36458 r = new RegExp(",", "g");
36459 value = value.replace(r, "");
36462 var nan = isNaN(value);
36464 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36465 return nan ? '' : value;
36467 return parseFloat(value).toFixed(this.decimalPrecision);
36470 setValue : function(v)
36472 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36478 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36480 this.inputEl().dom.value = (v == '') ? '' :
36481 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36483 if(!this.allowZero && v === '0') {
36484 this.hiddenEl().dom.value = '';
36485 this.inputEl().dom.value = '';
36492 decimalPrecisionFcn : function(v)
36494 return Math.floor(v);
36497 beforeBlur : function()
36499 var v = this.parseValue(this.getRawValue());
36501 if(v || v === 0 || v === ''){
36506 hiddenEl : function()
36508 return this.el.select('input.hidden-number-input',true).first();
36520 * @class Roo.bootstrap.DocumentSlider
36521 * @extends Roo.bootstrap.Component
36522 * Bootstrap DocumentSlider class
36525 * Create a new DocumentViewer
36526 * @param {Object} config The config object
36529 Roo.bootstrap.DocumentSlider = function(config){
36530 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36537 * Fire after initEvent
36538 * @param {Roo.bootstrap.DocumentSlider} this
36543 * Fire after update
36544 * @param {Roo.bootstrap.DocumentSlider} this
36550 * @param {Roo.bootstrap.DocumentSlider} this
36556 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36562 getAutoCreate : function()
36566 cls : 'roo-document-slider',
36570 cls : 'roo-document-slider-header',
36574 cls : 'roo-document-slider-header-title'
36580 cls : 'roo-document-slider-body',
36584 cls : 'roo-document-slider-prev',
36588 cls : 'fa fa-chevron-left'
36594 cls : 'roo-document-slider-thumb',
36598 cls : 'roo-document-slider-image'
36604 cls : 'roo-document-slider-next',
36608 cls : 'fa fa-chevron-right'
36620 initEvents : function()
36622 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36623 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36625 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36626 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36628 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36629 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36631 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36632 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36634 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36635 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36637 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36638 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36640 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36641 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36643 this.thumbEl.on('click', this.onClick, this);
36645 this.prevIndicator.on('click', this.prev, this);
36647 this.nextIndicator.on('click', this.next, this);
36651 initial : function()
36653 if(this.files.length){
36654 this.indicator = 1;
36658 this.fireEvent('initial', this);
36661 update : function()
36663 this.imageEl.attr('src', this.files[this.indicator - 1]);
36665 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36667 this.prevIndicator.show();
36669 if(this.indicator == 1){
36670 this.prevIndicator.hide();
36673 this.nextIndicator.show();
36675 if(this.indicator == this.files.length){
36676 this.nextIndicator.hide();
36679 this.thumbEl.scrollTo('top');
36681 this.fireEvent('update', this);
36684 onClick : function(e)
36686 e.preventDefault();
36688 this.fireEvent('click', this);
36693 e.preventDefault();
36695 this.indicator = Math.max(1, this.indicator - 1);
36702 e.preventDefault();
36704 this.indicator = Math.min(this.files.length, this.indicator + 1);
36718 * @class Roo.bootstrap.RadioSet
36719 * @extends Roo.bootstrap.Input
36720 * Bootstrap RadioSet class
36721 * @cfg {String} indicatorpos (left|right) default left
36722 * @cfg {Boolean} inline (true|false) inline the element (default true)
36723 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36725 * Create a new RadioSet
36726 * @param {Object} config The config object
36729 Roo.bootstrap.RadioSet = function(config){
36731 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36735 Roo.bootstrap.RadioSet.register(this);
36740 * Fires when the element is checked or unchecked.
36741 * @param {Roo.bootstrap.RadioSet} this This radio
36742 * @param {Roo.bootstrap.Radio} item The checked item
36747 * Fires when the element is click.
36748 * @param {Roo.bootstrap.RadioSet} this This radio set
36749 * @param {Roo.bootstrap.Radio} item The checked item
36750 * @param {Roo.EventObject} e The event object
36757 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36765 indicatorpos : 'left',
36767 getAutoCreate : function()
36771 cls : 'roo-radio-set-label',
36775 html : this.fieldLabel
36779 if (Roo.bootstrap.version == 3) {
36782 if(this.indicatorpos == 'left'){
36785 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36786 tooltip : 'This field is required'
36791 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36792 tooltip : 'This field is required'
36798 cls : 'roo-radio-set-items'
36801 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36803 if (align === 'left' && this.fieldLabel.length) {
36806 cls : "roo-radio-set-right",
36812 if(this.labelWidth > 12){
36813 label.style = "width: " + this.labelWidth + 'px';
36816 if(this.labelWidth < 13 && this.labelmd == 0){
36817 this.labelmd = this.labelWidth;
36820 if(this.labellg > 0){
36821 label.cls += ' col-lg-' + this.labellg;
36822 items.cls += ' col-lg-' + (12 - this.labellg);
36825 if(this.labelmd > 0){
36826 label.cls += ' col-md-' + this.labelmd;
36827 items.cls += ' col-md-' + (12 - this.labelmd);
36830 if(this.labelsm > 0){
36831 label.cls += ' col-sm-' + this.labelsm;
36832 items.cls += ' col-sm-' + (12 - this.labelsm);
36835 if(this.labelxs > 0){
36836 label.cls += ' col-xs-' + this.labelxs;
36837 items.cls += ' col-xs-' + (12 - this.labelxs);
36843 cls : 'roo-radio-set',
36847 cls : 'roo-radio-set-input',
36850 value : this.value ? this.value : ''
36857 if(this.weight.length){
36858 cfg.cls += ' roo-radio-' + this.weight;
36862 cfg.cls += ' roo-radio-set-inline';
36866 ['xs','sm','md','lg'].map(function(size){
36867 if (settings[size]) {
36868 cfg.cls += ' col-' + size + '-' + settings[size];
36876 initEvents : function()
36878 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36879 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36881 if(!this.fieldLabel.length){
36882 this.labelEl.hide();
36885 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36886 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36888 this.indicator = this.indicatorEl();
36890 if(this.indicator){
36891 this.indicator.addClass('invisible');
36894 this.originalValue = this.getValue();
36898 inputEl: function ()
36900 return this.el.select('.roo-radio-set-input', true).first();
36903 getChildContainer : function()
36905 return this.itemsEl;
36908 register : function(item)
36910 this.radioes.push(item);
36914 validate : function()
36916 if(this.getVisibilityEl().hasClass('hidden')){
36922 Roo.each(this.radioes, function(i){
36931 if(this.allowBlank) {
36935 if(this.disabled || valid){
36940 this.markInvalid();
36945 markValid : function()
36947 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36948 this.indicatorEl().removeClass('visible');
36949 this.indicatorEl().addClass('invisible');
36953 if (Roo.bootstrap.version == 3) {
36954 this.el.removeClass([this.invalidClass, this.validClass]);
36955 this.el.addClass(this.validClass);
36957 this.el.removeClass(['is-invalid','is-valid']);
36958 this.el.addClass(['is-valid']);
36960 this.fireEvent('valid', this);
36963 markInvalid : function(msg)
36965 if(this.allowBlank || this.disabled){
36969 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36970 this.indicatorEl().removeClass('invisible');
36971 this.indicatorEl().addClass('visible');
36973 if (Roo.bootstrap.version == 3) {
36974 this.el.removeClass([this.invalidClass, this.validClass]);
36975 this.el.addClass(this.invalidClass);
36977 this.el.removeClass(['is-invalid','is-valid']);
36978 this.el.addClass(['is-invalid']);
36981 this.fireEvent('invalid', this, msg);
36985 setValue : function(v, suppressEvent)
36987 if(this.value === v){
36994 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36997 Roo.each(this.radioes, function(i){
36999 i.el.removeClass('checked');
37002 Roo.each(this.radioes, function(i){
37004 if(i.value === v || i.value.toString() === v.toString()){
37006 i.el.addClass('checked');
37008 if(suppressEvent !== true){
37009 this.fireEvent('check', this, i);
37020 clearInvalid : function(){
37022 if(!this.el || this.preventMark){
37026 this.el.removeClass([this.invalidClass]);
37028 this.fireEvent('valid', this);
37033 Roo.apply(Roo.bootstrap.RadioSet, {
37037 register : function(set)
37039 this.groups[set.name] = set;
37042 get: function(name)
37044 if (typeof(this.groups[name]) == 'undefined') {
37048 return this.groups[name] ;
37054 * Ext JS Library 1.1.1
37055 * Copyright(c) 2006-2007, Ext JS, LLC.
37057 * Originally Released Under LGPL - original licence link has changed is not relivant.
37060 * <script type="text/javascript">
37065 * @class Roo.bootstrap.SplitBar
37066 * @extends Roo.util.Observable
37067 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37071 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37072 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37073 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37074 split.minSize = 100;
37075 split.maxSize = 600;
37076 split.animate = true;
37077 split.on('moved', splitterMoved);
37080 * Create a new SplitBar
37081 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37082 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37083 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37084 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37085 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37086 position of the SplitBar).
37088 Roo.bootstrap.SplitBar = function(cfg){
37093 // dragElement : elm
37094 // resizingElement: el,
37096 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37097 // placement : Roo.bootstrap.SplitBar.LEFT ,
37098 // existingProxy ???
37101 this.el = Roo.get(cfg.dragElement, true);
37102 this.el.dom.unselectable = "on";
37104 this.resizingEl = Roo.get(cfg.resizingElement, true);
37108 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37109 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37112 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37115 * The minimum size of the resizing element. (Defaults to 0)
37121 * The maximum size of the resizing element. (Defaults to 2000)
37124 this.maxSize = 2000;
37127 * Whether to animate the transition to the new size
37130 this.animate = false;
37133 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37136 this.useShim = false;
37141 if(!cfg.existingProxy){
37143 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37145 this.proxy = Roo.get(cfg.existingProxy).dom;
37148 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37151 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37154 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37157 this.dragSpecs = {};
37160 * @private The adapter to use to positon and resize elements
37162 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37163 this.adapter.init(this);
37165 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37167 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37168 this.el.addClass("roo-splitbar-h");
37171 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37172 this.el.addClass("roo-splitbar-v");
37178 * Fires when the splitter is moved (alias for {@link #event-moved})
37179 * @param {Roo.bootstrap.SplitBar} this
37180 * @param {Number} newSize the new width or height
37185 * Fires when the splitter is moved
37186 * @param {Roo.bootstrap.SplitBar} this
37187 * @param {Number} newSize the new width or height
37191 * @event beforeresize
37192 * Fires before the splitter is dragged
37193 * @param {Roo.bootstrap.SplitBar} this
37195 "beforeresize" : true,
37197 "beforeapply" : true
37200 Roo.util.Observable.call(this);
37203 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37204 onStartProxyDrag : function(x, y){
37205 this.fireEvent("beforeresize", this);
37207 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37209 o.enableDisplayMode("block");
37210 // all splitbars share the same overlay
37211 Roo.bootstrap.SplitBar.prototype.overlay = o;
37213 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37214 this.overlay.show();
37215 Roo.get(this.proxy).setDisplayed("block");
37216 var size = this.adapter.getElementSize(this);
37217 this.activeMinSize = this.getMinimumSize();;
37218 this.activeMaxSize = this.getMaximumSize();;
37219 var c1 = size - this.activeMinSize;
37220 var c2 = Math.max(this.activeMaxSize - size, 0);
37221 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37222 this.dd.resetConstraints();
37223 this.dd.setXConstraint(
37224 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37225 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37227 this.dd.setYConstraint(0, 0);
37229 this.dd.resetConstraints();
37230 this.dd.setXConstraint(0, 0);
37231 this.dd.setYConstraint(
37232 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37233 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37236 this.dragSpecs.startSize = size;
37237 this.dragSpecs.startPoint = [x, y];
37238 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37242 * @private Called after the drag operation by the DDProxy
37244 onEndProxyDrag : function(e){
37245 Roo.get(this.proxy).setDisplayed(false);
37246 var endPoint = Roo.lib.Event.getXY(e);
37248 this.overlay.hide();
37251 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37252 newSize = this.dragSpecs.startSize +
37253 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37254 endPoint[0] - this.dragSpecs.startPoint[0] :
37255 this.dragSpecs.startPoint[0] - endPoint[0]
37258 newSize = this.dragSpecs.startSize +
37259 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37260 endPoint[1] - this.dragSpecs.startPoint[1] :
37261 this.dragSpecs.startPoint[1] - endPoint[1]
37264 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37265 if(newSize != this.dragSpecs.startSize){
37266 if(this.fireEvent('beforeapply', this, newSize) !== false){
37267 this.adapter.setElementSize(this, newSize);
37268 this.fireEvent("moved", this, newSize);
37269 this.fireEvent("resize", this, newSize);
37275 * Get the adapter this SplitBar uses
37276 * @return The adapter object
37278 getAdapter : function(){
37279 return this.adapter;
37283 * Set the adapter this SplitBar uses
37284 * @param {Object} adapter A SplitBar adapter object
37286 setAdapter : function(adapter){
37287 this.adapter = adapter;
37288 this.adapter.init(this);
37292 * Gets the minimum size for the resizing element
37293 * @return {Number} The minimum size
37295 getMinimumSize : function(){
37296 return this.minSize;
37300 * Sets the minimum size for the resizing element
37301 * @param {Number} minSize The minimum size
37303 setMinimumSize : function(minSize){
37304 this.minSize = minSize;
37308 * Gets the maximum size for the resizing element
37309 * @return {Number} The maximum size
37311 getMaximumSize : function(){
37312 return this.maxSize;
37316 * Sets the maximum size for the resizing element
37317 * @param {Number} maxSize The maximum size
37319 setMaximumSize : function(maxSize){
37320 this.maxSize = maxSize;
37324 * Sets the initialize size for the resizing element
37325 * @param {Number} size The initial size
37327 setCurrentSize : function(size){
37328 var oldAnimate = this.animate;
37329 this.animate = false;
37330 this.adapter.setElementSize(this, size);
37331 this.animate = oldAnimate;
37335 * Destroy this splitbar.
37336 * @param {Boolean} removeEl True to remove the element
37338 destroy : function(removeEl){
37340 this.shim.remove();
37343 this.proxy.parentNode.removeChild(this.proxy);
37351 * @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.
37353 Roo.bootstrap.SplitBar.createProxy = function(dir){
37354 var proxy = new Roo.Element(document.createElement("div"));
37355 proxy.unselectable();
37356 var cls = 'roo-splitbar-proxy';
37357 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37358 document.body.appendChild(proxy.dom);
37363 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37364 * Default Adapter. It assumes the splitter and resizing element are not positioned
37365 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37367 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37370 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37371 // do nothing for now
37372 init : function(s){
37376 * Called before drag operations to get the current size of the resizing element.
37377 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37379 getElementSize : function(s){
37380 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37381 return s.resizingEl.getWidth();
37383 return s.resizingEl.getHeight();
37388 * Called after drag operations to set the size of the resizing element.
37389 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37390 * @param {Number} newSize The new size to set
37391 * @param {Function} onComplete A function to be invoked when resizing is complete
37393 setElementSize : function(s, newSize, onComplete){
37394 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37396 s.resizingEl.setWidth(newSize);
37398 onComplete(s, newSize);
37401 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37406 s.resizingEl.setHeight(newSize);
37408 onComplete(s, newSize);
37411 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37418 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37419 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37420 * Adapter that moves the splitter element to align with the resized sizing element.
37421 * Used with an absolute positioned SplitBar.
37422 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37423 * document.body, make sure you assign an id to the body element.
37425 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37426 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37427 this.container = Roo.get(container);
37430 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37431 init : function(s){
37432 this.basic.init(s);
37435 getElementSize : function(s){
37436 return this.basic.getElementSize(s);
37439 setElementSize : function(s, newSize, onComplete){
37440 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37443 moveSplitter : function(s){
37444 var yes = Roo.bootstrap.SplitBar;
37445 switch(s.placement){
37447 s.el.setX(s.resizingEl.getRight());
37450 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37453 s.el.setY(s.resizingEl.getBottom());
37456 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37463 * Orientation constant - Create a vertical SplitBar
37467 Roo.bootstrap.SplitBar.VERTICAL = 1;
37470 * Orientation constant - Create a horizontal SplitBar
37474 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37477 * Placement constant - The resizing element is to the left of the splitter element
37481 Roo.bootstrap.SplitBar.LEFT = 1;
37484 * Placement constant - The resizing element is to the right of the splitter element
37488 Roo.bootstrap.SplitBar.RIGHT = 2;
37491 * Placement constant - The resizing element is positioned above the splitter element
37495 Roo.bootstrap.SplitBar.TOP = 3;
37498 * Placement constant - The resizing element is positioned under splitter element
37502 Roo.bootstrap.SplitBar.BOTTOM = 4;
37503 Roo.namespace("Roo.bootstrap.layout");/*
37505 * Ext JS Library 1.1.1
37506 * Copyright(c) 2006-2007, Ext JS, LLC.
37508 * Originally Released Under LGPL - original licence link has changed is not relivant.
37511 * <script type="text/javascript">
37515 * @class Roo.bootstrap.layout.Manager
37516 * @extends Roo.bootstrap.Component
37517 * Base class for layout managers.
37519 Roo.bootstrap.layout.Manager = function(config)
37521 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37527 /** false to disable window resize monitoring @type Boolean */
37528 this.monitorWindowResize = true;
37533 * Fires when a layout is performed.
37534 * @param {Roo.LayoutManager} this
37538 * @event regionresized
37539 * Fires when the user resizes a region.
37540 * @param {Roo.LayoutRegion} region The resized region
37541 * @param {Number} newSize The new size (width for east/west, height for north/south)
37543 "regionresized" : true,
37545 * @event regioncollapsed
37546 * Fires when a region is collapsed.
37547 * @param {Roo.LayoutRegion} region The collapsed region
37549 "regioncollapsed" : true,
37551 * @event regionexpanded
37552 * Fires when a region is expanded.
37553 * @param {Roo.LayoutRegion} region The expanded region
37555 "regionexpanded" : true
37557 this.updating = false;
37560 this.el = Roo.get(config.el);
37566 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37571 monitorWindowResize : true,
37577 onRender : function(ct, position)
37580 this.el = Roo.get(ct);
37583 //this.fireEvent('render',this);
37587 initEvents: function()
37591 // ie scrollbar fix
37592 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37593 document.body.scroll = "no";
37594 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37595 this.el.position('relative');
37597 this.id = this.el.id;
37598 this.el.addClass("roo-layout-container");
37599 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37600 if(this.el.dom != document.body ) {
37601 this.el.on('resize', this.layout,this);
37602 this.el.on('show', this.layout,this);
37608 * Returns true if this layout is currently being updated
37609 * @return {Boolean}
37611 isUpdating : function(){
37612 return this.updating;
37616 * Suspend the LayoutManager from doing auto-layouts while
37617 * making multiple add or remove calls
37619 beginUpdate : function(){
37620 this.updating = true;
37624 * Restore auto-layouts and optionally disable the manager from performing a layout
37625 * @param {Boolean} noLayout true to disable a layout update
37627 endUpdate : function(noLayout){
37628 this.updating = false;
37634 layout: function(){
37638 onRegionResized : function(region, newSize){
37639 this.fireEvent("regionresized", region, newSize);
37643 onRegionCollapsed : function(region){
37644 this.fireEvent("regioncollapsed", region);
37647 onRegionExpanded : function(region){
37648 this.fireEvent("regionexpanded", region);
37652 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37653 * performs box-model adjustments.
37654 * @return {Object} The size as an object {width: (the width), height: (the height)}
37656 getViewSize : function()
37659 if(this.el.dom != document.body){
37660 size = this.el.getSize();
37662 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37664 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37665 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37670 * Returns the Element this layout is bound to.
37671 * @return {Roo.Element}
37673 getEl : function(){
37678 * Returns the specified region.
37679 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37680 * @return {Roo.LayoutRegion}
37682 getRegion : function(target){
37683 return this.regions[target.toLowerCase()];
37686 onWindowResize : function(){
37687 if(this.monitorWindowResize){
37694 * Ext JS Library 1.1.1
37695 * Copyright(c) 2006-2007, Ext JS, LLC.
37697 * Originally Released Under LGPL - original licence link has changed is not relivant.
37700 * <script type="text/javascript">
37703 * @class Roo.bootstrap.layout.Border
37704 * @extends Roo.bootstrap.layout.Manager
37705 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37706 * please see: examples/bootstrap/nested.html<br><br>
37708 <b>The container the layout is rendered into can be either the body element or any other element.
37709 If it is not the body element, the container needs to either be an absolute positioned element,
37710 or you will need to add "position:relative" to the css of the container. You will also need to specify
37711 the container size if it is not the body element.</b>
37714 * Create a new Border
37715 * @param {Object} config Configuration options
37717 Roo.bootstrap.layout.Border = function(config){
37718 config = config || {};
37719 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37723 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37724 if(config[region]){
37725 config[region].region = region;
37726 this.addRegion(config[region]);
37732 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37734 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37736 parent : false, // this might point to a 'nest' or a ???
37739 * Creates and adds a new region if it doesn't already exist.
37740 * @param {String} target The target region key (north, south, east, west or center).
37741 * @param {Object} config The regions config object
37742 * @return {BorderLayoutRegion} The new region
37744 addRegion : function(config)
37746 if(!this.regions[config.region]){
37747 var r = this.factory(config);
37748 this.bindRegion(r);
37750 return this.regions[config.region];
37754 bindRegion : function(r){
37755 this.regions[r.config.region] = r;
37757 r.on("visibilitychange", this.layout, this);
37758 r.on("paneladded", this.layout, this);
37759 r.on("panelremoved", this.layout, this);
37760 r.on("invalidated", this.layout, this);
37761 r.on("resized", this.onRegionResized, this);
37762 r.on("collapsed", this.onRegionCollapsed, this);
37763 r.on("expanded", this.onRegionExpanded, this);
37767 * Performs a layout update.
37769 layout : function()
37771 if(this.updating) {
37775 // render all the rebions if they have not been done alreayd?
37776 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37777 if(this.regions[region] && !this.regions[region].bodyEl){
37778 this.regions[region].onRender(this.el)
37782 var size = this.getViewSize();
37783 var w = size.width;
37784 var h = size.height;
37789 //var x = 0, y = 0;
37791 var rs = this.regions;
37792 var north = rs["north"];
37793 var south = rs["south"];
37794 var west = rs["west"];
37795 var east = rs["east"];
37796 var center = rs["center"];
37797 //if(this.hideOnLayout){ // not supported anymore
37798 //c.el.setStyle("display", "none");
37800 if(north && north.isVisible()){
37801 var b = north.getBox();
37802 var m = north.getMargins();
37803 b.width = w - (m.left+m.right);
37806 centerY = b.height + b.y + m.bottom;
37807 centerH -= centerY;
37808 north.updateBox(this.safeBox(b));
37810 if(south && south.isVisible()){
37811 var b = south.getBox();
37812 var m = south.getMargins();
37813 b.width = w - (m.left+m.right);
37815 var totalHeight = (b.height + m.top + m.bottom);
37816 b.y = h - totalHeight + m.top;
37817 centerH -= totalHeight;
37818 south.updateBox(this.safeBox(b));
37820 if(west && west.isVisible()){
37821 var b = west.getBox();
37822 var m = west.getMargins();
37823 b.height = centerH - (m.top+m.bottom);
37825 b.y = centerY + m.top;
37826 var totalWidth = (b.width + m.left + m.right);
37827 centerX += totalWidth;
37828 centerW -= totalWidth;
37829 west.updateBox(this.safeBox(b));
37831 if(east && east.isVisible()){
37832 var b = east.getBox();
37833 var m = east.getMargins();
37834 b.height = centerH - (m.top+m.bottom);
37835 var totalWidth = (b.width + m.left + m.right);
37836 b.x = w - totalWidth + m.left;
37837 b.y = centerY + m.top;
37838 centerW -= totalWidth;
37839 east.updateBox(this.safeBox(b));
37842 var m = center.getMargins();
37844 x: centerX + m.left,
37845 y: centerY + m.top,
37846 width: centerW - (m.left+m.right),
37847 height: centerH - (m.top+m.bottom)
37849 //if(this.hideOnLayout){
37850 //center.el.setStyle("display", "block");
37852 center.updateBox(this.safeBox(centerBox));
37855 this.fireEvent("layout", this);
37859 safeBox : function(box){
37860 box.width = Math.max(0, box.width);
37861 box.height = Math.max(0, box.height);
37866 * Adds a ContentPanel (or subclass) to this layout.
37867 * @param {String} target The target region key (north, south, east, west or center).
37868 * @param {Roo.ContentPanel} panel The panel to add
37869 * @return {Roo.ContentPanel} The added panel
37871 add : function(target, panel){
37873 target = target.toLowerCase();
37874 return this.regions[target].add(panel);
37878 * Remove a ContentPanel (or subclass) to this layout.
37879 * @param {String} target The target region key (north, south, east, west or center).
37880 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37881 * @return {Roo.ContentPanel} The removed panel
37883 remove : function(target, panel){
37884 target = target.toLowerCase();
37885 return this.regions[target].remove(panel);
37889 * Searches all regions for a panel with the specified id
37890 * @param {String} panelId
37891 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37893 findPanel : function(panelId){
37894 var rs = this.regions;
37895 for(var target in rs){
37896 if(typeof rs[target] != "function"){
37897 var p = rs[target].getPanel(panelId);
37907 * Searches all regions for a panel with the specified id and activates (shows) it.
37908 * @param {String/ContentPanel} panelId The panels id or the panel itself
37909 * @return {Roo.ContentPanel} The shown panel or null
37911 showPanel : function(panelId) {
37912 var rs = this.regions;
37913 for(var target in rs){
37914 var r = rs[target];
37915 if(typeof r != "function"){
37916 if(r.hasPanel(panelId)){
37917 return r.showPanel(panelId);
37925 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37926 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37929 restoreState : function(provider){
37931 provider = Roo.state.Manager;
37933 var sm = new Roo.LayoutStateManager();
37934 sm.init(this, provider);
37940 * Adds a xtype elements to the layout.
37944 xtype : 'ContentPanel',
37951 xtype : 'NestedLayoutPanel',
37957 items : [ ... list of content panels or nested layout panels.. ]
37961 * @param {Object} cfg Xtype definition of item to add.
37963 addxtype : function(cfg)
37965 // basically accepts a pannel...
37966 // can accept a layout region..!?!?
37967 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37970 // theory? children can only be panels??
37972 //if (!cfg.xtype.match(/Panel$/)) {
37977 if (typeof(cfg.region) == 'undefined') {
37978 Roo.log("Failed to add Panel, region was not set");
37982 var region = cfg.region;
37988 xitems = cfg.items;
37993 if ( region == 'center') {
37994 Roo.log("Center: " + cfg.title);
38000 case 'Content': // ContentPanel (el, cfg)
38001 case 'Scroll': // ContentPanel (el, cfg)
38003 cfg.autoCreate = cfg.autoCreate || true;
38004 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38006 // var el = this.el.createChild();
38007 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38010 this.add(region, ret);
38014 case 'TreePanel': // our new panel!
38015 cfg.el = this.el.createChild();
38016 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38017 this.add(region, ret);
38022 // create a new Layout (which is a Border Layout...
38024 var clayout = cfg.layout;
38025 clayout.el = this.el.createChild();
38026 clayout.items = clayout.items || [];
38030 // replace this exitems with the clayout ones..
38031 xitems = clayout.items;
38033 // force background off if it's in center...
38034 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38035 cfg.background = false;
38037 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38040 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38041 //console.log('adding nested layout panel ' + cfg.toSource());
38042 this.add(region, ret);
38043 nb = {}; /// find first...
38048 // needs grid and region
38050 //var el = this.getRegion(region).el.createChild();
38052 *var el = this.el.createChild();
38053 // create the grid first...
38054 cfg.grid.container = el;
38055 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38058 if (region == 'center' && this.active ) {
38059 cfg.background = false;
38062 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38064 this.add(region, ret);
38066 if (cfg.background) {
38067 // render grid on panel activation (if panel background)
38068 ret.on('activate', function(gp) {
38069 if (!gp.grid.rendered) {
38070 // gp.grid.render(el);
38074 // cfg.grid.render(el);
38080 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38081 // it was the old xcomponent building that caused this before.
38082 // espeically if border is the top element in the tree.
38092 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38094 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38095 this.add(region, ret);
38099 throw "Can not add '" + cfg.xtype + "' to Border";
38105 this.beginUpdate();
38109 Roo.each(xitems, function(i) {
38110 region = nb && i.region ? i.region : false;
38112 var add = ret.addxtype(i);
38115 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38116 if (!i.background) {
38117 abn[region] = nb[region] ;
38124 // make the last non-background panel active..
38125 //if (nb) { Roo.log(abn); }
38128 for(var r in abn) {
38129 region = this.getRegion(r);
38131 // tried using nb[r], but it does not work..
38133 region.showPanel(abn[r]);
38144 factory : function(cfg)
38147 var validRegions = Roo.bootstrap.layout.Border.regions;
38149 var target = cfg.region;
38152 var r = Roo.bootstrap.layout;
38156 return new r.North(cfg);
38158 return new r.South(cfg);
38160 return new r.East(cfg);
38162 return new r.West(cfg);
38164 return new r.Center(cfg);
38166 throw 'Layout region "'+target+'" not supported.';
38173 * Ext JS Library 1.1.1
38174 * Copyright(c) 2006-2007, Ext JS, LLC.
38176 * Originally Released Under LGPL - original licence link has changed is not relivant.
38179 * <script type="text/javascript">
38183 * @class Roo.bootstrap.layout.Basic
38184 * @extends Roo.util.Observable
38185 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38186 * and does not have a titlebar, tabs or any other features. All it does is size and position
38187 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38188 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38189 * @cfg {string} region the region that it inhabits..
38190 * @cfg {bool} skipConfig skip config?
38194 Roo.bootstrap.layout.Basic = function(config){
38196 this.mgr = config.mgr;
38198 this.position = config.region;
38200 var skipConfig = config.skipConfig;
38204 * @scope Roo.BasicLayoutRegion
38208 * @event beforeremove
38209 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38210 * @param {Roo.LayoutRegion} this
38211 * @param {Roo.ContentPanel} panel The panel
38212 * @param {Object} e The cancel event object
38214 "beforeremove" : true,
38216 * @event invalidated
38217 * Fires when the layout for this region is changed.
38218 * @param {Roo.LayoutRegion} this
38220 "invalidated" : true,
38222 * @event visibilitychange
38223 * Fires when this region is shown or hidden
38224 * @param {Roo.LayoutRegion} this
38225 * @param {Boolean} visibility true or false
38227 "visibilitychange" : true,
38229 * @event paneladded
38230 * Fires when a panel is added.
38231 * @param {Roo.LayoutRegion} this
38232 * @param {Roo.ContentPanel} panel The panel
38234 "paneladded" : true,
38236 * @event panelremoved
38237 * Fires when a panel is removed.
38238 * @param {Roo.LayoutRegion} this
38239 * @param {Roo.ContentPanel} panel The panel
38241 "panelremoved" : true,
38243 * @event beforecollapse
38244 * Fires when this region before collapse.
38245 * @param {Roo.LayoutRegion} this
38247 "beforecollapse" : true,
38250 * Fires when this region is collapsed.
38251 * @param {Roo.LayoutRegion} this
38253 "collapsed" : true,
38256 * Fires when this region is expanded.
38257 * @param {Roo.LayoutRegion} this
38262 * Fires when this region is slid into view.
38263 * @param {Roo.LayoutRegion} this
38265 "slideshow" : true,
38268 * Fires when this region slides out of view.
38269 * @param {Roo.LayoutRegion} this
38271 "slidehide" : true,
38273 * @event panelactivated
38274 * Fires when a panel is activated.
38275 * @param {Roo.LayoutRegion} this
38276 * @param {Roo.ContentPanel} panel The activated panel
38278 "panelactivated" : true,
38281 * Fires when the user resizes this region.
38282 * @param {Roo.LayoutRegion} this
38283 * @param {Number} newSize The new size (width for east/west, height for north/south)
38287 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38288 this.panels = new Roo.util.MixedCollection();
38289 this.panels.getKey = this.getPanelId.createDelegate(this);
38291 this.activePanel = null;
38292 // ensure listeners are added...
38294 if (config.listeners || config.events) {
38295 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38296 listeners : config.listeners || {},
38297 events : config.events || {}
38301 if(skipConfig !== true){
38302 this.applyConfig(config);
38306 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38308 getPanelId : function(p){
38312 applyConfig : function(config){
38313 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38314 this.config = config;
38319 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38320 * the width, for horizontal (north, south) the height.
38321 * @param {Number} newSize The new width or height
38323 resizeTo : function(newSize){
38324 var el = this.el ? this.el :
38325 (this.activePanel ? this.activePanel.getEl() : null);
38327 switch(this.position){
38330 el.setWidth(newSize);
38331 this.fireEvent("resized", this, newSize);
38335 el.setHeight(newSize);
38336 this.fireEvent("resized", this, newSize);
38342 getBox : function(){
38343 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38346 getMargins : function(){
38347 return this.margins;
38350 updateBox : function(box){
38352 var el = this.activePanel.getEl();
38353 el.dom.style.left = box.x + "px";
38354 el.dom.style.top = box.y + "px";
38355 this.activePanel.setSize(box.width, box.height);
38359 * Returns the container element for this region.
38360 * @return {Roo.Element}
38362 getEl : function(){
38363 return this.activePanel;
38367 * Returns true if this region is currently visible.
38368 * @return {Boolean}
38370 isVisible : function(){
38371 return this.activePanel ? true : false;
38374 setActivePanel : function(panel){
38375 panel = this.getPanel(panel);
38376 if(this.activePanel && this.activePanel != panel){
38377 this.activePanel.setActiveState(false);
38378 this.activePanel.getEl().setLeftTop(-10000,-10000);
38380 this.activePanel = panel;
38381 panel.setActiveState(true);
38383 panel.setSize(this.box.width, this.box.height);
38385 this.fireEvent("panelactivated", this, panel);
38386 this.fireEvent("invalidated");
38390 * Show the specified panel.
38391 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38392 * @return {Roo.ContentPanel} The shown panel or null
38394 showPanel : function(panel){
38395 panel = this.getPanel(panel);
38397 this.setActivePanel(panel);
38403 * Get the active panel for this region.
38404 * @return {Roo.ContentPanel} The active panel or null
38406 getActivePanel : function(){
38407 return this.activePanel;
38411 * Add the passed ContentPanel(s)
38412 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38413 * @return {Roo.ContentPanel} The panel added (if only one was added)
38415 add : function(panel){
38416 if(arguments.length > 1){
38417 for(var i = 0, len = arguments.length; i < len; i++) {
38418 this.add(arguments[i]);
38422 if(this.hasPanel(panel)){
38423 this.showPanel(panel);
38426 var el = panel.getEl();
38427 if(el.dom.parentNode != this.mgr.el.dom){
38428 this.mgr.el.dom.appendChild(el.dom);
38430 if(panel.setRegion){
38431 panel.setRegion(this);
38433 this.panels.add(panel);
38434 el.setStyle("position", "absolute");
38435 if(!panel.background){
38436 this.setActivePanel(panel);
38437 if(this.config.initialSize && this.panels.getCount()==1){
38438 this.resizeTo(this.config.initialSize);
38441 this.fireEvent("paneladded", this, panel);
38446 * Returns true if the panel is in this region.
38447 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38448 * @return {Boolean}
38450 hasPanel : function(panel){
38451 if(typeof panel == "object"){ // must be panel obj
38452 panel = panel.getId();
38454 return this.getPanel(panel) ? true : false;
38458 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38459 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38460 * @param {Boolean} preservePanel Overrides the config preservePanel option
38461 * @return {Roo.ContentPanel} The panel that was removed
38463 remove : function(panel, preservePanel){
38464 panel = this.getPanel(panel);
38469 this.fireEvent("beforeremove", this, panel, e);
38470 if(e.cancel === true){
38473 var panelId = panel.getId();
38474 this.panels.removeKey(panelId);
38479 * Returns the panel specified or null if it's not in this region.
38480 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38481 * @return {Roo.ContentPanel}
38483 getPanel : function(id){
38484 if(typeof id == "object"){ // must be panel obj
38487 return this.panels.get(id);
38491 * Returns this regions position (north/south/east/west/center).
38494 getPosition: function(){
38495 return this.position;
38499 * Ext JS Library 1.1.1
38500 * Copyright(c) 2006-2007, Ext JS, LLC.
38502 * Originally Released Under LGPL - original licence link has changed is not relivant.
38505 * <script type="text/javascript">
38509 * @class Roo.bootstrap.layout.Region
38510 * @extends Roo.bootstrap.layout.Basic
38511 * This class represents a region in a layout manager.
38513 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38514 * @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})
38515 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38516 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38517 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38518 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38519 * @cfg {String} title The title for the region (overrides panel titles)
38520 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38521 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38522 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38523 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38524 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38525 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38526 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38527 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38528 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38529 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38531 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38532 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38533 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38534 * @cfg {Number} width For East/West panels
38535 * @cfg {Number} height For North/South panels
38536 * @cfg {Boolean} split To show the splitter
38537 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38539 * @cfg {string} cls Extra CSS classes to add to region
38541 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38542 * @cfg {string} region the region that it inhabits..
38545 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38546 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38548 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38549 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38550 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38552 Roo.bootstrap.layout.Region = function(config)
38554 this.applyConfig(config);
38556 var mgr = config.mgr;
38557 var pos = config.region;
38558 config.skipConfig = true;
38559 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38562 this.onRender(mgr.el);
38565 this.visible = true;
38566 this.collapsed = false;
38567 this.unrendered_panels = [];
38570 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38572 position: '', // set by wrapper (eg. north/south etc..)
38573 unrendered_panels : null, // unrendered panels.
38575 tabPosition : false,
38577 mgr: false, // points to 'Border'
38580 createBody : function(){
38581 /** This region's body element
38582 * @type Roo.Element */
38583 this.bodyEl = this.el.createChild({
38585 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38589 onRender: function(ctr, pos)
38591 var dh = Roo.DomHelper;
38592 /** This region's container element
38593 * @type Roo.Element */
38594 this.el = dh.append(ctr.dom, {
38596 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38598 /** This region's title element
38599 * @type Roo.Element */
38601 this.titleEl = dh.append(this.el.dom, {
38603 unselectable: "on",
38604 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38606 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38607 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38611 this.titleEl.enableDisplayMode();
38612 /** This region's title text element
38613 * @type HTMLElement */
38614 this.titleTextEl = this.titleEl.dom.firstChild;
38615 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38617 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38618 this.closeBtn.enableDisplayMode();
38619 this.closeBtn.on("click", this.closeClicked, this);
38620 this.closeBtn.hide();
38622 this.createBody(this.config);
38623 if(this.config.hideWhenEmpty){
38625 this.on("paneladded", this.validateVisibility, this);
38626 this.on("panelremoved", this.validateVisibility, this);
38628 if(this.autoScroll){
38629 this.bodyEl.setStyle("overflow", "auto");
38631 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38633 //if(c.titlebar !== false){
38634 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38635 this.titleEl.hide();
38637 this.titleEl.show();
38638 if(this.config.title){
38639 this.titleTextEl.innerHTML = this.config.title;
38643 if(this.config.collapsed){
38644 this.collapse(true);
38646 if(this.config.hidden){
38650 if (this.unrendered_panels && this.unrendered_panels.length) {
38651 for (var i =0;i< this.unrendered_panels.length; i++) {
38652 this.add(this.unrendered_panels[i]);
38654 this.unrendered_panels = null;
38660 applyConfig : function(c)
38663 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38664 var dh = Roo.DomHelper;
38665 if(c.titlebar !== false){
38666 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38667 this.collapseBtn.on("click", this.collapse, this);
38668 this.collapseBtn.enableDisplayMode();
38670 if(c.showPin === true || this.showPin){
38671 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38672 this.stickBtn.enableDisplayMode();
38673 this.stickBtn.on("click", this.expand, this);
38674 this.stickBtn.hide();
38679 /** This region's collapsed element
38680 * @type Roo.Element */
38683 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38684 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38687 if(c.floatable !== false){
38688 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38689 this.collapsedEl.on("click", this.collapseClick, this);
38692 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38693 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38694 id: "message", unselectable: "on", style:{"float":"left"}});
38695 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38697 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38698 this.expandBtn.on("click", this.expand, this);
38702 if(this.collapseBtn){
38703 this.collapseBtn.setVisible(c.collapsible == true);
38706 this.cmargins = c.cmargins || this.cmargins ||
38707 (this.position == "west" || this.position == "east" ?
38708 {top: 0, left: 2, right:2, bottom: 0} :
38709 {top: 2, left: 0, right:0, bottom: 2});
38711 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38714 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38716 this.autoScroll = c.autoScroll || false;
38721 this.duration = c.duration || .30;
38722 this.slideDuration = c.slideDuration || .45;
38727 * Returns true if this region is currently visible.
38728 * @return {Boolean}
38730 isVisible : function(){
38731 return this.visible;
38735 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38736 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38738 //setCollapsedTitle : function(title){
38739 // title = title || " ";
38740 // if(this.collapsedTitleTextEl){
38741 // this.collapsedTitleTextEl.innerHTML = title;
38745 getBox : function(){
38747 // if(!this.collapsed){
38748 b = this.el.getBox(false, true);
38750 // b = this.collapsedEl.getBox(false, true);
38755 getMargins : function(){
38756 return this.margins;
38757 //return this.collapsed ? this.cmargins : this.margins;
38760 highlight : function(){
38761 this.el.addClass("x-layout-panel-dragover");
38764 unhighlight : function(){
38765 this.el.removeClass("x-layout-panel-dragover");
38768 updateBox : function(box)
38770 if (!this.bodyEl) {
38771 return; // not rendered yet..
38775 if(!this.collapsed){
38776 this.el.dom.style.left = box.x + "px";
38777 this.el.dom.style.top = box.y + "px";
38778 this.updateBody(box.width, box.height);
38780 this.collapsedEl.dom.style.left = box.x + "px";
38781 this.collapsedEl.dom.style.top = box.y + "px";
38782 this.collapsedEl.setSize(box.width, box.height);
38785 this.tabs.autoSizeTabs();
38789 updateBody : function(w, h)
38792 this.el.setWidth(w);
38793 w -= this.el.getBorderWidth("rl");
38794 if(this.config.adjustments){
38795 w += this.config.adjustments[0];
38798 if(h !== null && h > 0){
38799 this.el.setHeight(h);
38800 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38801 h -= this.el.getBorderWidth("tb");
38802 if(this.config.adjustments){
38803 h += this.config.adjustments[1];
38805 this.bodyEl.setHeight(h);
38807 h = this.tabs.syncHeight(h);
38810 if(this.panelSize){
38811 w = w !== null ? w : this.panelSize.width;
38812 h = h !== null ? h : this.panelSize.height;
38814 if(this.activePanel){
38815 var el = this.activePanel.getEl();
38816 w = w !== null ? w : el.getWidth();
38817 h = h !== null ? h : el.getHeight();
38818 this.panelSize = {width: w, height: h};
38819 this.activePanel.setSize(w, h);
38821 if(Roo.isIE && this.tabs){
38822 this.tabs.el.repaint();
38827 * Returns the container element for this region.
38828 * @return {Roo.Element}
38830 getEl : function(){
38835 * Hides this region.
38838 //if(!this.collapsed){
38839 this.el.dom.style.left = "-2000px";
38842 // this.collapsedEl.dom.style.left = "-2000px";
38843 // this.collapsedEl.hide();
38845 this.visible = false;
38846 this.fireEvent("visibilitychange", this, false);
38850 * Shows this region if it was previously hidden.
38853 //if(!this.collapsed){
38856 // this.collapsedEl.show();
38858 this.visible = true;
38859 this.fireEvent("visibilitychange", this, true);
38862 closeClicked : function(){
38863 if(this.activePanel){
38864 this.remove(this.activePanel);
38868 collapseClick : function(e){
38870 e.stopPropagation();
38873 e.stopPropagation();
38879 * Collapses this region.
38880 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38883 collapse : function(skipAnim, skipCheck = false){
38884 if(this.collapsed) {
38888 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38890 this.collapsed = true;
38892 this.split.el.hide();
38894 if(this.config.animate && skipAnim !== true){
38895 this.fireEvent("invalidated", this);
38896 this.animateCollapse();
38898 this.el.setLocation(-20000,-20000);
38900 this.collapsedEl.show();
38901 this.fireEvent("collapsed", this);
38902 this.fireEvent("invalidated", this);
38908 animateCollapse : function(){
38913 * Expands this region if it was previously collapsed.
38914 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38915 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38918 expand : function(e, skipAnim){
38920 e.stopPropagation();
38922 if(!this.collapsed || this.el.hasActiveFx()) {
38926 this.afterSlideIn();
38929 this.collapsed = false;
38930 if(this.config.animate && skipAnim !== true){
38931 this.animateExpand();
38935 this.split.el.show();
38937 this.collapsedEl.setLocation(-2000,-2000);
38938 this.collapsedEl.hide();
38939 this.fireEvent("invalidated", this);
38940 this.fireEvent("expanded", this);
38944 animateExpand : function(){
38948 initTabs : function()
38950 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38952 var ts = new Roo.bootstrap.panel.Tabs({
38953 el: this.bodyEl.dom,
38955 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38956 disableTooltips: this.config.disableTabTips,
38957 toolbar : this.config.toolbar
38960 if(this.config.hideTabs){
38961 ts.stripWrap.setDisplayed(false);
38964 ts.resizeTabs = this.config.resizeTabs === true;
38965 ts.minTabWidth = this.config.minTabWidth || 40;
38966 ts.maxTabWidth = this.config.maxTabWidth || 250;
38967 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38968 ts.monitorResize = false;
38969 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38970 ts.bodyEl.addClass('roo-layout-tabs-body');
38971 this.panels.each(this.initPanelAsTab, this);
38974 initPanelAsTab : function(panel){
38975 var ti = this.tabs.addTab(
38979 this.config.closeOnTab && panel.isClosable(),
38982 if(panel.tabTip !== undefined){
38983 ti.setTooltip(panel.tabTip);
38985 ti.on("activate", function(){
38986 this.setActivePanel(panel);
38989 if(this.config.closeOnTab){
38990 ti.on("beforeclose", function(t, e){
38992 this.remove(panel);
38996 panel.tabItem = ti;
39001 updatePanelTitle : function(panel, title)
39003 if(this.activePanel == panel){
39004 this.updateTitle(title);
39007 var ti = this.tabs.getTab(panel.getEl().id);
39009 if(panel.tabTip !== undefined){
39010 ti.setTooltip(panel.tabTip);
39015 updateTitle : function(title){
39016 if(this.titleTextEl && !this.config.title){
39017 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39021 setActivePanel : function(panel)
39023 panel = this.getPanel(panel);
39024 if(this.activePanel && this.activePanel != panel){
39025 if(this.activePanel.setActiveState(false) === false){
39029 this.activePanel = panel;
39030 panel.setActiveState(true);
39031 if(this.panelSize){
39032 panel.setSize(this.panelSize.width, this.panelSize.height);
39035 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39037 this.updateTitle(panel.getTitle());
39039 this.fireEvent("invalidated", this);
39041 this.fireEvent("panelactivated", this, panel);
39045 * Shows the specified panel.
39046 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39047 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39049 showPanel : function(panel)
39051 panel = this.getPanel(panel);
39054 var tab = this.tabs.getTab(panel.getEl().id);
39055 if(tab.isHidden()){
39056 this.tabs.unhideTab(tab.id);
39060 this.setActivePanel(panel);
39067 * Get the active panel for this region.
39068 * @return {Roo.ContentPanel} The active panel or null
39070 getActivePanel : function(){
39071 return this.activePanel;
39074 validateVisibility : function(){
39075 if(this.panels.getCount() < 1){
39076 this.updateTitle(" ");
39077 this.closeBtn.hide();
39080 if(!this.isVisible()){
39087 * Adds the passed ContentPanel(s) to this region.
39088 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39089 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39091 add : function(panel)
39093 if(arguments.length > 1){
39094 for(var i = 0, len = arguments.length; i < len; i++) {
39095 this.add(arguments[i]);
39100 // if we have not been rendered yet, then we can not really do much of this..
39101 if (!this.bodyEl) {
39102 this.unrendered_panels.push(panel);
39109 if(this.hasPanel(panel)){
39110 this.showPanel(panel);
39113 panel.setRegion(this);
39114 this.panels.add(panel);
39115 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39116 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39117 // and hide them... ???
39118 this.bodyEl.dom.appendChild(panel.getEl().dom);
39119 if(panel.background !== true){
39120 this.setActivePanel(panel);
39122 this.fireEvent("paneladded", this, panel);
39129 this.initPanelAsTab(panel);
39133 if(panel.background !== true){
39134 this.tabs.activate(panel.getEl().id);
39136 this.fireEvent("paneladded", this, panel);
39141 * Hides the tab for the specified panel.
39142 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39144 hidePanel : function(panel){
39145 if(this.tabs && (panel = this.getPanel(panel))){
39146 this.tabs.hideTab(panel.getEl().id);
39151 * Unhides the tab for a previously hidden panel.
39152 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39154 unhidePanel : function(panel){
39155 if(this.tabs && (panel = this.getPanel(panel))){
39156 this.tabs.unhideTab(panel.getEl().id);
39160 clearPanels : function(){
39161 while(this.panels.getCount() > 0){
39162 this.remove(this.panels.first());
39167 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39168 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39169 * @param {Boolean} preservePanel Overrides the config preservePanel option
39170 * @return {Roo.ContentPanel} The panel that was removed
39172 remove : function(panel, preservePanel)
39174 panel = this.getPanel(panel);
39179 this.fireEvent("beforeremove", this, panel, e);
39180 if(e.cancel === true){
39183 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39184 var panelId = panel.getId();
39185 this.panels.removeKey(panelId);
39187 document.body.appendChild(panel.getEl().dom);
39190 this.tabs.removeTab(panel.getEl().id);
39191 }else if (!preservePanel){
39192 this.bodyEl.dom.removeChild(panel.getEl().dom);
39194 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39195 var p = this.panels.first();
39196 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39197 tempEl.appendChild(p.getEl().dom);
39198 this.bodyEl.update("");
39199 this.bodyEl.dom.appendChild(p.getEl().dom);
39201 this.updateTitle(p.getTitle());
39203 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39204 this.setActivePanel(p);
39206 panel.setRegion(null);
39207 if(this.activePanel == panel){
39208 this.activePanel = null;
39210 if(this.config.autoDestroy !== false && preservePanel !== true){
39211 try{panel.destroy();}catch(e){}
39213 this.fireEvent("panelremoved", this, panel);
39218 * Returns the TabPanel component used by this region
39219 * @return {Roo.TabPanel}
39221 getTabs : function(){
39225 createTool : function(parentEl, className){
39226 var btn = Roo.DomHelper.append(parentEl, {
39228 cls: "x-layout-tools-button",
39231 cls: "roo-layout-tools-button-inner " + className,
39235 btn.addClassOnOver("roo-layout-tools-button-over");
39240 * Ext JS Library 1.1.1
39241 * Copyright(c) 2006-2007, Ext JS, LLC.
39243 * Originally Released Under LGPL - original licence link has changed is not relivant.
39246 * <script type="text/javascript">
39252 * @class Roo.SplitLayoutRegion
39253 * @extends Roo.LayoutRegion
39254 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39256 Roo.bootstrap.layout.Split = function(config){
39257 this.cursor = config.cursor;
39258 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39261 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39263 splitTip : "Drag to resize.",
39264 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39265 useSplitTips : false,
39267 applyConfig : function(config){
39268 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39271 onRender : function(ctr,pos) {
39273 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39274 if(!this.config.split){
39279 var splitEl = Roo.DomHelper.append(ctr.dom, {
39281 id: this.el.id + "-split",
39282 cls: "roo-layout-split roo-layout-split-"+this.position,
39285 /** The SplitBar for this region
39286 * @type Roo.SplitBar */
39287 // does not exist yet...
39288 Roo.log([this.position, this.orientation]);
39290 this.split = new Roo.bootstrap.SplitBar({
39291 dragElement : splitEl,
39292 resizingElement: this.el,
39293 orientation : this.orientation
39296 this.split.on("moved", this.onSplitMove, this);
39297 this.split.useShim = this.config.useShim === true;
39298 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39299 if(this.useSplitTips){
39300 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39302 //if(config.collapsible){
39303 // this.split.el.on("dblclick", this.collapse, this);
39306 if(typeof this.config.minSize != "undefined"){
39307 this.split.minSize = this.config.minSize;
39309 if(typeof this.config.maxSize != "undefined"){
39310 this.split.maxSize = this.config.maxSize;
39312 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39313 this.hideSplitter();
39318 getHMaxSize : function(){
39319 var cmax = this.config.maxSize || 10000;
39320 var center = this.mgr.getRegion("center");
39321 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39324 getVMaxSize : function(){
39325 var cmax = this.config.maxSize || 10000;
39326 var center = this.mgr.getRegion("center");
39327 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39330 onSplitMove : function(split, newSize){
39331 this.fireEvent("resized", this, newSize);
39335 * Returns the {@link Roo.SplitBar} for this region.
39336 * @return {Roo.SplitBar}
39338 getSplitBar : function(){
39343 this.hideSplitter();
39344 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39347 hideSplitter : function(){
39349 this.split.el.setLocation(-2000,-2000);
39350 this.split.el.hide();
39356 this.split.el.show();
39358 Roo.bootstrap.layout.Split.superclass.show.call(this);
39361 beforeSlide: function(){
39362 if(Roo.isGecko){// firefox overflow auto bug workaround
39363 this.bodyEl.clip();
39365 this.tabs.bodyEl.clip();
39367 if(this.activePanel){
39368 this.activePanel.getEl().clip();
39370 if(this.activePanel.beforeSlide){
39371 this.activePanel.beforeSlide();
39377 afterSlide : function(){
39378 if(Roo.isGecko){// firefox overflow auto bug workaround
39379 this.bodyEl.unclip();
39381 this.tabs.bodyEl.unclip();
39383 if(this.activePanel){
39384 this.activePanel.getEl().unclip();
39385 if(this.activePanel.afterSlide){
39386 this.activePanel.afterSlide();
39392 initAutoHide : function(){
39393 if(this.autoHide !== false){
39394 if(!this.autoHideHd){
39395 var st = new Roo.util.DelayedTask(this.slideIn, this);
39396 this.autoHideHd = {
39397 "mouseout": function(e){
39398 if(!e.within(this.el, true)){
39402 "mouseover" : function(e){
39408 this.el.on(this.autoHideHd);
39412 clearAutoHide : function(){
39413 if(this.autoHide !== false){
39414 this.el.un("mouseout", this.autoHideHd.mouseout);
39415 this.el.un("mouseover", this.autoHideHd.mouseover);
39419 clearMonitor : function(){
39420 Roo.get(document).un("click", this.slideInIf, this);
39423 // these names are backwards but not changed for compat
39424 slideOut : function(){
39425 if(this.isSlid || this.el.hasActiveFx()){
39428 this.isSlid = true;
39429 if(this.collapseBtn){
39430 this.collapseBtn.hide();
39432 this.closeBtnState = this.closeBtn.getStyle('display');
39433 this.closeBtn.hide();
39435 this.stickBtn.show();
39438 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39439 this.beforeSlide();
39440 this.el.setStyle("z-index", 10001);
39441 this.el.slideIn(this.getSlideAnchor(), {
39442 callback: function(){
39444 this.initAutoHide();
39445 Roo.get(document).on("click", this.slideInIf, this);
39446 this.fireEvent("slideshow", this);
39453 afterSlideIn : function(){
39454 this.clearAutoHide();
39455 this.isSlid = false;
39456 this.clearMonitor();
39457 this.el.setStyle("z-index", "");
39458 if(this.collapseBtn){
39459 this.collapseBtn.show();
39461 this.closeBtn.setStyle('display', this.closeBtnState);
39463 this.stickBtn.hide();
39465 this.fireEvent("slidehide", this);
39468 slideIn : function(cb){
39469 if(!this.isSlid || this.el.hasActiveFx()){
39473 this.isSlid = false;
39474 this.beforeSlide();
39475 this.el.slideOut(this.getSlideAnchor(), {
39476 callback: function(){
39477 this.el.setLeftTop(-10000, -10000);
39479 this.afterSlideIn();
39487 slideInIf : function(e){
39488 if(!e.within(this.el)){
39493 animateCollapse : function(){
39494 this.beforeSlide();
39495 this.el.setStyle("z-index", 20000);
39496 var anchor = this.getSlideAnchor();
39497 this.el.slideOut(anchor, {
39498 callback : function(){
39499 this.el.setStyle("z-index", "");
39500 this.collapsedEl.slideIn(anchor, {duration:.3});
39502 this.el.setLocation(-10000,-10000);
39504 this.fireEvent("collapsed", this);
39511 animateExpand : function(){
39512 this.beforeSlide();
39513 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39514 this.el.setStyle("z-index", 20000);
39515 this.collapsedEl.hide({
39518 this.el.slideIn(this.getSlideAnchor(), {
39519 callback : function(){
39520 this.el.setStyle("z-index", "");
39523 this.split.el.show();
39525 this.fireEvent("invalidated", this);
39526 this.fireEvent("expanded", this);
39554 getAnchor : function(){
39555 return this.anchors[this.position];
39558 getCollapseAnchor : function(){
39559 return this.canchors[this.position];
39562 getSlideAnchor : function(){
39563 return this.sanchors[this.position];
39566 getAlignAdj : function(){
39567 var cm = this.cmargins;
39568 switch(this.position){
39584 getExpandAdj : function(){
39585 var c = this.collapsedEl, cm = this.cmargins;
39586 switch(this.position){
39588 return [-(cm.right+c.getWidth()+cm.left), 0];
39591 return [cm.right+c.getWidth()+cm.left, 0];
39594 return [0, -(cm.top+cm.bottom+c.getHeight())];
39597 return [0, cm.top+cm.bottom+c.getHeight()];
39603 * Ext JS Library 1.1.1
39604 * Copyright(c) 2006-2007, Ext JS, LLC.
39606 * Originally Released Under LGPL - original licence link has changed is not relivant.
39609 * <script type="text/javascript">
39612 * These classes are private internal classes
39614 Roo.bootstrap.layout.Center = function(config){
39615 config.region = "center";
39616 Roo.bootstrap.layout.Region.call(this, config);
39617 this.visible = true;
39618 this.minWidth = config.minWidth || 20;
39619 this.minHeight = config.minHeight || 20;
39622 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39624 // center panel can't be hidden
39628 // center panel can't be hidden
39631 getMinWidth: function(){
39632 return this.minWidth;
39635 getMinHeight: function(){
39636 return this.minHeight;
39650 Roo.bootstrap.layout.North = function(config)
39652 config.region = 'north';
39653 config.cursor = 'n-resize';
39655 Roo.bootstrap.layout.Split.call(this, config);
39659 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39660 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39661 this.split.el.addClass("roo-layout-split-v");
39663 //var size = config.initialSize || config.height;
39664 //if(this.el && typeof size != "undefined"){
39665 // this.el.setHeight(size);
39668 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39670 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39673 onRender : function(ctr, pos)
39675 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39676 var size = this.config.initialSize || this.config.height;
39677 if(this.el && typeof size != "undefined"){
39678 this.el.setHeight(size);
39683 getBox : function(){
39684 if(this.collapsed){
39685 return this.collapsedEl.getBox();
39687 var box = this.el.getBox();
39689 box.height += this.split.el.getHeight();
39694 updateBox : function(box){
39695 if(this.split && !this.collapsed){
39696 box.height -= this.split.el.getHeight();
39697 this.split.el.setLeft(box.x);
39698 this.split.el.setTop(box.y+box.height);
39699 this.split.el.setWidth(box.width);
39701 if(this.collapsed){
39702 this.updateBody(box.width, null);
39704 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39712 Roo.bootstrap.layout.South = function(config){
39713 config.region = 'south';
39714 config.cursor = 's-resize';
39715 Roo.bootstrap.layout.Split.call(this, config);
39717 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39718 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39719 this.split.el.addClass("roo-layout-split-v");
39724 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39725 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39727 onRender : function(ctr, pos)
39729 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39730 var size = this.config.initialSize || this.config.height;
39731 if(this.el && typeof size != "undefined"){
39732 this.el.setHeight(size);
39737 getBox : function(){
39738 if(this.collapsed){
39739 return this.collapsedEl.getBox();
39741 var box = this.el.getBox();
39743 var sh = this.split.el.getHeight();
39750 updateBox : function(box){
39751 if(this.split && !this.collapsed){
39752 var sh = this.split.el.getHeight();
39755 this.split.el.setLeft(box.x);
39756 this.split.el.setTop(box.y-sh);
39757 this.split.el.setWidth(box.width);
39759 if(this.collapsed){
39760 this.updateBody(box.width, null);
39762 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39766 Roo.bootstrap.layout.East = function(config){
39767 config.region = "east";
39768 config.cursor = "e-resize";
39769 Roo.bootstrap.layout.Split.call(this, config);
39771 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39772 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39773 this.split.el.addClass("roo-layout-split-h");
39777 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39778 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39780 onRender : function(ctr, pos)
39782 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39783 var size = this.config.initialSize || this.config.width;
39784 if(this.el && typeof size != "undefined"){
39785 this.el.setWidth(size);
39790 getBox : function(){
39791 if(this.collapsed){
39792 return this.collapsedEl.getBox();
39794 var box = this.el.getBox();
39796 var sw = this.split.el.getWidth();
39803 updateBox : function(box){
39804 if(this.split && !this.collapsed){
39805 var sw = this.split.el.getWidth();
39807 this.split.el.setLeft(box.x);
39808 this.split.el.setTop(box.y);
39809 this.split.el.setHeight(box.height);
39812 if(this.collapsed){
39813 this.updateBody(null, box.height);
39815 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39819 Roo.bootstrap.layout.West = function(config){
39820 config.region = "west";
39821 config.cursor = "w-resize";
39823 Roo.bootstrap.layout.Split.call(this, config);
39825 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39826 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39827 this.split.el.addClass("roo-layout-split-h");
39831 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39832 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39834 onRender: function(ctr, pos)
39836 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39837 var size = this.config.initialSize || this.config.width;
39838 if(typeof size != "undefined"){
39839 this.el.setWidth(size);
39843 getBox : function(){
39844 if(this.collapsed){
39845 return this.collapsedEl.getBox();
39847 var box = this.el.getBox();
39848 if (box.width == 0) {
39849 box.width = this.config.width; // kludge?
39852 box.width += this.split.el.getWidth();
39857 updateBox : function(box){
39858 if(this.split && !this.collapsed){
39859 var sw = this.split.el.getWidth();
39861 this.split.el.setLeft(box.x+box.width);
39862 this.split.el.setTop(box.y);
39863 this.split.el.setHeight(box.height);
39865 if(this.collapsed){
39866 this.updateBody(null, box.height);
39868 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39870 });Roo.namespace("Roo.bootstrap.panel");/*
39872 * Ext JS Library 1.1.1
39873 * Copyright(c) 2006-2007, Ext JS, LLC.
39875 * Originally Released Under LGPL - original licence link has changed is not relivant.
39878 * <script type="text/javascript">
39881 * @class Roo.ContentPanel
39882 * @extends Roo.util.Observable
39883 * A basic ContentPanel element.
39884 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39885 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39886 * @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
39887 * @cfg {Boolean} closable True if the panel can be closed/removed
39888 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39889 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39890 * @cfg {Toolbar} toolbar A toolbar for this panel
39891 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39892 * @cfg {String} title The title for this panel
39893 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39894 * @cfg {String} url Calls {@link #setUrl} with this value
39895 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39896 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39897 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39898 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39899 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39900 * @cfg {Boolean} badges render the badges
39901 * @cfg {String} cls extra classes to use
39902 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39905 * Create a new ContentPanel.
39906 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39907 * @param {String/Object} config A string to set only the title or a config object
39908 * @param {String} content (optional) Set the HTML content for this panel
39909 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39911 Roo.bootstrap.panel.Content = function( config){
39913 this.tpl = config.tpl || false;
39915 var el = config.el;
39916 var content = config.content;
39918 if(config.autoCreate){ // xtype is available if this is called from factory
39921 this.el = Roo.get(el);
39922 if(!this.el && config && config.autoCreate){
39923 if(typeof config.autoCreate == "object"){
39924 if(!config.autoCreate.id){
39925 config.autoCreate.id = config.id||el;
39927 this.el = Roo.DomHelper.append(document.body,
39928 config.autoCreate, true);
39932 cls: (config.cls || '') +
39933 (config.background ? ' bg-' + config.background : '') +
39934 " roo-layout-inactive-content",
39937 if (config.iframe) {
39941 style : 'border: 0px',
39942 src : 'about:blank'
39948 elcfg.html = config.html;
39952 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39953 if (config.iframe) {
39954 this.iframeEl = this.el.select('iframe',true).first();
39959 this.closable = false;
39960 this.loaded = false;
39961 this.active = false;
39964 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39966 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39968 this.wrapEl = this.el; //this.el.wrap();
39970 if (config.toolbar.items) {
39971 ti = config.toolbar.items ;
39972 delete config.toolbar.items ;
39976 this.toolbar.render(this.wrapEl, 'before');
39977 for(var i =0;i < ti.length;i++) {
39978 // Roo.log(['add child', items[i]]);
39979 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39981 this.toolbar.items = nitems;
39982 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39983 delete config.toolbar;
39987 // xtype created footer. - not sure if will work as we normally have to render first..
39988 if (this.footer && !this.footer.el && this.footer.xtype) {
39989 if (!this.wrapEl) {
39990 this.wrapEl = this.el.wrap();
39993 this.footer.container = this.wrapEl.createChild();
39995 this.footer = Roo.factory(this.footer, Roo);
40000 if(typeof config == "string"){
40001 this.title = config;
40003 Roo.apply(this, config);
40007 this.resizeEl = Roo.get(this.resizeEl, true);
40009 this.resizeEl = this.el;
40011 // handle view.xtype
40019 * Fires when this panel is activated.
40020 * @param {Roo.ContentPanel} this
40024 * @event deactivate
40025 * Fires when this panel is activated.
40026 * @param {Roo.ContentPanel} this
40028 "deactivate" : true,
40032 * Fires when this panel is resized if fitToFrame is true.
40033 * @param {Roo.ContentPanel} this
40034 * @param {Number} width The width after any component adjustments
40035 * @param {Number} height The height after any component adjustments
40041 * Fires when this tab is created
40042 * @param {Roo.ContentPanel} this
40053 if(this.autoScroll && !this.iframe){
40054 this.resizeEl.setStyle("overflow", "auto");
40056 // fix randome scrolling
40057 //this.el.on('scroll', function() {
40058 // Roo.log('fix random scolling');
40059 // this.scrollTo('top',0);
40062 content = content || this.content;
40064 this.setContent(content);
40066 if(config && config.url){
40067 this.setUrl(this.url, this.params, this.loadOnce);
40072 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40074 if (this.view && typeof(this.view.xtype) != 'undefined') {
40075 this.view.el = this.el.appendChild(document.createElement("div"));
40076 this.view = Roo.factory(this.view);
40077 this.view.render && this.view.render(false, '');
40081 this.fireEvent('render', this);
40084 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40094 setRegion : function(region){
40095 this.region = region;
40096 this.setActiveClass(region && !this.background);
40100 setActiveClass: function(state)
40103 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40104 this.el.setStyle('position','relative');
40106 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40107 this.el.setStyle('position', 'absolute');
40112 * Returns the toolbar for this Panel if one was configured.
40113 * @return {Roo.Toolbar}
40115 getToolbar : function(){
40116 return this.toolbar;
40119 setActiveState : function(active)
40121 this.active = active;
40122 this.setActiveClass(active);
40124 if(this.fireEvent("deactivate", this) === false){
40129 this.fireEvent("activate", this);
40133 * Updates this panel's element (not for iframe)
40134 * @param {String} content The new content
40135 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40137 setContent : function(content, loadScripts){
40142 this.el.update(content, loadScripts);
40145 ignoreResize : function(w, h){
40146 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40149 this.lastSize = {width: w, height: h};
40154 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40155 * @return {Roo.UpdateManager} The UpdateManager
40157 getUpdateManager : function(){
40161 return this.el.getUpdateManager();
40164 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40165 * Does not work with IFRAME contents
40166 * @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:
40169 url: "your-url.php",
40170 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40171 callback: yourFunction,
40172 scope: yourObject, //(optional scope)
40175 text: "Loading...",
40181 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40182 * 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.
40183 * @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}
40184 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40185 * @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.
40186 * @return {Roo.ContentPanel} this
40194 var um = this.el.getUpdateManager();
40195 um.update.apply(um, arguments);
40201 * 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.
40202 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40203 * @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)
40204 * @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)
40205 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40207 setUrl : function(url, params, loadOnce){
40209 this.iframeEl.dom.src = url;
40213 if(this.refreshDelegate){
40214 this.removeListener("activate", this.refreshDelegate);
40216 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40217 this.on("activate", this.refreshDelegate);
40218 return this.el.getUpdateManager();
40221 _handleRefresh : function(url, params, loadOnce){
40222 if(!loadOnce || !this.loaded){
40223 var updater = this.el.getUpdateManager();
40224 updater.update(url, params, this._setLoaded.createDelegate(this));
40228 _setLoaded : function(){
40229 this.loaded = true;
40233 * Returns this panel's id
40236 getId : function(){
40241 * Returns this panel's element - used by regiosn to add.
40242 * @return {Roo.Element}
40244 getEl : function(){
40245 return this.wrapEl || this.el;
40250 adjustForComponents : function(width, height)
40252 //Roo.log('adjustForComponents ');
40253 if(this.resizeEl != this.el){
40254 width -= this.el.getFrameWidth('lr');
40255 height -= this.el.getFrameWidth('tb');
40258 var te = this.toolbar.getEl();
40259 te.setWidth(width);
40260 height -= te.getHeight();
40263 var te = this.footer.getEl();
40264 te.setWidth(width);
40265 height -= te.getHeight();
40269 if(this.adjustments){
40270 width += this.adjustments[0];
40271 height += this.adjustments[1];
40273 return {"width": width, "height": height};
40276 setSize : function(width, height){
40277 if(this.fitToFrame && !this.ignoreResize(width, height)){
40278 if(this.fitContainer && this.resizeEl != this.el){
40279 this.el.setSize(width, height);
40281 var size = this.adjustForComponents(width, height);
40283 this.iframeEl.setSize(width,height);
40286 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40287 this.fireEvent('resize', this, size.width, size.height);
40294 * Returns this panel's title
40297 getTitle : function(){
40299 if (typeof(this.title) != 'object') {
40304 for (var k in this.title) {
40305 if (!this.title.hasOwnProperty(k)) {
40309 if (k.indexOf('-') >= 0) {
40310 var s = k.split('-');
40311 for (var i = 0; i<s.length; i++) {
40312 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40315 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40322 * Set this panel's title
40323 * @param {String} title
40325 setTitle : function(title){
40326 this.title = title;
40328 this.region.updatePanelTitle(this, title);
40333 * Returns true is this panel was configured to be closable
40334 * @return {Boolean}
40336 isClosable : function(){
40337 return this.closable;
40340 beforeSlide : function(){
40342 this.resizeEl.clip();
40345 afterSlide : function(){
40347 this.resizeEl.unclip();
40351 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40352 * Will fail silently if the {@link #setUrl} method has not been called.
40353 * This does not activate the panel, just updates its content.
40355 refresh : function(){
40356 if(this.refreshDelegate){
40357 this.loaded = false;
40358 this.refreshDelegate();
40363 * Destroys this panel
40365 destroy : function(){
40366 this.el.removeAllListeners();
40367 var tempEl = document.createElement("span");
40368 tempEl.appendChild(this.el.dom);
40369 tempEl.innerHTML = "";
40375 * form - if the content panel contains a form - this is a reference to it.
40376 * @type {Roo.form.Form}
40380 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40381 * This contains a reference to it.
40387 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40397 * @param {Object} cfg Xtype definition of item to add.
40401 getChildContainer: function () {
40402 return this.getEl();
40407 var ret = new Roo.factory(cfg);
40412 if (cfg.xtype.match(/^Form$/)) {
40415 //if (this.footer) {
40416 // el = this.footer.container.insertSibling(false, 'before');
40418 el = this.el.createChild();
40421 this.form = new Roo.form.Form(cfg);
40424 if ( this.form.allItems.length) {
40425 this.form.render(el.dom);
40429 // should only have one of theses..
40430 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40431 // views.. should not be just added - used named prop 'view''
40433 cfg.el = this.el.appendChild(document.createElement("div"));
40436 var ret = new Roo.factory(cfg);
40438 ret.render && ret.render(false, ''); // render blank..
40448 * @class Roo.bootstrap.panel.Grid
40449 * @extends Roo.bootstrap.panel.Content
40451 * Create a new GridPanel.
40452 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40453 * @param {Object} config A the config object
40459 Roo.bootstrap.panel.Grid = function(config)
40463 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40464 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40466 config.el = this.wrapper;
40467 //this.el = this.wrapper;
40469 if (config.container) {
40470 // ctor'ed from a Border/panel.grid
40473 this.wrapper.setStyle("overflow", "hidden");
40474 this.wrapper.addClass('roo-grid-container');
40479 if(config.toolbar){
40480 var tool_el = this.wrapper.createChild();
40481 this.toolbar = Roo.factory(config.toolbar);
40483 if (config.toolbar.items) {
40484 ti = config.toolbar.items ;
40485 delete config.toolbar.items ;
40489 this.toolbar.render(tool_el);
40490 for(var i =0;i < ti.length;i++) {
40491 // Roo.log(['add child', items[i]]);
40492 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40494 this.toolbar.items = nitems;
40496 delete config.toolbar;
40499 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40500 config.grid.scrollBody = true;;
40501 config.grid.monitorWindowResize = false; // turn off autosizing
40502 config.grid.autoHeight = false;
40503 config.grid.autoWidth = false;
40505 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40507 if (config.background) {
40508 // render grid on panel activation (if panel background)
40509 this.on('activate', function(gp) {
40510 if (!gp.grid.rendered) {
40511 gp.grid.render(this.wrapper);
40512 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40517 this.grid.render(this.wrapper);
40518 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40521 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40522 // ??? needed ??? config.el = this.wrapper;
40527 // xtype created footer. - not sure if will work as we normally have to render first..
40528 if (this.footer && !this.footer.el && this.footer.xtype) {
40530 var ctr = this.grid.getView().getFooterPanel(true);
40531 this.footer.dataSource = this.grid.dataSource;
40532 this.footer = Roo.factory(this.footer, Roo);
40533 this.footer.render(ctr);
40543 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40544 getId : function(){
40545 return this.grid.id;
40549 * Returns the grid for this panel
40550 * @return {Roo.bootstrap.Table}
40552 getGrid : function(){
40556 setSize : function(width, height){
40557 if(!this.ignoreResize(width, height)){
40558 var grid = this.grid;
40559 var size = this.adjustForComponents(width, height);
40560 // tfoot is not a footer?
40563 var gridel = grid.getGridEl();
40564 gridel.setSize(size.width, size.height);
40566 var tbd = grid.getGridEl().select('tbody', true).first();
40567 var thd = grid.getGridEl().select('thead',true).first();
40568 var tbf= grid.getGridEl().select('tfoot', true).first();
40571 size.height -= tbf.getHeight();
40574 size.height -= thd.getHeight();
40577 tbd.setSize(size.width, size.height );
40578 // this is for the account management tab -seems to work there.
40579 var thd = grid.getGridEl().select('thead',true).first();
40581 // tbd.setSize(size.width, size.height - thd.getHeight());
40590 beforeSlide : function(){
40591 this.grid.getView().scroller.clip();
40594 afterSlide : function(){
40595 this.grid.getView().scroller.unclip();
40598 destroy : function(){
40599 this.grid.destroy();
40601 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40606 * @class Roo.bootstrap.panel.Nest
40607 * @extends Roo.bootstrap.panel.Content
40609 * Create a new Panel, that can contain a layout.Border.
40612 * @param {Roo.BorderLayout} layout The layout for this panel
40613 * @param {String/Object} config A string to set only the title or a config object
40615 Roo.bootstrap.panel.Nest = function(config)
40617 // construct with only one argument..
40618 /* FIXME - implement nicer consturctors
40619 if (layout.layout) {
40621 layout = config.layout;
40622 delete config.layout;
40624 if (layout.xtype && !layout.getEl) {
40625 // then layout needs constructing..
40626 layout = Roo.factory(layout, Roo);
40630 config.el = config.layout.getEl();
40632 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40634 config.layout.monitorWindowResize = false; // turn off autosizing
40635 this.layout = config.layout;
40636 this.layout.getEl().addClass("roo-layout-nested-layout");
40637 this.layout.parent = this;
40644 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40646 setSize : function(width, height){
40647 if(!this.ignoreResize(width, height)){
40648 var size = this.adjustForComponents(width, height);
40649 var el = this.layout.getEl();
40650 if (size.height < 1) {
40651 el.setWidth(size.width);
40653 el.setSize(size.width, size.height);
40655 var touch = el.dom.offsetWidth;
40656 this.layout.layout();
40657 // ie requires a double layout on the first pass
40658 if(Roo.isIE && !this.initialized){
40659 this.initialized = true;
40660 this.layout.layout();
40665 // activate all subpanels if not currently active..
40667 setActiveState : function(active){
40668 this.active = active;
40669 this.setActiveClass(active);
40672 this.fireEvent("deactivate", this);
40676 this.fireEvent("activate", this);
40677 // not sure if this should happen before or after..
40678 if (!this.layout) {
40679 return; // should not happen..
40682 for (var r in this.layout.regions) {
40683 reg = this.layout.getRegion(r);
40684 if (reg.getActivePanel()) {
40685 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40686 reg.setActivePanel(reg.getActivePanel());
40689 if (!reg.panels.length) {
40692 reg.showPanel(reg.getPanel(0));
40701 * Returns the nested BorderLayout for this panel
40702 * @return {Roo.BorderLayout}
40704 getLayout : function(){
40705 return this.layout;
40709 * Adds a xtype elements to the layout of the nested panel
40713 xtype : 'ContentPanel',
40720 xtype : 'NestedLayoutPanel',
40726 items : [ ... list of content panels or nested layout panels.. ]
40730 * @param {Object} cfg Xtype definition of item to add.
40732 addxtype : function(cfg) {
40733 return this.layout.addxtype(cfg);
40738 * Ext JS Library 1.1.1
40739 * Copyright(c) 2006-2007, Ext JS, LLC.
40741 * Originally Released Under LGPL - original licence link has changed is not relivant.
40744 * <script type="text/javascript">
40747 * @class Roo.TabPanel
40748 * @extends Roo.util.Observable
40749 * A lightweight tab container.
40753 // basic tabs 1, built from existing content
40754 var tabs = new Roo.TabPanel("tabs1");
40755 tabs.addTab("script", "View Script");
40756 tabs.addTab("markup", "View Markup");
40757 tabs.activate("script");
40759 // more advanced tabs, built from javascript
40760 var jtabs = new Roo.TabPanel("jtabs");
40761 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40763 // set up the UpdateManager
40764 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40765 var updater = tab2.getUpdateManager();
40766 updater.setDefaultUrl("ajax1.htm");
40767 tab2.on('activate', updater.refresh, updater, true);
40769 // Use setUrl for Ajax loading
40770 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40771 tab3.setUrl("ajax2.htm", null, true);
40774 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40777 jtabs.activate("jtabs-1");
40780 * Create a new TabPanel.
40781 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40782 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40784 Roo.bootstrap.panel.Tabs = function(config){
40786 * The container element for this TabPanel.
40787 * @type Roo.Element
40789 this.el = Roo.get(config.el);
40792 if(typeof config == "boolean"){
40793 this.tabPosition = config ? "bottom" : "top";
40795 Roo.apply(this, config);
40799 if(this.tabPosition == "bottom"){
40800 // if tabs are at the bottom = create the body first.
40801 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40802 this.el.addClass("roo-tabs-bottom");
40804 // next create the tabs holders
40806 if (this.tabPosition == "west"){
40808 var reg = this.region; // fake it..
40810 if (!reg.mgr.parent) {
40813 reg = reg.mgr.parent.region;
40815 Roo.log("got nest?");
40817 if (reg.mgr.getRegion('west')) {
40818 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40819 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40820 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40821 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40822 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40830 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40831 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40832 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40833 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40838 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40841 // finally - if tabs are at the top, then create the body last..
40842 if(this.tabPosition != "bottom"){
40843 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40844 * @type Roo.Element
40846 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40847 this.el.addClass("roo-tabs-top");
40851 this.bodyEl.setStyle("position", "relative");
40853 this.active = null;
40854 this.activateDelegate = this.activate.createDelegate(this);
40859 * Fires when the active tab changes
40860 * @param {Roo.TabPanel} this
40861 * @param {Roo.TabPanelItem} activePanel The new active tab
40865 * @event beforetabchange
40866 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40867 * @param {Roo.TabPanel} this
40868 * @param {Object} e Set cancel to true on this object to cancel the tab change
40869 * @param {Roo.TabPanelItem} tab The tab being changed to
40871 "beforetabchange" : true
40874 Roo.EventManager.onWindowResize(this.onResize, this);
40875 this.cpad = this.el.getPadding("lr");
40876 this.hiddenCount = 0;
40879 // toolbar on the tabbar support...
40880 if (this.toolbar) {
40881 alert("no toolbar support yet");
40882 this.toolbar = false;
40884 var tcfg = this.toolbar;
40885 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40886 this.toolbar = new Roo.Toolbar(tcfg);
40887 if (Roo.isSafari) {
40888 var tbl = tcfg.container.child('table', true);
40889 tbl.setAttribute('width', '100%');
40897 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40900 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40902 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40904 tabPosition : "top",
40906 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40908 currentTabWidth : 0,
40910 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40914 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40918 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40920 preferredTabWidth : 175,
40922 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40924 resizeTabs : false,
40926 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40928 monitorResize : true,
40930 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40932 toolbar : false, // set by caller..
40934 region : false, /// set by caller
40936 disableTooltips : true, // not used yet...
40939 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40940 * @param {String} id The id of the div to use <b>or create</b>
40941 * @param {String} text The text for the tab
40942 * @param {String} content (optional) Content to put in the TabPanelItem body
40943 * @param {Boolean} closable (optional) True to create a close icon on the tab
40944 * @return {Roo.TabPanelItem} The created TabPanelItem
40946 addTab : function(id, text, content, closable, tpl)
40948 var item = new Roo.bootstrap.panel.TabItem({
40952 closable : closable,
40955 this.addTabItem(item);
40957 item.setContent(content);
40963 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40964 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40965 * @return {Roo.TabPanelItem}
40967 getTab : function(id){
40968 return this.items[id];
40972 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40973 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40975 hideTab : function(id){
40976 var t = this.items[id];
40979 this.hiddenCount++;
40980 this.autoSizeTabs();
40985 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40986 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40988 unhideTab : function(id){
40989 var t = this.items[id];
40991 t.setHidden(false);
40992 this.hiddenCount--;
40993 this.autoSizeTabs();
40998 * Adds an existing {@link Roo.TabPanelItem}.
40999 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41001 addTabItem : function(item)
41003 this.items[item.id] = item;
41004 this.items.push(item);
41005 this.autoSizeTabs();
41006 // if(this.resizeTabs){
41007 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41008 // this.autoSizeTabs();
41010 // item.autoSize();
41015 * Removes a {@link Roo.TabPanelItem}.
41016 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41018 removeTab : function(id){
41019 var items = this.items;
41020 var tab = items[id];
41021 if(!tab) { return; }
41022 var index = items.indexOf(tab);
41023 if(this.active == tab && items.length > 1){
41024 var newTab = this.getNextAvailable(index);
41029 this.stripEl.dom.removeChild(tab.pnode.dom);
41030 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41031 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41033 items.splice(index, 1);
41034 delete this.items[tab.id];
41035 tab.fireEvent("close", tab);
41036 tab.purgeListeners();
41037 this.autoSizeTabs();
41040 getNextAvailable : function(start){
41041 var items = this.items;
41043 // look for a next tab that will slide over to
41044 // replace the one being removed
41045 while(index < items.length){
41046 var item = items[++index];
41047 if(item && !item.isHidden()){
41051 // if one isn't found select the previous tab (on the left)
41054 var item = items[--index];
41055 if(item && !item.isHidden()){
41063 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41064 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41066 disableTab : function(id){
41067 var tab = this.items[id];
41068 if(tab && this.active != tab){
41074 * Enables a {@link Roo.TabPanelItem} that is disabled.
41075 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41077 enableTab : function(id){
41078 var tab = this.items[id];
41083 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41084 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41085 * @return {Roo.TabPanelItem} The TabPanelItem.
41087 activate : function(id)
41089 //Roo.log('activite:' + id);
41091 var tab = this.items[id];
41095 if(tab == this.active || tab.disabled){
41099 this.fireEvent("beforetabchange", this, e, tab);
41100 if(e.cancel !== true && !tab.disabled){
41102 this.active.hide();
41104 this.active = this.items[id];
41105 this.active.show();
41106 this.fireEvent("tabchange", this, this.active);
41112 * Gets the active {@link Roo.TabPanelItem}.
41113 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41115 getActiveTab : function(){
41116 return this.active;
41120 * Updates the tab body element to fit the height of the container element
41121 * for overflow scrolling
41122 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41124 syncHeight : function(targetHeight){
41125 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41126 var bm = this.bodyEl.getMargins();
41127 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41128 this.bodyEl.setHeight(newHeight);
41132 onResize : function(){
41133 if(this.monitorResize){
41134 this.autoSizeTabs();
41139 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41141 beginUpdate : function(){
41142 this.updating = true;
41146 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41148 endUpdate : function(){
41149 this.updating = false;
41150 this.autoSizeTabs();
41154 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41156 autoSizeTabs : function()
41158 var count = this.items.length;
41159 var vcount = count - this.hiddenCount;
41162 this.stripEl.hide();
41164 this.stripEl.show();
41167 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41172 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41173 var availWidth = Math.floor(w / vcount);
41174 var b = this.stripBody;
41175 if(b.getWidth() > w){
41176 var tabs = this.items;
41177 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41178 if(availWidth < this.minTabWidth){
41179 /*if(!this.sleft){ // incomplete scrolling code
41180 this.createScrollButtons();
41183 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41186 if(this.currentTabWidth < this.preferredTabWidth){
41187 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41193 * Returns the number of tabs in this TabPanel.
41196 getCount : function(){
41197 return this.items.length;
41201 * Resizes all the tabs to the passed width
41202 * @param {Number} The new width
41204 setTabWidth : function(width){
41205 this.currentTabWidth = width;
41206 for(var i = 0, len = this.items.length; i < len; i++) {
41207 if(!this.items[i].isHidden()) {
41208 this.items[i].setWidth(width);
41214 * Destroys this TabPanel
41215 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41217 destroy : function(removeEl){
41218 Roo.EventManager.removeResizeListener(this.onResize, this);
41219 for(var i = 0, len = this.items.length; i < len; i++){
41220 this.items[i].purgeListeners();
41222 if(removeEl === true){
41223 this.el.update("");
41228 createStrip : function(container)
41230 var strip = document.createElement("nav");
41231 strip.className = Roo.bootstrap.version == 4 ?
41232 "navbar-light bg-light" :
41233 "navbar navbar-default"; //"x-tabs-wrap";
41234 container.appendChild(strip);
41238 createStripList : function(strip)
41240 // div wrapper for retard IE
41241 // returns the "tr" element.
41242 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41243 //'<div class="x-tabs-strip-wrap">'+
41244 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41245 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41246 return strip.firstChild; //.firstChild.firstChild.firstChild;
41248 createBody : function(container)
41250 var body = document.createElement("div");
41251 Roo.id(body, "tab-body");
41252 //Roo.fly(body).addClass("x-tabs-body");
41253 Roo.fly(body).addClass("tab-content");
41254 container.appendChild(body);
41257 createItemBody :function(bodyEl, id){
41258 var body = Roo.getDom(id);
41260 body = document.createElement("div");
41263 //Roo.fly(body).addClass("x-tabs-item-body");
41264 Roo.fly(body).addClass("tab-pane");
41265 bodyEl.insertBefore(body, bodyEl.firstChild);
41269 createStripElements : function(stripEl, text, closable, tpl)
41271 var td = document.createElement("li"); // was td..
41272 td.className = 'nav-item';
41274 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41277 stripEl.appendChild(td);
41279 td.className = "x-tabs-closable";
41280 if(!this.closeTpl){
41281 this.closeTpl = new Roo.Template(
41282 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41283 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41284 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41287 var el = this.closeTpl.overwrite(td, {"text": text});
41288 var close = el.getElementsByTagName("div")[0];
41289 var inner = el.getElementsByTagName("em")[0];
41290 return {"el": el, "close": close, "inner": inner};
41293 // not sure what this is..
41294 // if(!this.tabTpl){
41295 //this.tabTpl = new Roo.Template(
41296 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41297 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41299 // this.tabTpl = new Roo.Template(
41300 // '<a href="#">' +
41301 // '<span unselectable="on"' +
41302 // (this.disableTooltips ? '' : ' title="{text}"') +
41303 // ' >{text}</span></a>'
41309 var template = tpl || this.tabTpl || false;
41312 template = new Roo.Template(
41313 Roo.bootstrap.version == 4 ?
41315 '<a class="nav-link" href="#" unselectable="on"' +
41316 (this.disableTooltips ? '' : ' title="{text}"') +
41319 '<a class="nav-link" href="#">' +
41320 '<span unselectable="on"' +
41321 (this.disableTooltips ? '' : ' title="{text}"') +
41322 ' >{text}</span></a>'
41327 switch (typeof(template)) {
41331 template = new Roo.Template(template);
41337 var el = template.overwrite(td, {"text": text});
41339 var inner = el.getElementsByTagName("span")[0];
41341 return {"el": el, "inner": inner};
41349 * @class Roo.TabPanelItem
41350 * @extends Roo.util.Observable
41351 * Represents an individual item (tab plus body) in a TabPanel.
41352 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41353 * @param {String} id The id of this TabPanelItem
41354 * @param {String} text The text for the tab of this TabPanelItem
41355 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41357 Roo.bootstrap.panel.TabItem = function(config){
41359 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41360 * @type Roo.TabPanel
41362 this.tabPanel = config.panel;
41364 * The id for this TabPanelItem
41367 this.id = config.id;
41369 this.disabled = false;
41371 this.text = config.text;
41373 this.loaded = false;
41374 this.closable = config.closable;
41377 * The body element for this TabPanelItem.
41378 * @type Roo.Element
41380 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41381 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41382 this.bodyEl.setStyle("display", "block");
41383 this.bodyEl.setStyle("zoom", "1");
41384 //this.hideAction();
41386 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41388 this.el = Roo.get(els.el);
41389 this.inner = Roo.get(els.inner, true);
41390 this.textEl = Roo.bootstrap.version == 4 ?
41391 this.el : Roo.get(this.el.dom.firstChild, true);
41393 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41394 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41397 // this.el.on("mousedown", this.onTabMouseDown, this);
41398 this.el.on("click", this.onTabClick, this);
41400 if(config.closable){
41401 var c = Roo.get(els.close, true);
41402 c.dom.title = this.closeText;
41403 c.addClassOnOver("close-over");
41404 c.on("click", this.closeClick, this);
41410 * Fires when this tab becomes the active tab.
41411 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41412 * @param {Roo.TabPanelItem} this
41416 * @event beforeclose
41417 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41418 * @param {Roo.TabPanelItem} this
41419 * @param {Object} e Set cancel to true on this object to cancel the close.
41421 "beforeclose": true,
41424 * Fires when this tab is closed.
41425 * @param {Roo.TabPanelItem} this
41429 * @event deactivate
41430 * Fires when this tab is no longer the active tab.
41431 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41432 * @param {Roo.TabPanelItem} this
41434 "deactivate" : true
41436 this.hidden = false;
41438 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41441 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41443 purgeListeners : function(){
41444 Roo.util.Observable.prototype.purgeListeners.call(this);
41445 this.el.removeAllListeners();
41448 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41451 this.status_node.addClass("active");
41454 this.tabPanel.stripWrap.repaint();
41456 this.fireEvent("activate", this.tabPanel, this);
41460 * Returns true if this tab is the active tab.
41461 * @return {Boolean}
41463 isActive : function(){
41464 return this.tabPanel.getActiveTab() == this;
41468 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41471 this.status_node.removeClass("active");
41473 this.fireEvent("deactivate", this.tabPanel, this);
41476 hideAction : function(){
41477 this.bodyEl.hide();
41478 this.bodyEl.setStyle("position", "absolute");
41479 this.bodyEl.setLeft("-20000px");
41480 this.bodyEl.setTop("-20000px");
41483 showAction : function(){
41484 this.bodyEl.setStyle("position", "relative");
41485 this.bodyEl.setTop("");
41486 this.bodyEl.setLeft("");
41487 this.bodyEl.show();
41491 * Set the tooltip for the tab.
41492 * @param {String} tooltip The tab's tooltip
41494 setTooltip : function(text){
41495 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41496 this.textEl.dom.qtip = text;
41497 this.textEl.dom.removeAttribute('title');
41499 this.textEl.dom.title = text;
41503 onTabClick : function(e){
41504 e.preventDefault();
41505 this.tabPanel.activate(this.id);
41508 onTabMouseDown : function(e){
41509 e.preventDefault();
41510 this.tabPanel.activate(this.id);
41513 getWidth : function(){
41514 return this.inner.getWidth();
41517 setWidth : function(width){
41518 var iwidth = width - this.linode.getPadding("lr");
41519 this.inner.setWidth(iwidth);
41520 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41521 this.linode.setWidth(width);
41525 * Show or hide the tab
41526 * @param {Boolean} hidden True to hide or false to show.
41528 setHidden : function(hidden){
41529 this.hidden = hidden;
41530 this.linode.setStyle("display", hidden ? "none" : "");
41534 * Returns true if this tab is "hidden"
41535 * @return {Boolean}
41537 isHidden : function(){
41538 return this.hidden;
41542 * Returns the text for this tab
41545 getText : function(){
41549 autoSize : function(){
41550 //this.el.beginMeasure();
41551 this.textEl.setWidth(1);
41553 * #2804 [new] Tabs in Roojs
41554 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41556 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41557 //this.el.endMeasure();
41561 * Sets the text for the tab (Note: this also sets the tooltip text)
41562 * @param {String} text The tab's text and tooltip
41564 setText : function(text){
41566 this.textEl.update(text);
41567 this.setTooltip(text);
41568 //if(!this.tabPanel.resizeTabs){
41569 // this.autoSize();
41573 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41575 activate : function(){
41576 this.tabPanel.activate(this.id);
41580 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41582 disable : function(){
41583 if(this.tabPanel.active != this){
41584 this.disabled = true;
41585 this.status_node.addClass("disabled");
41590 * Enables this TabPanelItem if it was previously disabled.
41592 enable : function(){
41593 this.disabled = false;
41594 this.status_node.removeClass("disabled");
41598 * Sets the content for this TabPanelItem.
41599 * @param {String} content The content
41600 * @param {Boolean} loadScripts true to look for and load scripts
41602 setContent : function(content, loadScripts){
41603 this.bodyEl.update(content, loadScripts);
41607 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41608 * @return {Roo.UpdateManager} The UpdateManager
41610 getUpdateManager : function(){
41611 return this.bodyEl.getUpdateManager();
41615 * Set a URL to be used to load the content for this TabPanelItem.
41616 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41617 * @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)
41618 * @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)
41619 * @return {Roo.UpdateManager} The UpdateManager
41621 setUrl : function(url, params, loadOnce){
41622 if(this.refreshDelegate){
41623 this.un('activate', this.refreshDelegate);
41625 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41626 this.on("activate", this.refreshDelegate);
41627 return this.bodyEl.getUpdateManager();
41631 _handleRefresh : function(url, params, loadOnce){
41632 if(!loadOnce || !this.loaded){
41633 var updater = this.bodyEl.getUpdateManager();
41634 updater.update(url, params, this._setLoaded.createDelegate(this));
41639 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41640 * Will fail silently if the setUrl method has not been called.
41641 * This does not activate the panel, just updates its content.
41643 refresh : function(){
41644 if(this.refreshDelegate){
41645 this.loaded = false;
41646 this.refreshDelegate();
41651 _setLoaded : function(){
41652 this.loaded = true;
41656 closeClick : function(e){
41659 this.fireEvent("beforeclose", this, o);
41660 if(o.cancel !== true){
41661 this.tabPanel.removeTab(this.id);
41665 * The text displayed in the tooltip for the close icon.
41668 closeText : "Close this tab"
41671 * This script refer to:
41672 * Title: International Telephone Input
41673 * Author: Jack O'Connor
41674 * Code version: v12.1.12
41675 * Availability: https://github.com/jackocnr/intl-tel-input.git
41678 Roo.bootstrap.PhoneInputData = function() {
41681 "Afghanistan (افغانستان)",
41686 "Albania (Shqipëri)",
41691 "Algeria (الجزائر)",
41716 "Antigua and Barbuda",
41726 "Armenia (Հայաստան)",
41742 "Austria (Österreich)",
41747 "Azerbaijan (Azərbaycan)",
41757 "Bahrain (البحرين)",
41762 "Bangladesh (বাংলাদেশ)",
41772 "Belarus (Беларусь)",
41777 "Belgium (België)",
41807 "Bosnia and Herzegovina (Босна и Херцеговина)",
41822 "British Indian Ocean Territory",
41827 "British Virgin Islands",
41837 "Bulgaria (България)",
41847 "Burundi (Uburundi)",
41852 "Cambodia (កម្ពុជា)",
41857 "Cameroon (Cameroun)",
41866 ["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"]
41869 "Cape Verde (Kabu Verdi)",
41874 "Caribbean Netherlands",
41885 "Central African Republic (République centrafricaine)",
41905 "Christmas Island",
41911 "Cocos (Keeling) Islands",
41922 "Comoros (جزر القمر)",
41927 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41932 "Congo (Republic) (Congo-Brazzaville)",
41952 "Croatia (Hrvatska)",
41973 "Czech Republic (Česká republika)",
41978 "Denmark (Danmark)",
41993 "Dominican Republic (República Dominicana)",
41997 ["809", "829", "849"]
42015 "Equatorial Guinea (Guinea Ecuatorial)",
42035 "Falkland Islands (Islas Malvinas)",
42040 "Faroe Islands (Føroyar)",
42061 "French Guiana (Guyane française)",
42066 "French Polynesia (Polynésie française)",
42081 "Georgia (საქართველო)",
42086 "Germany (Deutschland)",
42106 "Greenland (Kalaallit Nunaat)",
42143 "Guinea-Bissau (Guiné Bissau)",
42168 "Hungary (Magyarország)",
42173 "Iceland (Ísland)",
42193 "Iraq (العراق)",
42209 "Israel (ישראל)",
42236 "Jordan (الأردن)",
42241 "Kazakhstan (Казахстан)",
42262 "Kuwait (الكويت)",
42267 "Kyrgyzstan (Кыргызстан)",
42277 "Latvia (Latvija)",
42282 "Lebanon (لبنان)",
42297 "Libya (ليبيا)",
42307 "Lithuania (Lietuva)",
42322 "Macedonia (FYROM) (Македонија)",
42327 "Madagascar (Madagasikara)",
42357 "Marshall Islands",
42367 "Mauritania (موريتانيا)",
42372 "Mauritius (Moris)",
42393 "Moldova (Republica Moldova)",
42403 "Mongolia (Монгол)",
42408 "Montenegro (Crna Gora)",
42418 "Morocco (المغرب)",
42424 "Mozambique (Moçambique)",
42429 "Myanmar (Burma) (မြန်မာ)",
42434 "Namibia (Namibië)",
42449 "Netherlands (Nederland)",
42454 "New Caledonia (Nouvelle-Calédonie)",
42489 "North Korea (조선 민주주의 인민 공화국)",
42494 "Northern Mariana Islands",
42510 "Pakistan (پاکستان)",
42520 "Palestine (فلسطين)",
42530 "Papua New Guinea",
42572 "Réunion (La Réunion)",
42578 "Romania (România)",
42594 "Saint Barthélemy",
42605 "Saint Kitts and Nevis",
42615 "Saint Martin (Saint-Martin (partie française))",
42621 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42626 "Saint Vincent and the Grenadines",
42641 "São Tomé and Príncipe (São Tomé e Príncipe)",
42646 "Saudi Arabia (المملكة العربية السعودية)",
42651 "Senegal (Sénégal)",
42681 "Slovakia (Slovensko)",
42686 "Slovenia (Slovenija)",
42696 "Somalia (Soomaaliya)",
42706 "South Korea (대한민국)",
42711 "South Sudan (جنوب السودان)",
42721 "Sri Lanka (ශ්රී ලංකාව)",
42726 "Sudan (السودان)",
42736 "Svalbard and Jan Mayen",
42747 "Sweden (Sverige)",
42752 "Switzerland (Schweiz)",
42757 "Syria (سوريا)",
42802 "Trinidad and Tobago",
42807 "Tunisia (تونس)",
42812 "Turkey (Türkiye)",
42822 "Turks and Caicos Islands",
42832 "U.S. Virgin Islands",
42842 "Ukraine (Україна)",
42847 "United Arab Emirates (الإمارات العربية المتحدة)",
42869 "Uzbekistan (Oʻzbekiston)",
42879 "Vatican City (Città del Vaticano)",
42890 "Vietnam (Việt Nam)",
42895 "Wallis and Futuna (Wallis-et-Futuna)",
42900 "Western Sahara (الصحراء الغربية)",
42906 "Yemen (اليمن)",
42930 * This script refer to:
42931 * Title: International Telephone Input
42932 * Author: Jack O'Connor
42933 * Code version: v12.1.12
42934 * Availability: https://github.com/jackocnr/intl-tel-input.git
42938 * @class Roo.bootstrap.PhoneInput
42939 * @extends Roo.bootstrap.TriggerField
42940 * An input with International dial-code selection
42942 * @cfg {String} defaultDialCode default '+852'
42943 * @cfg {Array} preferedCountries default []
42946 * Create a new PhoneInput.
42947 * @param {Object} config Configuration options
42950 Roo.bootstrap.PhoneInput = function(config) {
42951 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42954 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42956 listWidth: undefined,
42958 selectedClass: 'active',
42960 invalidClass : "has-warning",
42962 validClass: 'has-success',
42964 allowed: '0123456789',
42969 * @cfg {String} defaultDialCode The default dial code when initializing the input
42971 defaultDialCode: '+852',
42974 * @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
42976 preferedCountries: false,
42978 getAutoCreate : function()
42980 var data = Roo.bootstrap.PhoneInputData();
42981 var align = this.labelAlign || this.parentLabelAlign();
42984 this.allCountries = [];
42985 this.dialCodeMapping = [];
42987 for (var i = 0; i < data.length; i++) {
42989 this.allCountries[i] = {
42993 priority: c[3] || 0,
42994 areaCodes: c[4] || null
42996 this.dialCodeMapping[c[2]] = {
42999 priority: c[3] || 0,
43000 areaCodes: c[4] || null
43012 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43013 maxlength: this.max_length,
43014 cls : 'form-control tel-input',
43015 autocomplete: 'new-password'
43018 var hiddenInput = {
43021 cls: 'hidden-tel-input'
43025 hiddenInput.name = this.name;
43028 if (this.disabled) {
43029 input.disabled = true;
43032 var flag_container = {
43049 cls: this.hasFeedback ? 'has-feedback' : '',
43055 cls: 'dial-code-holder',
43062 cls: 'roo-select2-container input-group',
43069 if (this.fieldLabel.length) {
43072 tooltip: 'This field is required'
43078 cls: 'control-label',
43084 html: this.fieldLabel
43087 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43093 if(this.indicatorpos == 'right') {
43094 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43101 if(align == 'left') {
43109 if(this.labelWidth > 12){
43110 label.style = "width: " + this.labelWidth + 'px';
43112 if(this.labelWidth < 13 && this.labelmd == 0){
43113 this.labelmd = this.labelWidth;
43115 if(this.labellg > 0){
43116 label.cls += ' col-lg-' + this.labellg;
43117 input.cls += ' col-lg-' + (12 - this.labellg);
43119 if(this.labelmd > 0){
43120 label.cls += ' col-md-' + this.labelmd;
43121 container.cls += ' col-md-' + (12 - this.labelmd);
43123 if(this.labelsm > 0){
43124 label.cls += ' col-sm-' + this.labelsm;
43125 container.cls += ' col-sm-' + (12 - this.labelsm);
43127 if(this.labelxs > 0){
43128 label.cls += ' col-xs-' + this.labelxs;
43129 container.cls += ' col-xs-' + (12 - this.labelxs);
43139 var settings = this;
43141 ['xs','sm','md','lg'].map(function(size){
43142 if (settings[size]) {
43143 cfg.cls += ' col-' + size + '-' + settings[size];
43147 this.store = new Roo.data.Store({
43148 proxy : new Roo.data.MemoryProxy({}),
43149 reader : new Roo.data.JsonReader({
43160 'name' : 'dialCode',
43164 'name' : 'priority',
43168 'name' : 'areaCodes',
43175 if(!this.preferedCountries) {
43176 this.preferedCountries = [
43183 var p = this.preferedCountries.reverse();
43186 for (var i = 0; i < p.length; i++) {
43187 for (var j = 0; j < this.allCountries.length; j++) {
43188 if(this.allCountries[j].iso2 == p[i]) {
43189 var t = this.allCountries[j];
43190 this.allCountries.splice(j,1);
43191 this.allCountries.unshift(t);
43197 this.store.proxy.data = {
43199 data: this.allCountries
43205 initEvents : function()
43208 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43210 this.indicator = this.indicatorEl();
43211 this.flag = this.flagEl();
43212 this.dialCodeHolder = this.dialCodeHolderEl();
43214 this.trigger = this.el.select('div.flag-box',true).first();
43215 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43220 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43221 _this.list.setWidth(lw);
43224 this.list.on('mouseover', this.onViewOver, this);
43225 this.list.on('mousemove', this.onViewMove, this);
43226 this.inputEl().on("keyup", this.onKeyUp, this);
43227 this.inputEl().on("keypress", this.onKeyPress, this);
43229 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43231 this.view = new Roo.View(this.list, this.tpl, {
43232 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43235 this.view.on('click', this.onViewClick, this);
43236 this.setValue(this.defaultDialCode);
43239 onTriggerClick : function(e)
43241 Roo.log('trigger click');
43246 if(this.isExpanded()){
43248 this.hasFocus = false;
43250 this.store.load({});
43251 this.hasFocus = true;
43256 isExpanded : function()
43258 return this.list.isVisible();
43261 collapse : function()
43263 if(!this.isExpanded()){
43267 Roo.get(document).un('mousedown', this.collapseIf, this);
43268 Roo.get(document).un('mousewheel', this.collapseIf, this);
43269 this.fireEvent('collapse', this);
43273 expand : function()
43277 if(this.isExpanded() || !this.hasFocus){
43281 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43282 this.list.setWidth(lw);
43285 this.restrictHeight();
43287 Roo.get(document).on('mousedown', this.collapseIf, this);
43288 Roo.get(document).on('mousewheel', this.collapseIf, this);
43290 this.fireEvent('expand', this);
43293 restrictHeight : function()
43295 this.list.alignTo(this.inputEl(), this.listAlign);
43296 this.list.alignTo(this.inputEl(), this.listAlign);
43299 onViewOver : function(e, t)
43301 if(this.inKeyMode){
43304 var item = this.view.findItemFromChild(t);
43307 var index = this.view.indexOf(item);
43308 this.select(index, false);
43313 onViewClick : function(view, doFocus, el, e)
43315 var index = this.view.getSelectedIndexes()[0];
43317 var r = this.store.getAt(index);
43320 this.onSelect(r, index);
43322 if(doFocus !== false && !this.blockFocus){
43323 this.inputEl().focus();
43327 onViewMove : function(e, t)
43329 this.inKeyMode = false;
43332 select : function(index, scrollIntoView)
43334 this.selectedIndex = index;
43335 this.view.select(index);
43336 if(scrollIntoView !== false){
43337 var el = this.view.getNode(index);
43339 this.list.scrollChildIntoView(el, false);
43344 createList : function()
43346 this.list = Roo.get(document.body).createChild({
43348 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43349 style: 'display:none'
43352 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43355 collapseIf : function(e)
43357 var in_combo = e.within(this.el);
43358 var in_list = e.within(this.list);
43359 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43361 if (in_combo || in_list || is_list) {
43367 onSelect : function(record, index)
43369 if(this.fireEvent('beforeselect', this, record, index) !== false){
43371 this.setFlagClass(record.data.iso2);
43372 this.setDialCode(record.data.dialCode);
43373 this.hasFocus = false;
43375 this.fireEvent('select', this, record, index);
43379 flagEl : function()
43381 var flag = this.el.select('div.flag',true).first();
43388 dialCodeHolderEl : function()
43390 var d = this.el.select('input.dial-code-holder',true).first();
43397 setDialCode : function(v)
43399 this.dialCodeHolder.dom.value = '+'+v;
43402 setFlagClass : function(n)
43404 this.flag.dom.className = 'flag '+n;
43407 getValue : function()
43409 var v = this.inputEl().getValue();
43410 if(this.dialCodeHolder) {
43411 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43416 setValue : function(v)
43418 var d = this.getDialCode(v);
43420 //invalid dial code
43421 if(v.length == 0 || !d || d.length == 0) {
43423 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43424 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43430 this.setFlagClass(this.dialCodeMapping[d].iso2);
43431 this.setDialCode(d);
43432 this.inputEl().dom.value = v.replace('+'+d,'');
43433 this.hiddenEl().dom.value = this.getValue();
43438 getDialCode : function(v)
43442 if (v.length == 0) {
43443 return this.dialCodeHolder.dom.value;
43447 if (v.charAt(0) != "+") {
43450 var numericChars = "";
43451 for (var i = 1; i < v.length; i++) {
43452 var c = v.charAt(i);
43455 if (this.dialCodeMapping[numericChars]) {
43456 dialCode = v.substr(1, i);
43458 if (numericChars.length == 4) {
43468 this.setValue(this.defaultDialCode);
43472 hiddenEl : function()
43474 return this.el.select('input.hidden-tel-input',true).first();
43477 // after setting val
43478 onKeyUp : function(e){
43479 this.setValue(this.getValue());
43482 onKeyPress : function(e){
43483 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43490 * @class Roo.bootstrap.MoneyField
43491 * @extends Roo.bootstrap.ComboBox
43492 * Bootstrap MoneyField class
43495 * Create a new MoneyField.
43496 * @param {Object} config Configuration options
43499 Roo.bootstrap.MoneyField = function(config) {
43501 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43505 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43508 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43510 allowDecimals : true,
43512 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43514 decimalSeparator : ".",
43516 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43518 decimalPrecision : 0,
43520 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43522 allowNegative : true,
43524 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43528 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43530 minValue : Number.NEGATIVE_INFINITY,
43532 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43534 maxValue : Number.MAX_VALUE,
43536 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43538 minText : "The minimum value for this field is {0}",
43540 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43542 maxText : "The maximum value for this field is {0}",
43544 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43545 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43547 nanText : "{0} is not a valid number",
43549 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43553 * @cfg {String} defaults currency of the MoneyField
43554 * value should be in lkey
43556 defaultCurrency : false,
43558 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43560 thousandsDelimiter : false,
43562 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43573 getAutoCreate : function()
43575 var align = this.labelAlign || this.parentLabelAlign();
43587 cls : 'form-control roo-money-amount-input',
43588 autocomplete: 'new-password'
43591 var hiddenInput = {
43595 cls: 'hidden-number-input'
43598 if(this.max_length) {
43599 input.maxlength = this.max_length;
43603 hiddenInput.name = this.name;
43606 if (this.disabled) {
43607 input.disabled = true;
43610 var clg = 12 - this.inputlg;
43611 var cmd = 12 - this.inputmd;
43612 var csm = 12 - this.inputsm;
43613 var cxs = 12 - this.inputxs;
43617 cls : 'row roo-money-field',
43621 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43625 cls: 'roo-select2-container input-group',
43629 cls : 'form-control roo-money-currency-input',
43630 autocomplete: 'new-password',
43632 name : this.currencyName
43636 cls : 'input-group-addon',
43650 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43654 cls: this.hasFeedback ? 'has-feedback' : '',
43665 if (this.fieldLabel.length) {
43668 tooltip: 'This field is required'
43674 cls: 'control-label',
43680 html: this.fieldLabel
43683 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43689 if(this.indicatorpos == 'right') {
43690 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43697 if(align == 'left') {
43705 if(this.labelWidth > 12){
43706 label.style = "width: " + this.labelWidth + 'px';
43708 if(this.labelWidth < 13 && this.labelmd == 0){
43709 this.labelmd = this.labelWidth;
43711 if(this.labellg > 0){
43712 label.cls += ' col-lg-' + this.labellg;
43713 input.cls += ' col-lg-' + (12 - this.labellg);
43715 if(this.labelmd > 0){
43716 label.cls += ' col-md-' + this.labelmd;
43717 container.cls += ' col-md-' + (12 - this.labelmd);
43719 if(this.labelsm > 0){
43720 label.cls += ' col-sm-' + this.labelsm;
43721 container.cls += ' col-sm-' + (12 - this.labelsm);
43723 if(this.labelxs > 0){
43724 label.cls += ' col-xs-' + this.labelxs;
43725 container.cls += ' col-xs-' + (12 - this.labelxs);
43736 var settings = this;
43738 ['xs','sm','md','lg'].map(function(size){
43739 if (settings[size]) {
43740 cfg.cls += ' col-' + size + '-' + settings[size];
43747 initEvents : function()
43749 this.indicator = this.indicatorEl();
43751 this.initCurrencyEvent();
43753 this.initNumberEvent();
43756 initCurrencyEvent : function()
43759 throw "can not find store for combo";
43762 this.store = Roo.factory(this.store, Roo.data);
43763 this.store.parent = this;
43767 this.triggerEl = this.el.select('.input-group-addon', true).first();
43769 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43774 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43775 _this.list.setWidth(lw);
43778 this.list.on('mouseover', this.onViewOver, this);
43779 this.list.on('mousemove', this.onViewMove, this);
43780 this.list.on('scroll', this.onViewScroll, this);
43783 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43786 this.view = new Roo.View(this.list, this.tpl, {
43787 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43790 this.view.on('click', this.onViewClick, this);
43792 this.store.on('beforeload', this.onBeforeLoad, this);
43793 this.store.on('load', this.onLoad, this);
43794 this.store.on('loadexception', this.onLoadException, this);
43796 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43797 "up" : function(e){
43798 this.inKeyMode = true;
43802 "down" : function(e){
43803 if(!this.isExpanded()){
43804 this.onTriggerClick();
43806 this.inKeyMode = true;
43811 "enter" : function(e){
43814 if(this.fireEvent("specialkey", this, e)){
43815 this.onViewClick(false);
43821 "esc" : function(e){
43825 "tab" : function(e){
43828 if(this.fireEvent("specialkey", this, e)){
43829 this.onViewClick(false);
43837 doRelay : function(foo, bar, hname){
43838 if(hname == 'down' || this.scope.isExpanded()){
43839 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43847 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43851 initNumberEvent : function(e)
43853 this.inputEl().on("keydown" , this.fireKey, this);
43854 this.inputEl().on("focus", this.onFocus, this);
43855 this.inputEl().on("blur", this.onBlur, this);
43857 this.inputEl().relayEvent('keyup', this);
43859 if(this.indicator){
43860 this.indicator.addClass('invisible');
43863 this.originalValue = this.getValue();
43865 if(this.validationEvent == 'keyup'){
43866 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43867 this.inputEl().on('keyup', this.filterValidation, this);
43869 else if(this.validationEvent !== false){
43870 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43873 if(this.selectOnFocus){
43874 this.on("focus", this.preFocus, this);
43877 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43878 this.inputEl().on("keypress", this.filterKeys, this);
43880 this.inputEl().relayEvent('keypress', this);
43883 var allowed = "0123456789";
43885 if(this.allowDecimals){
43886 allowed += this.decimalSeparator;
43889 if(this.allowNegative){
43893 if(this.thousandsDelimiter) {
43897 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43899 var keyPress = function(e){
43901 var k = e.getKey();
43903 var c = e.getCharCode();
43906 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43907 allowed.indexOf(String.fromCharCode(c)) === -1
43913 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43917 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43922 this.inputEl().on("keypress", keyPress, this);
43926 onTriggerClick : function(e)
43933 this.loadNext = false;
43935 if(this.isExpanded()){
43940 this.hasFocus = true;
43942 if(this.triggerAction == 'all') {
43943 this.doQuery(this.allQuery, true);
43947 this.doQuery(this.getRawValue());
43950 getCurrency : function()
43952 var v = this.currencyEl().getValue();
43957 restrictHeight : function()
43959 this.list.alignTo(this.currencyEl(), this.listAlign);
43960 this.list.alignTo(this.currencyEl(), this.listAlign);
43963 onViewClick : function(view, doFocus, el, e)
43965 var index = this.view.getSelectedIndexes()[0];
43967 var r = this.store.getAt(index);
43970 this.onSelect(r, index);
43974 onSelect : function(record, index){
43976 if(this.fireEvent('beforeselect', this, record, index) !== false){
43978 this.setFromCurrencyData(index > -1 ? record.data : false);
43982 this.fireEvent('select', this, record, index);
43986 setFromCurrencyData : function(o)
43990 this.lastCurrency = o;
43992 if (this.currencyField) {
43993 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43995 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43998 this.lastSelectionText = currency;
44000 //setting default currency
44001 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44002 this.setCurrency(this.defaultCurrency);
44006 this.setCurrency(currency);
44009 setFromData : function(o)
44013 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44015 this.setFromCurrencyData(c);
44020 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44022 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44025 this.setValue(value);
44029 setCurrency : function(v)
44031 this.currencyValue = v;
44034 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44039 setValue : function(v)
44041 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44047 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44049 this.inputEl().dom.value = (v == '') ? '' :
44050 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44052 if(!this.allowZero && v === '0') {
44053 this.hiddenEl().dom.value = '';
44054 this.inputEl().dom.value = '';
44061 getRawValue : function()
44063 var v = this.inputEl().getValue();
44068 getValue : function()
44070 return this.fixPrecision(this.parseValue(this.getRawValue()));
44073 parseValue : function(value)
44075 if(this.thousandsDelimiter) {
44077 r = new RegExp(",", "g");
44078 value = value.replace(r, "");
44081 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44082 return isNaN(value) ? '' : value;
44086 fixPrecision : function(value)
44088 if(this.thousandsDelimiter) {
44090 r = new RegExp(",", "g");
44091 value = value.replace(r, "");
44094 var nan = isNaN(value);
44096 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44097 return nan ? '' : value;
44099 return parseFloat(value).toFixed(this.decimalPrecision);
44102 decimalPrecisionFcn : function(v)
44104 return Math.floor(v);
44107 validateValue : function(value)
44109 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44113 var num = this.parseValue(value);
44116 this.markInvalid(String.format(this.nanText, value));
44120 if(num < this.minValue){
44121 this.markInvalid(String.format(this.minText, this.minValue));
44125 if(num > this.maxValue){
44126 this.markInvalid(String.format(this.maxText, this.maxValue));
44133 validate : function()
44135 if(this.disabled || this.allowBlank){
44140 var currency = this.getCurrency();
44142 if(this.validateValue(this.getRawValue()) && currency.length){
44147 this.markInvalid();
44151 getName: function()
44156 beforeBlur : function()
44162 var v = this.parseValue(this.getRawValue());
44169 onBlur : function()
44173 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44174 //this.el.removeClass(this.focusClass);
44177 this.hasFocus = false;
44179 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44183 var v = this.getValue();
44185 if(String(v) !== String(this.startValue)){
44186 this.fireEvent('change', this, v, this.startValue);
44189 this.fireEvent("blur", this);
44192 inputEl : function()
44194 return this.el.select('.roo-money-amount-input', true).first();
44197 currencyEl : function()
44199 return this.el.select('.roo-money-currency-input', true).first();
44202 hiddenEl : function()
44204 return this.el.select('input.hidden-number-input',true).first();
44208 * @class Roo.bootstrap.BezierSignature
44209 * @extends Roo.bootstrap.Component
44210 * Bootstrap BezierSignature class
44211 * This script refer to:
44212 * Title: Signature Pad
44214 * Availability: https://github.com/szimek/signature_pad
44217 * Create a new BezierSignature
44218 * @param {Object} config The config object
44221 Roo.bootstrap.BezierSignature = function(config){
44222 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44228 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44235 mouse_btn_down: true,
44238 * @cfg {int} canvas height
44240 canvas_height: '200px',
44243 * @cfg {float|function} Radius of a single dot.
44248 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44253 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44258 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44263 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44268 * @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.
44270 bg_color: 'rgba(0, 0, 0, 0)',
44273 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44275 dot_color: 'black',
44278 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44280 velocity_filter_weight: 0.7,
44283 * @cfg {function} Callback when stroke begin.
44288 * @cfg {function} Callback when stroke end.
44292 getAutoCreate : function()
44294 var cls = 'roo-signature column';
44297 cls += ' ' + this.cls;
44307 for(var i = 0; i < col_sizes.length; i++) {
44308 if(this[col_sizes[i]]) {
44309 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44319 cls: 'roo-signature-body',
44323 cls: 'roo-signature-body-canvas',
44324 height: this.canvas_height,
44325 width: this.canvas_width
44332 style: 'display: none'
44340 initEvents: function()
44342 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44344 var canvas = this.canvasEl();
44346 // mouse && touch event swapping...
44347 canvas.dom.style.touchAction = 'none';
44348 canvas.dom.style.msTouchAction = 'none';
44350 this.mouse_btn_down = false;
44351 canvas.on('mousedown', this._handleMouseDown, this);
44352 canvas.on('mousemove', this._handleMouseMove, this);
44353 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44355 if (window.PointerEvent) {
44356 canvas.on('pointerdown', this._handleMouseDown, this);
44357 canvas.on('pointermove', this._handleMouseMove, this);
44358 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44361 if ('ontouchstart' in window) {
44362 canvas.on('touchstart', this._handleTouchStart, this);
44363 canvas.on('touchmove', this._handleTouchMove, this);
44364 canvas.on('touchend', this._handleTouchEnd, this);
44367 Roo.EventManager.onWindowResize(this.resize, this, true);
44369 // file input event
44370 this.fileEl().on('change', this.uploadImage, this);
44377 resize: function(){
44379 var canvas = this.canvasEl().dom;
44380 var ctx = this.canvasElCtx();
44381 var img_data = false;
44383 if(canvas.width > 0) {
44384 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44386 // setting canvas width will clean img data
44389 var style = window.getComputedStyle ?
44390 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44392 var padding_left = parseInt(style.paddingLeft) || 0;
44393 var padding_right = parseInt(style.paddingRight) || 0;
44395 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44398 ctx.putImageData(img_data, 0, 0);
44402 _handleMouseDown: function(e)
44404 if (e.browserEvent.which === 1) {
44405 this.mouse_btn_down = true;
44406 this.strokeBegin(e);
44410 _handleMouseMove: function (e)
44412 if (this.mouse_btn_down) {
44413 this.strokeMoveUpdate(e);
44417 _handleMouseUp: function (e)
44419 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44420 this.mouse_btn_down = false;
44425 _handleTouchStart: function (e) {
44427 e.preventDefault();
44428 if (e.browserEvent.targetTouches.length === 1) {
44429 // var touch = e.browserEvent.changedTouches[0];
44430 // this.strokeBegin(touch);
44432 this.strokeBegin(e); // assume e catching the correct xy...
44436 _handleTouchMove: function (e) {
44437 e.preventDefault();
44438 // var touch = event.targetTouches[0];
44439 // _this._strokeMoveUpdate(touch);
44440 this.strokeMoveUpdate(e);
44443 _handleTouchEnd: function (e) {
44444 var wasCanvasTouched = e.target === this.canvasEl().dom;
44445 if (wasCanvasTouched) {
44446 e.preventDefault();
44447 // var touch = event.changedTouches[0];
44448 // _this._strokeEnd(touch);
44453 reset: function () {
44454 this._lastPoints = [];
44455 this._lastVelocity = 0;
44456 this._lastWidth = (this.min_width + this.max_width) / 2;
44457 this.canvasElCtx().fillStyle = this.dot_color;
44460 strokeMoveUpdate: function(e)
44462 this.strokeUpdate(e);
44464 if (this.throttle) {
44465 this.throttleStroke(this.strokeUpdate, this.throttle);
44468 this.strokeUpdate(e);
44472 strokeBegin: function(e)
44474 var newPointGroup = {
44475 color: this.dot_color,
44479 if (typeof this.onBegin === 'function') {
44483 this.curve_data.push(newPointGroup);
44485 this.strokeUpdate(e);
44488 strokeUpdate: function(e)
44490 var rect = this.canvasEl().dom.getBoundingClientRect();
44491 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44492 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44493 var lastPoints = lastPointGroup.points;
44494 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44495 var isLastPointTooClose = lastPoint
44496 ? point.distanceTo(lastPoint) <= this.min_distance
44498 var color = lastPointGroup.color;
44499 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44500 var curve = this.addPoint(point);
44502 this.drawDot({color: color, point: point});
44505 this.drawCurve({color: color, curve: curve});
44515 strokeEnd: function(e)
44517 this.strokeUpdate(e);
44518 if (typeof this.onEnd === 'function') {
44523 addPoint: function (point) {
44524 var _lastPoints = this._lastPoints;
44525 _lastPoints.push(point);
44526 if (_lastPoints.length > 2) {
44527 if (_lastPoints.length === 3) {
44528 _lastPoints.unshift(_lastPoints[0]);
44530 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44531 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44532 _lastPoints.shift();
44538 calculateCurveWidths: function (startPoint, endPoint) {
44539 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44540 (1 - this.velocity_filter_weight) * this._lastVelocity;
44542 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44545 start: this._lastWidth
44548 this._lastVelocity = velocity;
44549 this._lastWidth = newWidth;
44553 drawDot: function (_a) {
44554 var color = _a.color, point = _a.point;
44555 var ctx = this.canvasElCtx();
44556 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44558 this.drawCurveSegment(point.x, point.y, width);
44560 ctx.fillStyle = color;
44564 drawCurve: function (_a) {
44565 var color = _a.color, curve = _a.curve;
44566 var ctx = this.canvasElCtx();
44567 var widthDelta = curve.endWidth - curve.startWidth;
44568 var drawSteps = Math.floor(curve.length()) * 2;
44570 ctx.fillStyle = color;
44571 for (var i = 0; i < drawSteps; i += 1) {
44572 var t = i / drawSteps;
44578 var x = uuu * curve.startPoint.x;
44579 x += 3 * uu * t * curve.control1.x;
44580 x += 3 * u * tt * curve.control2.x;
44581 x += ttt * curve.endPoint.x;
44582 var y = uuu * curve.startPoint.y;
44583 y += 3 * uu * t * curve.control1.y;
44584 y += 3 * u * tt * curve.control2.y;
44585 y += ttt * curve.endPoint.y;
44586 var width = curve.startWidth + ttt * widthDelta;
44587 this.drawCurveSegment(x, y, width);
44593 drawCurveSegment: function (x, y, width) {
44594 var ctx = this.canvasElCtx();
44596 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44597 this.is_empty = false;
44602 var ctx = this.canvasElCtx();
44603 var canvas = this.canvasEl().dom;
44604 ctx.fillStyle = this.bg_color;
44605 ctx.clearRect(0, 0, canvas.width, canvas.height);
44606 ctx.fillRect(0, 0, canvas.width, canvas.height);
44607 this.curve_data = [];
44609 this.is_empty = true;
44614 return this.el.select('input',true).first();
44617 canvasEl: function()
44619 return this.el.select('canvas',true).first();
44622 canvasElCtx: function()
44624 return this.el.select('canvas',true).first().dom.getContext('2d');
44627 getImage: function(type)
44629 if(this.is_empty) {
44634 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44637 drawFromImage: function(img_src)
44639 var img = new Image();
44641 img.onload = function(){
44642 this.canvasElCtx().drawImage(img, 0, 0);
44647 this.is_empty = false;
44650 selectImage: function()
44652 this.fileEl().dom.click();
44655 uploadImage: function(e)
44657 var reader = new FileReader();
44659 reader.onload = function(e){
44660 var img = new Image();
44661 img.onload = function(){
44663 this.canvasElCtx().drawImage(img, 0, 0);
44665 img.src = e.target.result;
44668 reader.readAsDataURL(e.target.files[0]);
44671 // Bezier Point Constructor
44672 Point: (function () {
44673 function Point(x, y, time) {
44676 this.time = time || Date.now();
44678 Point.prototype.distanceTo = function (start) {
44679 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44681 Point.prototype.equals = function (other) {
44682 return this.x === other.x && this.y === other.y && this.time === other.time;
44684 Point.prototype.velocityFrom = function (start) {
44685 return this.time !== start.time
44686 ? this.distanceTo(start) / (this.time - start.time)
44693 // Bezier Constructor
44694 Bezier: (function () {
44695 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44696 this.startPoint = startPoint;
44697 this.control2 = control2;
44698 this.control1 = control1;
44699 this.endPoint = endPoint;
44700 this.startWidth = startWidth;
44701 this.endWidth = endWidth;
44703 Bezier.fromPoints = function (points, widths, scope) {
44704 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44705 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44706 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44708 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44709 var dx1 = s1.x - s2.x;
44710 var dy1 = s1.y - s2.y;
44711 var dx2 = s2.x - s3.x;
44712 var dy2 = s2.y - s3.y;
44713 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44714 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44715 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44716 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44717 var dxm = m1.x - m2.x;
44718 var dym = m1.y - m2.y;
44719 var k = l2 / (l1 + l2);
44720 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44721 var tx = s2.x - cm.x;
44722 var ty = s2.y - cm.y;
44724 c1: new scope.Point(m1.x + tx, m1.y + ty),
44725 c2: new scope.Point(m2.x + tx, m2.y + ty)
44728 Bezier.prototype.length = function () {
44733 for (var i = 0; i <= steps; i += 1) {
44735 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44736 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44738 var xdiff = cx - px;
44739 var ydiff = cy - py;
44740 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44747 Bezier.prototype.point = function (t, start, c1, c2, end) {
44748 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44749 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44750 + (3.0 * c2 * (1.0 - t) * t * t)
44751 + (end * t * t * t);
44756 throttleStroke: function(fn, wait) {
44757 if (wait === void 0) { wait = 250; }
44759 var timeout = null;
44763 var later = function () {
44764 previous = Date.now();
44766 result = fn.apply(storedContext, storedArgs);
44768 storedContext = null;
44772 return function wrapper() {
44774 for (var _i = 0; _i < arguments.length; _i++) {
44775 args[_i] = arguments[_i];
44777 var now = Date.now();
44778 var remaining = wait - (now - previous);
44779 storedContext = this;
44781 if (remaining <= 0 || remaining > wait) {
44783 clearTimeout(timeout);
44787 result = fn.apply(storedContext, storedArgs);
44789 storedContext = null;
44793 else if (!timeout) {
44794 timeout = window.setTimeout(later, remaining);