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)
3582 * @param {Object} config The config object
3586 Roo.bootstrap.Menu = function(config){
3587 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3588 if (this.registerMenu && this.type != 'treeview') {
3589 Roo.bootstrap.MenuMgr.register(this);
3596 * Fires before this menu is displayed (return false to block)
3597 * @param {Roo.menu.Menu} this
3602 * Fires before this menu is hidden (return false to block)
3603 * @param {Roo.menu.Menu} this
3608 * Fires after this menu is displayed
3609 * @param {Roo.menu.Menu} this
3614 * Fires after this menu is hidden
3615 * @param {Roo.menu.Menu} this
3620 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3621 * @param {Roo.menu.Menu} this
3622 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3623 * @param {Roo.EventObject} e
3628 * Fires when the mouse is hovering over this menu
3629 * @param {Roo.menu.Menu} this
3630 * @param {Roo.EventObject} e
3631 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3636 * Fires when the mouse exits this menu
3637 * @param {Roo.menu.Menu} this
3638 * @param {Roo.EventObject} e
3639 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644 * Fires when a menu item contained in this menu is clicked
3645 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3646 * @param {Roo.EventObject} e
3650 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3653 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3657 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3660 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3662 registerMenu : true,
3664 menuItems :false, // stores the menu items..
3674 container_method : 'getDocumentBody',
3677 getChildContainer : function() {
3681 getAutoCreate : function(){
3683 //if (['right'].indexOf(this.align)!==-1) {
3684 // cfg.cn[1].cls += ' pull-right'
3690 cls : 'dropdown-menu' ,
3691 style : 'z-index:1000'
3695 if (this.type === 'submenu') {
3696 cfg.cls = 'submenu active';
3698 if (this.type === 'treeview') {
3699 cfg.cls = 'treeview-menu';
3704 initEvents : function() {
3706 // Roo.log("ADD event");
3707 // Roo.log(this.triggerEl.dom);
3709 this.triggerEl.on('click', this.onTriggerClick, this);
3711 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3714 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3715 // dropdown toggle on the 'a' in BS4?
3716 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3717 } else if (!this.triggerEl.hasClass('no-dropdown-toggle')) {
3718 this.triggerEl.addClass('dropdown-toggle');
3721 this.el.on('touchstart' , this.onTouch, this);
3723 this.el.on('click' , this.onClick, this);
3725 this.el.on("mouseover", this.onMouseOver, this);
3726 this.el.on("mouseout", this.onMouseOut, this);
3730 findTargetItem : function(e)
3732 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3736 //Roo.log(t); Roo.log(t.id);
3738 //Roo.log(this.menuitems);
3739 return this.menuitems.get(t.id);
3741 //return this.items.get(t.menuItemId);
3747 onTouch : function(e)
3749 Roo.log("menu.onTouch");
3750 //e.stopEvent(); this make the user popdown broken
3754 onClick : function(e)
3756 Roo.log("menu.onClick");
3758 var t = this.findTargetItem(e);
3759 if(!t || t.isContainer){
3764 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3765 if(t == this.activeItem && t.shouldDeactivate(e)){
3766 this.activeItem.deactivate();
3767 delete this.activeItem;
3771 this.setActiveItem(t, true);
3779 Roo.log('pass click event');
3783 this.fireEvent("click", this, t, e);
3787 if(!t.href.length || t.href == '#'){
3788 (function() { _this.hide(); }).defer(100);
3793 onMouseOver : function(e){
3794 var t = this.findTargetItem(e);
3797 // if(t.canActivate && !t.disabled){
3798 // this.setActiveItem(t, true);
3802 this.fireEvent("mouseover", this, e, t);
3804 isVisible : function(){
3805 return !this.hidden;
3807 onMouseOut : function(e){
3808 var t = this.findTargetItem(e);
3811 // if(t == this.activeItem && t.shouldDeactivate(e)){
3812 // this.activeItem.deactivate();
3813 // delete this.activeItem;
3816 this.fireEvent("mouseout", this, e, t);
3821 * Displays this menu relative to another element
3822 * @param {String/HTMLElement/Roo.Element} element The element to align to
3823 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3824 * the element (defaults to this.defaultAlign)
3825 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3827 show : function(el, pos, parentMenu)
3829 if (false === this.fireEvent("beforeshow", this)) {
3830 Roo.log("show canceled");
3833 this.parentMenu = parentMenu;
3838 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3841 * Displays this menu at a specific xy position
3842 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3843 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845 showAt : function(xy, parentMenu, /* private: */_e){
3846 this.parentMenu = parentMenu;
3851 this.fireEvent("beforeshow", this);
3852 //xy = this.el.adjustForConstraints(xy);
3856 this.hideMenuItems();
3857 this.hidden = false;
3858 this.triggerEl.addClass('open');
3859 this.el.addClass('show');
3861 // reassign x when hitting right
3862 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3863 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3866 // reassign y when hitting bottom
3867 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3868 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3871 // but the list may align on trigger left or trigger top... should it be a properity?
3873 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3878 this.fireEvent("show", this);
3884 this.doFocus.defer(50, this);
3888 doFocus : function(){
3890 this.focusEl.focus();
3895 * Hides this menu and optionally all parent menus
3896 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3898 hide : function(deep)
3900 if (false === this.fireEvent("beforehide", this)) {
3901 Roo.log("hide canceled");
3904 this.hideMenuItems();
3905 if(this.el && this.isVisible()){
3907 if(this.activeItem){
3908 this.activeItem.deactivate();
3909 this.activeItem = null;
3911 this.triggerEl.removeClass('open');;
3912 this.el.removeClass('show');
3914 this.fireEvent("hide", this);
3916 if(deep === true && this.parentMenu){
3917 this.parentMenu.hide(true);
3921 onTriggerClick : function(e)
3923 Roo.log('trigger click');
3925 var target = e.getTarget();
3927 Roo.log(target.nodeName.toLowerCase());
3929 if(target.nodeName.toLowerCase() === 'i'){
3935 onTriggerPress : function(e)
3937 Roo.log('trigger press');
3938 //Roo.log(e.getTarget());
3939 // Roo.log(this.triggerEl.dom);
3941 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3942 var pel = Roo.get(e.getTarget());
3943 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3944 Roo.log('is treeview or dropdown?');
3948 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3952 if (this.isVisible()) {
3957 this.show(this.triggerEl, '?', false);
3960 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3967 hideMenuItems : function()
3969 Roo.log("hide Menu Items");
3974 this.el.select('.open',true).each(function(aa) {
3976 aa.removeClass('open');
3980 addxtypeChild : function (tree, cntr) {
3981 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3983 this.menuitems.add(comp);
3995 this.getEl().dom.innerHTML = '';
3996 this.menuitems.clear();
4010 * @class Roo.bootstrap.MenuItem
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap MenuItem class
4013 * @cfg {String} html the menu label
4014 * @cfg {String} href the link
4015 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4016 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4017 * @cfg {Boolean} active used on sidebars to highlight active itesm
4018 * @cfg {String} fa favicon to show on left of menu item.
4019 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4023 * Create a new MenuItem
4024 * @param {Object} config The config object
4028 Roo.bootstrap.MenuItem = function(config){
4029 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4034 * The raw click event for the entire grid.
4035 * @param {Roo.bootstrap.MenuItem} this
4036 * @param {Roo.EventObject} e
4042 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4046 preventDefault: false,
4047 isContainer : false,
4051 getAutoCreate : function(){
4053 if(this.isContainer){
4056 cls: 'dropdown-menu-item '
4066 cls : 'dropdown-item',
4071 if (this.fa !== false) {
4074 cls : 'fa fa-' + this.fa
4083 cls: 'dropdown-menu-item',
4086 if (this.parent().type == 'treeview') {
4087 cfg.cls = 'treeview-menu';
4090 cfg.cls += ' active';
4095 anc.href = this.href || cfg.cn[0].href ;
4096 ctag.html = this.html || cfg.cn[0].html ;
4100 initEvents: function()
4102 if (this.parent().type == 'treeview') {
4103 this.el.select('a').on('click', this.onClick, this);
4107 this.menu.parentType = this.xtype;
4108 this.menu.triggerEl = this.el;
4109 this.menu = this.addxtype(Roo.apply({}, this.menu));
4113 onClick : function(e)
4115 Roo.log('item on click ');
4117 if(this.preventDefault){
4120 //this.parent().hideMenuItems();
4122 this.fireEvent('click', this, e);
4141 * @class Roo.bootstrap.MenuSeparator
4142 * @extends Roo.bootstrap.Component
4143 * Bootstrap MenuSeparator class
4146 * Create a new MenuItem
4147 * @param {Object} config The config object
4151 Roo.bootstrap.MenuSeparator = function(config){
4152 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4155 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4157 getAutoCreate : function(){
4176 * @class Roo.bootstrap.Modal
4177 * @extends Roo.bootstrap.Component
4178 * Bootstrap Modal class
4179 * @cfg {String} title Title of dialog
4180 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4181 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4182 * @cfg {Boolean} specificTitle default false
4183 * @cfg {Array} buttons Array of buttons or standard button set..
4184 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4185 * @cfg {Boolean} animate default true
4186 * @cfg {Boolean} allow_close default true
4187 * @cfg {Boolean} fitwindow default false
4188 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4189 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4190 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4191 * @cfg {String} size (sm|lg|xl) default empty
4192 * @cfg {Number} max_width set the max width of modal
4193 * @cfg {Boolean} editableTitle can the title be edited
4198 * Create a new Modal Dialog
4199 * @param {Object} config The config object
4202 Roo.bootstrap.Modal = function(config){
4203 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4208 * The raw btnclick event for the button
4209 * @param {Roo.EventObject} e
4214 * Fire when dialog resize
4215 * @param {Roo.bootstrap.Modal} this
4216 * @param {Roo.EventObject} e
4220 * @event titlechanged
4221 * Fire when the editable title has been changed
4222 * @param {Roo.bootstrap.Modal} this
4223 * @param {Roo.EventObject} value
4225 "titlechanged" : true
4228 this.buttons = this.buttons || [];
4231 this.tmpl = Roo.factory(this.tmpl);
4236 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4238 title : 'test dialog',
4248 specificTitle: false,
4250 buttonPosition: 'right',
4272 editableTitle : false,
4274 onRender : function(ct, position)
4276 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4279 var cfg = Roo.apply({}, this.getAutoCreate());
4282 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4284 //if (!cfg.name.length) {
4288 cfg.cls += ' ' + this.cls;
4291 cfg.style = this.style;
4293 this.el = Roo.get(document.body).createChild(cfg, position);
4295 //var type = this.el.dom.type;
4298 if(this.tabIndex !== undefined){
4299 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4302 this.dialogEl = this.el.select('.modal-dialog',true).first();
4303 this.bodyEl = this.el.select('.modal-body',true).first();
4304 this.closeEl = this.el.select('.modal-header .close', true).first();
4305 this.headerEl = this.el.select('.modal-header',true).first();
4306 this.titleEl = this.el.select('.modal-title',true).first();
4307 this.footerEl = this.el.select('.modal-footer',true).first();
4309 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4311 //this.el.addClass("x-dlg-modal");
4313 if (this.buttons.length) {
4314 Roo.each(this.buttons, function(bb) {
4315 var b = Roo.apply({}, bb);
4316 b.xns = b.xns || Roo.bootstrap;
4317 b.xtype = b.xtype || 'Button';
4318 if (typeof(b.listeners) == 'undefined') {
4319 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4322 var btn = Roo.factory(b);
4324 btn.render(this.getButtonContainer());
4328 // render the children.
4331 if(typeof(this.items) != 'undefined'){
4332 var items = this.items;
4335 for(var i =0;i < items.length;i++) {
4336 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4340 this.items = nitems;
4342 // where are these used - they used to be body/close/footer
4346 //this.el.addClass([this.fieldClass, this.cls]);
4350 getAutoCreate : function()
4352 // we will default to modal-body-overflow - might need to remove or make optional later.
4354 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4355 html : this.html || ''
4360 cls : 'modal-title',
4364 if(this.specificTitle){ // WTF is this?
4369 if (this.allow_close && Roo.bootstrap.version == 3) {
4379 if (this.editableTitle) {
4381 cls: 'form-control roo-editable-title d-none',
4387 if (this.allow_close && Roo.bootstrap.version == 4) {
4397 if(this.size.length){
4398 size = 'modal-' + this.size;
4401 var footer = Roo.bootstrap.version == 3 ?
4403 cls : 'modal-footer',
4407 cls: 'btn-' + this.buttonPosition
4412 { // BS4 uses mr-auto on left buttons....
4413 cls : 'modal-footer'
4424 cls: "modal-dialog " + size,
4427 cls : "modal-content",
4430 cls : 'modal-header',
4445 modal.cls += ' fade';
4451 getChildContainer : function() {
4456 getButtonContainer : function() {
4458 return Roo.bootstrap.version == 4 ?
4459 this.el.select('.modal-footer',true).first()
4460 : this.el.select('.modal-footer div',true).first();
4463 initEvents : function()
4465 if (this.allow_close) {
4466 this.closeEl.on('click', this.hide, this);
4468 Roo.EventManager.onWindowResize(this.resize, this, true);
4469 if (this.editableTitle) {
4470 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4471 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4472 this.headerEditEl.on('keyup', function(e) {
4473 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4474 this.toggleHeaderInput(false)
4477 this.headerEditEl.on('blur', function(e) {
4478 this.toggleHeaderInput(false)
4487 this.maskEl.setSize(
4488 Roo.lib.Dom.getViewWidth(true),
4489 Roo.lib.Dom.getViewHeight(true)
4492 if (this.fitwindow) {
4494 this.dialogEl.setStyle( { 'max-width' : '100%' });
4496 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4497 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4502 if(this.max_width !== 0) {
4504 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4507 this.setSize(w, this.height);
4511 if(this.max_height) {
4512 this.setSize(w,Math.min(
4514 Roo.lib.Dom.getViewportHeight(true) - 60
4520 if(!this.fit_content) {
4521 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4525 this.setSize(w, Math.min(
4527 this.headerEl.getHeight() +
4528 this.footerEl.getHeight() +
4529 this.getChildHeight(this.bodyEl.dom.childNodes),
4530 Roo.lib.Dom.getViewportHeight(true) - 60)
4536 setSize : function(w,h)
4547 if (!this.rendered) {
4550 this.toggleHeaderInput(false);
4551 //this.el.setStyle('display', 'block');
4552 this.el.removeClass('hideing');
4553 this.el.dom.style.display='block';
4555 Roo.get(document.body).addClass('modal-open');
4557 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4560 this.el.addClass('show');
4561 this.el.addClass('in');
4564 this.el.addClass('show');
4565 this.el.addClass('in');
4568 // not sure how we can show data in here..
4570 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4573 Roo.get(document.body).addClass("x-body-masked");
4575 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4576 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4577 this.maskEl.dom.style.display = 'block';
4578 this.maskEl.addClass('show');
4583 this.fireEvent('show', this);
4585 // set zindex here - otherwise it appears to be ignored...
4586 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4589 this.items.forEach( function(e) {
4590 e.layout ? e.layout() : false;
4598 if(this.fireEvent("beforehide", this) !== false){
4600 this.maskEl.removeClass('show');
4602 this.maskEl.dom.style.display = '';
4603 Roo.get(document.body).removeClass("x-body-masked");
4604 this.el.removeClass('in');
4605 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4607 if(this.animate){ // why
4608 this.el.addClass('hideing');
4609 this.el.removeClass('show');
4611 if (!this.el.hasClass('hideing')) {
4612 return; // it's been shown again...
4615 this.el.dom.style.display='';
4617 Roo.get(document.body).removeClass('modal-open');
4618 this.el.removeClass('hideing');
4622 this.el.removeClass('show');
4623 this.el.dom.style.display='';
4624 Roo.get(document.body).removeClass('modal-open');
4627 this.fireEvent('hide', this);
4630 isVisible : function()
4633 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4637 addButton : function(str, cb)
4641 var b = Roo.apply({}, { html : str } );
4642 b.xns = b.xns || Roo.bootstrap;
4643 b.xtype = b.xtype || 'Button';
4644 if (typeof(b.listeners) == 'undefined') {
4645 b.listeners = { click : cb.createDelegate(this) };
4648 var btn = Roo.factory(b);
4650 btn.render(this.getButtonContainer());
4656 setDefaultButton : function(btn)
4658 //this.el.select('.modal-footer').()
4661 resizeTo: function(w,h)
4663 this.dialogEl.setWidth(w);
4665 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4667 this.bodyEl.setHeight(h - diff);
4669 this.fireEvent('resize', this);
4672 setContentSize : function(w, h)
4676 onButtonClick: function(btn,e)
4679 this.fireEvent('btnclick', btn.name, e);
4682 * Set the title of the Dialog
4683 * @param {String} str new Title
4685 setTitle: function(str) {
4686 this.titleEl.dom.innerHTML = str;
4690 * Set the body of the Dialog
4691 * @param {String} str new Title
4693 setBody: function(str) {
4694 this.bodyEl.dom.innerHTML = str;
4697 * Set the body of the Dialog using the template
4698 * @param {Obj} data - apply this data to the template and replace the body contents.
4700 applyBody: function(obj)
4703 Roo.log("Error - using apply Body without a template");
4706 this.tmpl.overwrite(this.bodyEl, obj);
4709 getChildHeight : function(child_nodes)
4713 child_nodes.length == 0
4718 var child_height = 0;
4720 for(var i = 0; i < child_nodes.length; i++) {
4723 * for modal with tabs...
4724 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4726 var layout_childs = child_nodes[i].childNodes;
4728 for(var j = 0; j < layout_childs.length; j++) {
4730 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4732 var layout_body_childs = layout_childs[j].childNodes;
4734 for(var k = 0; k < layout_body_childs.length; k++) {
4736 if(layout_body_childs[k].classList.contains('navbar')) {
4737 child_height += layout_body_childs[k].offsetHeight;
4741 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4743 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4745 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4747 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4748 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4763 child_height += child_nodes[i].offsetHeight;
4764 // Roo.log(child_nodes[i].offsetHeight);
4767 return child_height;
4769 toggleHeaderInput : function(is_edit)
4771 if (!this.editableTitle) {
4772 return; // not editable.
4774 if (is_edit && this.is_header_editing) {
4775 return; // already editing..
4779 this.headerEditEl.dom.value = this.title;
4780 this.headerEditEl.removeClass('d-none');
4781 this.headerEditEl.dom.focus();
4782 this.titleEl.addClass('d-none');
4784 this.is_header_editing = true;
4787 // flip back to not editing.
4788 this.title = this.headerEditEl.dom.value;
4789 this.headerEditEl.addClass('d-none');
4790 this.titleEl.removeClass('d-none');
4791 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4792 this.is_header_editing = false;
4793 this.fireEvent('titlechanged', this, this.title);
4802 Roo.apply(Roo.bootstrap.Modal, {
4804 * Button config that displays a single OK button
4813 * Button config that displays Yes and No buttons
4829 * Button config that displays OK and Cancel buttons
4844 * Button config that displays Yes, No and Cancel buttons
4869 * messagebox - can be used as a replace
4873 * @class Roo.MessageBox
4874 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4878 Roo.Msg.alert('Status', 'Changes saved successfully.');
4880 // Prompt for user data:
4881 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4883 // process text value...
4887 // Show a dialog using config options:
4889 title:'Save Changes?',
4890 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4891 buttons: Roo.Msg.YESNOCANCEL,
4898 Roo.bootstrap.MessageBox = function(){
4899 var dlg, opt, mask, waitTimer;
4900 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4901 var buttons, activeTextEl, bwidth;
4905 var handleButton = function(button){
4907 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4911 var handleHide = function(){
4913 dlg.el.removeClass(opt.cls);
4916 // Roo.TaskMgr.stop(waitTimer);
4917 // waitTimer = null;
4922 var updateButtons = function(b){
4925 buttons["ok"].hide();
4926 buttons["cancel"].hide();
4927 buttons["yes"].hide();
4928 buttons["no"].hide();
4929 dlg.footerEl.hide();
4933 dlg.footerEl.show();
4934 for(var k in buttons){
4935 if(typeof buttons[k] != "function"){
4938 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4939 width += buttons[k].el.getWidth()+15;
4949 var handleEsc = function(d, k, e){
4950 if(opt && opt.closable !== false){
4960 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4961 * @return {Roo.BasicDialog} The BasicDialog element
4963 getDialog : function(){
4965 dlg = new Roo.bootstrap.Modal( {
4968 //constraintoviewport:false,
4970 //collapsible : false,
4975 //buttonAlign:"center",
4976 closeClick : function(){
4977 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4980 handleButton("cancel");
4985 dlg.on("hide", handleHide);
4987 //dlg.addKeyListener(27, handleEsc);
4989 this.buttons = buttons;
4990 var bt = this.buttonText;
4991 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4992 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4993 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4994 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4996 bodyEl = dlg.bodyEl.createChild({
4998 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4999 '<textarea class="roo-mb-textarea"></textarea>' +
5000 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5002 msgEl = bodyEl.dom.firstChild;
5003 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5004 textboxEl.enableDisplayMode();
5005 textboxEl.addKeyListener([10,13], function(){
5006 if(dlg.isVisible() && opt && opt.buttons){
5009 }else if(opt.buttons.yes){
5010 handleButton("yes");
5014 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5015 textareaEl.enableDisplayMode();
5016 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5017 progressEl.enableDisplayMode();
5019 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5020 var pf = progressEl.dom.firstChild;
5022 pp = Roo.get(pf.firstChild);
5023 pp.setHeight(pf.offsetHeight);
5031 * Updates the message box body text
5032 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5033 * the XHTML-compliant non-breaking space character '&#160;')
5034 * @return {Roo.MessageBox} This message box
5036 updateText : function(text)
5038 if(!dlg.isVisible() && !opt.width){
5039 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5040 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5042 msgEl.innerHTML = text || ' ';
5044 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5045 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5047 Math.min(opt.width || cw , this.maxWidth),
5048 Math.max(opt.minWidth || this.minWidth, bwidth)
5051 activeTextEl.setWidth(w);
5053 if(dlg.isVisible()){
5054 dlg.fixedcenter = false;
5056 // to big, make it scroll. = But as usual stupid IE does not support
5059 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5060 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5061 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5063 bodyEl.dom.style.height = '';
5064 bodyEl.dom.style.overflowY = '';
5067 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5069 bodyEl.dom.style.overflowX = '';
5072 dlg.setContentSize(w, bodyEl.getHeight());
5073 if(dlg.isVisible()){
5074 dlg.fixedcenter = true;
5080 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5081 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5082 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5083 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5084 * @return {Roo.MessageBox} This message box
5086 updateProgress : function(value, text){
5088 this.updateText(text);
5091 if (pp) { // weird bug on my firefox - for some reason this is not defined
5092 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5093 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5099 * Returns true if the message box is currently displayed
5100 * @return {Boolean} True if the message box is visible, else false
5102 isVisible : function(){
5103 return dlg && dlg.isVisible();
5107 * Hides the message box if it is displayed
5110 if(this.isVisible()){
5116 * Displays a new message box, or reinitializes an existing message box, based on the config options
5117 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5118 * The following config object properties are supported:
5120 Property Type Description
5121 ---------- --------------- ------------------------------------------------------------------------------------
5122 animEl String/Element An id or Element from which the message box should animate as it opens and
5123 closes (defaults to undefined)
5124 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5125 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5126 closable Boolean False to hide the top-right close button (defaults to true). Note that
5127 progress and wait dialogs will ignore this property and always hide the
5128 close button as they can only be closed programmatically.
5129 cls String A custom CSS class to apply to the message box element
5130 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5131 displayed (defaults to 75)
5132 fn Function A callback function to execute after closing the dialog. The arguments to the
5133 function will be btn (the name of the button that was clicked, if applicable,
5134 e.g. "ok"), and text (the value of the active text field, if applicable).
5135 Progress and wait dialogs will ignore this option since they do not respond to
5136 user actions and can only be closed programmatically, so any required function
5137 should be called by the same code after it closes the dialog.
5138 icon String A CSS class that provides a background image to be used as an icon for
5139 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5140 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5141 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5142 modal Boolean False to allow user interaction with the page while the message box is
5143 displayed (defaults to true)
5144 msg String A string that will replace the existing message box body text (defaults
5145 to the XHTML-compliant non-breaking space character ' ')
5146 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5147 progress Boolean True to display a progress bar (defaults to false)
5148 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5149 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5150 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5151 title String The title text
5152 value String The string value to set into the active textbox element if displayed
5153 wait Boolean True to display a progress bar (defaults to false)
5154 width Number The width of the dialog in pixels
5161 msg: 'Please enter your address:',
5163 buttons: Roo.MessageBox.OKCANCEL,
5166 animEl: 'addAddressBtn'
5169 * @param {Object} config Configuration options
5170 * @return {Roo.MessageBox} This message box
5172 show : function(options)
5175 // this causes nightmares if you show one dialog after another
5176 // especially on callbacks..
5178 if(this.isVisible()){
5181 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5182 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5183 Roo.log("New Dialog Message:" + options.msg )
5184 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5185 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5188 var d = this.getDialog();
5190 d.setTitle(opt.title || " ");
5191 d.closeEl.setDisplayed(opt.closable !== false);
5192 activeTextEl = textboxEl;
5193 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5198 textareaEl.setHeight(typeof opt.multiline == "number" ?
5199 opt.multiline : this.defaultTextHeight);
5200 activeTextEl = textareaEl;
5209 progressEl.setDisplayed(opt.progress === true);
5211 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5213 this.updateProgress(0);
5214 activeTextEl.dom.value = opt.value || "";
5216 dlg.setDefaultButton(activeTextEl);
5218 var bs = opt.buttons;
5222 }else if(bs && bs.yes){
5223 db = buttons["yes"];
5225 dlg.setDefaultButton(db);
5227 bwidth = updateButtons(opt.buttons);
5228 this.updateText(opt.msg);
5230 d.el.addClass(opt.cls);
5232 d.proxyDrag = opt.proxyDrag === true;
5233 d.modal = opt.modal !== false;
5234 d.mask = opt.modal !== false ? mask : false;
5236 // force it to the end of the z-index stack so it gets a cursor in FF
5237 document.body.appendChild(dlg.el.dom);
5238 d.animateTarget = null;
5239 d.show(options.animEl);
5245 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5246 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5247 * and closing the message box when the process is complete.
5248 * @param {String} title The title bar text
5249 * @param {String} msg The message box body text
5250 * @return {Roo.MessageBox} This message box
5252 progress : function(title, msg){
5259 minWidth: this.minProgressWidth,
5266 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5267 * If a callback function is passed it will be called after the user clicks the button, and the
5268 * id of the button that was clicked will be passed as the only parameter to the callback
5269 * (could also be the top-right close button).
5270 * @param {String} title The title bar text
5271 * @param {String} msg The message box body text
5272 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5273 * @param {Object} scope (optional) The scope of the callback function
5274 * @return {Roo.MessageBox} This message box
5276 alert : function(title, msg, fn, scope)
5291 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5292 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5293 * You are responsible for closing the message box when the process is complete.
5294 * @param {String} msg The message box body text
5295 * @param {String} title (optional) The title bar text
5296 * @return {Roo.MessageBox} This message box
5298 wait : function(msg, title){
5309 waitTimer = Roo.TaskMgr.start({
5311 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5319 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5320 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5321 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5322 * @param {String} title The title bar text
5323 * @param {String} msg The message box body text
5324 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5325 * @param {Object} scope (optional) The scope of the callback function
5326 * @return {Roo.MessageBox} This message box
5328 confirm : function(title, msg, fn, scope){
5332 buttons: this.YESNO,
5341 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5342 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5343 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5344 * (could also be the top-right close button) and the text that was entered will be passed as the two
5345 * parameters to the callback.
5346 * @param {String} title The title bar text
5347 * @param {String} msg The message box body text
5348 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5349 * @param {Object} scope (optional) The scope of the callback function
5350 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5351 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5352 * @return {Roo.MessageBox} This message box
5354 prompt : function(title, msg, fn, scope, multiline){
5358 buttons: this.OKCANCEL,
5363 multiline: multiline,
5370 * Button config that displays a single OK button
5375 * Button config that displays Yes and No buttons
5378 YESNO : {yes:true, no:true},
5380 * Button config that displays OK and Cancel buttons
5383 OKCANCEL : {ok:true, cancel:true},
5385 * Button config that displays Yes, No and Cancel buttons
5388 YESNOCANCEL : {yes:true, no:true, cancel:true},
5391 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5394 defaultTextHeight : 75,
5396 * The maximum width in pixels of the message box (defaults to 600)
5401 * The minimum width in pixels of the message box (defaults to 100)
5406 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5407 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5410 minProgressWidth : 250,
5412 * An object containing the default button text strings that can be overriden for localized language support.
5413 * Supported properties are: ok, cancel, yes and no.
5414 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5427 * Shorthand for {@link Roo.MessageBox}
5429 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5430 Roo.Msg = Roo.Msg || Roo.MessageBox;
5439 * @class Roo.bootstrap.Navbar
5440 * @extends Roo.bootstrap.Component
5441 * Bootstrap Navbar class
5444 * Create a new Navbar
5445 * @param {Object} config The config object
5449 Roo.bootstrap.Navbar = function(config){
5450 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5454 * @event beforetoggle
5455 * Fire before toggle the menu
5456 * @param {Roo.EventObject} e
5458 "beforetoggle" : true
5462 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5471 getAutoCreate : function(){
5474 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5478 initEvents :function ()
5480 //Roo.log(this.el.select('.navbar-toggle',true));
5481 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5488 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5490 var size = this.el.getSize();
5491 this.maskEl.setSize(size.width, size.height);
5492 this.maskEl.enableDisplayMode("block");
5501 getChildContainer : function()
5503 if (this.el && this.el.select('.collapse').getCount()) {
5504 return this.el.select('.collapse',true).first();
5519 onToggle : function()
5522 if(this.fireEvent('beforetoggle', this) === false){
5525 var ce = this.el.select('.navbar-collapse',true).first();
5527 if (!ce.hasClass('show')) {
5537 * Expand the navbar pulldown
5539 expand : function ()
5542 var ce = this.el.select('.navbar-collapse',true).first();
5543 if (ce.hasClass('collapsing')) {
5546 ce.dom.style.height = '';
5548 ce.addClass('in'); // old...
5549 ce.removeClass('collapse');
5550 ce.addClass('show');
5551 var h = ce.getHeight();
5553 ce.removeClass('show');
5554 // at this point we should be able to see it..
5555 ce.addClass('collapsing');
5557 ce.setHeight(0); // resize it ...
5558 ce.on('transitionend', function() {
5559 //Roo.log('done transition');
5560 ce.removeClass('collapsing');
5561 ce.addClass('show');
5562 ce.removeClass('collapse');
5564 ce.dom.style.height = '';
5565 }, this, { single: true} );
5567 ce.dom.scrollTop = 0;
5570 * Collapse the navbar pulldown
5572 collapse : function()
5574 var ce = this.el.select('.navbar-collapse',true).first();
5576 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5577 // it's collapsed or collapsing..
5580 ce.removeClass('in'); // old...
5581 ce.setHeight(ce.getHeight());
5582 ce.removeClass('show');
5583 ce.addClass('collapsing');
5585 ce.on('transitionend', function() {
5586 ce.dom.style.height = '';
5587 ce.removeClass('collapsing');
5588 ce.addClass('collapse');
5589 }, this, { single: true} );
5609 * @class Roo.bootstrap.NavSimplebar
5610 * @extends Roo.bootstrap.Navbar
5611 * Bootstrap Sidebar class
5613 * @cfg {Boolean} inverse is inverted color
5615 * @cfg {String} type (nav | pills | tabs)
5616 * @cfg {Boolean} arrangement stacked | justified
5617 * @cfg {String} align (left | right) alignment
5619 * @cfg {Boolean} main (true|false) main nav bar? default false
5620 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5622 * @cfg {String} tag (header|footer|nav|div) default is nav
5624 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5628 * Create a new Sidebar
5629 * @param {Object} config The config object
5633 Roo.bootstrap.NavSimplebar = function(config){
5634 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5637 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5653 getAutoCreate : function(){
5657 tag : this.tag || 'div',
5658 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5660 if (['light','white'].indexOf(this.weight) > -1) {
5661 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5663 cfg.cls += ' bg-' + this.weight;
5666 cfg.cls += ' navbar-inverse';
5670 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5672 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5681 cls: 'nav nav-' + this.xtype,
5687 this.type = this.type || 'nav';
5688 if (['tabs','pills'].indexOf(this.type) != -1) {
5689 cfg.cn[0].cls += ' nav-' + this.type
5693 if (this.type!=='nav') {
5694 Roo.log('nav type must be nav/tabs/pills')
5696 cfg.cn[0].cls += ' navbar-nav'
5702 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5703 cfg.cn[0].cls += ' nav-' + this.arrangement;
5707 if (this.align === 'right') {
5708 cfg.cn[0].cls += ' navbar-right';
5733 * navbar-expand-md fixed-top
5737 * @class Roo.bootstrap.NavHeaderbar
5738 * @extends Roo.bootstrap.NavSimplebar
5739 * Bootstrap Sidebar class
5741 * @cfg {String} brand what is brand
5742 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5743 * @cfg {String} brand_href href of the brand
5744 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5745 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5746 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5747 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5750 * Create a new Sidebar
5751 * @param {Object} config The config object
5755 Roo.bootstrap.NavHeaderbar = function(config){
5756 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5760 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5767 desktopCenter : false,
5770 getAutoCreate : function(){
5773 tag: this.nav || 'nav',
5774 cls: 'navbar navbar-expand-md',
5780 if (this.desktopCenter) {
5781 cn.push({cls : 'container', cn : []});
5789 cls: 'navbar-toggle navbar-toggler',
5790 'data-toggle': 'collapse',
5795 html: 'Toggle navigation'
5799 cls: 'icon-bar navbar-toggler-icon'
5812 cn.push( Roo.bootstrap.version == 4 ? btn : {
5814 cls: 'navbar-header',
5823 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5827 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5829 if (['light','white'].indexOf(this.weight) > -1) {
5830 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5832 cfg.cls += ' bg-' + this.weight;
5835 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5836 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5838 // tag can override this..
5840 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5843 if (this.brand !== '') {
5844 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5845 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5847 href: this.brand_href ? this.brand_href : '#',
5848 cls: 'navbar-brand',
5856 cfg.cls += ' main-nav';
5864 getHeaderChildContainer : function()
5866 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5867 return this.el.select('.navbar-header',true).first();
5870 return this.getChildContainer();
5873 getChildContainer : function()
5876 return this.el.select('.roo-navbar-collapse',true).first();
5881 initEvents : function()
5883 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5885 if (this.autohide) {
5890 Roo.get(document).on('scroll',function(e) {
5891 var ns = Roo.get(document).getScroll().top;
5892 var os = prevScroll;
5896 ft.removeClass('slideDown');
5897 ft.addClass('slideUp');
5900 ft.removeClass('slideUp');
5901 ft.addClass('slideDown');
5922 * @class Roo.bootstrap.NavSidebar
5923 * @extends Roo.bootstrap.Navbar
5924 * Bootstrap Sidebar class
5927 * Create a new Sidebar
5928 * @param {Object} config The config object
5932 Roo.bootstrap.NavSidebar = function(config){
5933 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5936 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5938 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5940 getAutoCreate : function(){
5945 cls: 'sidebar sidebar-nav'
5967 * @class Roo.bootstrap.NavGroup
5968 * @extends Roo.bootstrap.Component
5969 * Bootstrap NavGroup class
5970 * @cfg {String} align (left|right)
5971 * @cfg {Boolean} inverse
5972 * @cfg {String} type (nav|pills|tab) default nav
5973 * @cfg {String} navId - reference Id for navbar.
5974 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5977 * Create a new nav group
5978 * @param {Object} config The config object
5981 Roo.bootstrap.NavGroup = function(config){
5982 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5985 Roo.bootstrap.NavGroup.register(this);
5989 * Fires when the active item changes
5990 * @param {Roo.bootstrap.NavGroup} this
5991 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5992 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5999 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6011 getAutoCreate : function()
6013 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6019 if (Roo.bootstrap.version == 4) {
6020 if (['tabs','pills'].indexOf(this.type) != -1) {
6021 cfg.cls += ' nav-' + this.type;
6023 // trying to remove so header bar can right align top?
6024 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6025 // do not use on header bar...
6026 cfg.cls += ' navbar-nav';
6031 if (['tabs','pills'].indexOf(this.type) != -1) {
6032 cfg.cls += ' nav-' + this.type
6034 if (this.type !== 'nav') {
6035 Roo.log('nav type must be nav/tabs/pills')
6037 cfg.cls += ' navbar-nav'
6041 if (this.parent() && this.parent().sidebar) {
6044 cls: 'dashboard-menu sidebar-menu'
6050 if (this.form === true) {
6053 cls: 'navbar-form form-inline'
6055 //nav navbar-right ml-md-auto
6056 if (this.align === 'right') {
6057 cfg.cls += ' navbar-right ml-md-auto';
6059 cfg.cls += ' navbar-left';
6063 if (this.align === 'right') {
6064 cfg.cls += ' navbar-right ml-md-auto';
6066 cfg.cls += ' mr-auto';
6070 cfg.cls += ' navbar-inverse';
6078 * sets the active Navigation item
6079 * @param {Roo.bootstrap.NavItem} the new current navitem
6081 setActiveItem : function(item)
6084 Roo.each(this.navItems, function(v){
6089 v.setActive(false, true);
6096 item.setActive(true, true);
6097 this.fireEvent('changed', this, item, prev);
6102 * gets the active Navigation item
6103 * @return {Roo.bootstrap.NavItem} the current navitem
6105 getActive : function()
6109 Roo.each(this.navItems, function(v){
6120 indexOfNav : function()
6124 Roo.each(this.navItems, function(v,i){
6135 * adds a Navigation item
6136 * @param {Roo.bootstrap.NavItem} the navitem to add
6138 addItem : function(cfg)
6140 if (this.form && Roo.bootstrap.version == 4) {
6143 var cn = new Roo.bootstrap.NavItem(cfg);
6145 cn.parentId = this.id;
6146 cn.onRender(this.el, null);
6150 * register a Navigation item
6151 * @param {Roo.bootstrap.NavItem} the navitem to add
6153 register : function(item)
6155 this.navItems.push( item);
6156 item.navId = this.navId;
6161 * clear all the Navigation item
6164 clearAll : function()
6167 this.el.dom.innerHTML = '';
6170 getNavItem: function(tabId)
6173 Roo.each(this.navItems, function(e) {
6174 if (e.tabId == tabId) {
6184 setActiveNext : function()
6186 var i = this.indexOfNav(this.getActive());
6187 if (i > this.navItems.length) {
6190 this.setActiveItem(this.navItems[i+1]);
6192 setActivePrev : function()
6194 var i = this.indexOfNav(this.getActive());
6198 this.setActiveItem(this.navItems[i-1]);
6200 clearWasActive : function(except) {
6201 Roo.each(this.navItems, function(e) {
6202 if (e.tabId != except.tabId && e.was_active) {
6203 e.was_active = false;
6210 getWasActive : function ()
6213 Roo.each(this.navItems, function(e) {
6228 Roo.apply(Roo.bootstrap.NavGroup, {
6232 * register a Navigation Group
6233 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6235 register : function(navgrp)
6237 this.groups[navgrp.navId] = navgrp;
6241 * fetch a Navigation Group based on the navigation ID
6242 * @param {string} the navgroup to add
6243 * @returns {Roo.bootstrap.NavGroup} the navgroup
6245 get: function(navId) {
6246 if (typeof(this.groups[navId]) == 'undefined') {
6248 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6250 return this.groups[navId] ;
6265 * @class Roo.bootstrap.NavItem
6266 * @extends Roo.bootstrap.Component
6267 * Bootstrap Navbar.NavItem class
6268 * @cfg {String} href link to
6269 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6270 * @cfg {Boolean} button_outline show and outlined button
6271 * @cfg {String} html content of button
6272 * @cfg {String} badge text inside badge
6273 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6274 * @cfg {String} glyphicon DEPRICATED - use fa
6275 * @cfg {String} icon DEPRICATED - use fa
6276 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6277 * @cfg {Boolean} active Is item active
6278 * @cfg {Boolean} disabled Is item disabled
6279 * @cfg {String} linkcls Link Class
6280 * @cfg {Boolean} preventDefault (true | false) default false
6281 * @cfg {String} tabId the tab that this item activates.
6282 * @cfg {String} tagtype (a|span) render as a href or span?
6283 * @cfg {Boolean} animateRef (true|false) link to element default false
6286 * Create a new Navbar Item
6287 * @param {Object} config The config object
6289 Roo.bootstrap.NavItem = function(config){
6290 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6295 * The raw click event for the entire grid.
6296 * @param {Roo.EventObject} e
6301 * Fires when the active item active state changes
6302 * @param {Roo.bootstrap.NavItem} this
6303 * @param {boolean} state the new state
6309 * Fires when scroll to element
6310 * @param {Roo.bootstrap.NavItem} this
6311 * @param {Object} options
6312 * @param {Roo.EventObject} e
6320 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6329 preventDefault : false,
6337 button_outline : false,
6341 getAutoCreate : function(){
6348 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6351 cfg.cls += ' active' ;
6353 if (this.disabled) {
6354 cfg.cls += ' disabled';
6358 if (this.button_weight.length) {
6359 cfg.tag = this.href ? 'a' : 'button';
6360 cfg.html = this.html || '';
6361 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6363 cfg.href = this.href;
6366 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6368 cfg.cls += " nav-html";
6371 // menu .. should add dropdown-menu class - so no need for carat..
6373 if (this.badge !== '') {
6375 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6380 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6384 href : this.href || "#",
6385 html: this.html || '',
6389 if (this.tagtype == 'a') {
6390 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6394 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6395 } else if (this.fa) {
6396 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6397 } else if(this.glyphicon) {
6398 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6400 cfg.cn[0].cls += " nav-html";
6404 cfg.cn[0].html += " <span class='caret'></span>";
6408 if (this.badge !== '') {
6409 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6417 onRender : function(ct, position)
6419 // Roo.log("Call onRender: " + this.xtype);
6420 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6424 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6425 this.navLink = this.el.select('.nav-link',true).first();
6426 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6431 initEvents: function()
6433 if (typeof (this.menu) != 'undefined') {
6434 this.menu.parentType = this.xtype;
6435 this.menu.triggerEl = this.el;
6436 this.menu = this.addxtype(Roo.apply({}, this.menu));
6439 this.el.on('click', this.onClick, this);
6441 //if(this.tagtype == 'span'){
6442 // this.el.select('span',true).on('click', this.onClick, this);
6445 // at this point parent should be available..
6446 this.parent().register(this);
6449 onClick : function(e)
6451 if (e.getTarget('.dropdown-menu-item')) {
6452 // did you click on a menu itemm.... - then don't trigger onclick..
6457 this.preventDefault ||
6460 Roo.log("NavItem - prevent Default?");
6464 if (this.disabled) {
6468 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6469 if (tg && tg.transition) {
6470 Roo.log("waiting for the transitionend");
6476 //Roo.log("fire event clicked");
6477 if(this.fireEvent('click', this, e) === false){
6481 if(this.tagtype == 'span'){
6485 //Roo.log(this.href);
6486 var ael = this.el.select('a',true).first();
6489 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6490 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6491 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6492 return; // ignore... - it's a 'hash' to another page.
6494 Roo.log("NavItem - prevent Default?");
6496 this.scrollToElement(e);
6500 var p = this.parent();
6502 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6503 if (typeof(p.setActiveItem) !== 'undefined') {
6504 p.setActiveItem(this);
6508 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6509 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6510 // remove the collapsed menu expand...
6511 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6515 isActive: function () {
6518 setActive : function(state, fire, is_was_active)
6520 if (this.active && !state && this.navId) {
6521 this.was_active = true;
6522 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6524 nv.clearWasActive(this);
6528 this.active = state;
6531 this.el.removeClass('active');
6532 this.navLink ? this.navLink.removeClass('active') : false;
6533 } else if (!this.el.hasClass('active')) {
6535 this.el.addClass('active');
6536 if (Roo.bootstrap.version == 4 && this.navLink ) {
6537 this.navLink.addClass('active');
6542 this.fireEvent('changed', this, state);
6545 // show a panel if it's registered and related..
6547 if (!this.navId || !this.tabId || !state || is_was_active) {
6551 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6555 var pan = tg.getPanelByName(this.tabId);
6559 // if we can not flip to new panel - go back to old nav highlight..
6560 if (false == tg.showPanel(pan)) {
6561 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6563 var onav = nv.getWasActive();
6565 onav.setActive(true, false, true);
6574 // this should not be here...
6575 setDisabled : function(state)
6577 this.disabled = state;
6579 this.el.removeClass('disabled');
6580 } else if (!this.el.hasClass('disabled')) {
6581 this.el.addClass('disabled');
6587 * Fetch the element to display the tooltip on.
6588 * @return {Roo.Element} defaults to this.el
6590 tooltipEl : function()
6592 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6595 scrollToElement : function(e)
6597 var c = document.body;
6600 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6602 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6603 c = document.documentElement;
6606 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6612 var o = target.calcOffsetsTo(c);
6619 this.fireEvent('scrollto', this, options, e);
6621 Roo.get(c).scrollTo('top', options.value, true);
6626 * Set the HTML (text content) of the item
6627 * @param {string} html content for the nav item
6629 setHtml : function(html)
6632 this.htmlEl.dom.innerHTML = html;
6644 * <span> icon </span>
6645 * <span> text </span>
6646 * <span>badge </span>
6650 * @class Roo.bootstrap.NavSidebarItem
6651 * @extends Roo.bootstrap.NavItem
6652 * Bootstrap Navbar.NavSidebarItem class
6653 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6654 * {Boolean} open is the menu open
6655 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6656 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6657 * {String} buttonSize (sm|md|lg)the extra classes for the button
6658 * {Boolean} showArrow show arrow next to the text (default true)
6660 * Create a new Navbar Button
6661 * @param {Object} config The config object
6663 Roo.bootstrap.NavSidebarItem = function(config){
6664 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6669 * The raw click event for the entire grid.
6670 * @param {Roo.EventObject} e
6675 * Fires when the active item active state changes
6676 * @param {Roo.bootstrap.NavSidebarItem} this
6677 * @param {boolean} state the new state
6685 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6687 badgeWeight : 'default',
6693 buttonWeight : 'default',
6699 getAutoCreate : function(){
6704 href : this.href || '#',
6710 if(this.buttonView){
6713 href : this.href || '#',
6714 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6727 cfg.cls += ' active';
6730 if (this.disabled) {
6731 cfg.cls += ' disabled';
6734 cfg.cls += ' open x-open';
6737 if (this.glyphicon || this.icon) {
6738 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6739 a.cn.push({ tag : 'i', cls : c }) ;
6742 if(!this.buttonView){
6745 html : this.html || ''
6752 if (this.badge !== '') {
6753 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6759 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6762 a.cls += ' dropdown-toggle treeview' ;
6768 initEvents : function()
6770 if (typeof (this.menu) != 'undefined') {
6771 this.menu.parentType = this.xtype;
6772 this.menu.triggerEl = this.el;
6773 this.menu = this.addxtype(Roo.apply({}, this.menu));
6776 this.el.on('click', this.onClick, this);
6778 if(this.badge !== ''){
6779 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6784 onClick : function(e)
6791 if(this.preventDefault){
6795 this.fireEvent('click', this, e);
6798 disable : function()
6800 this.setDisabled(true);
6805 this.setDisabled(false);
6808 setDisabled : function(state)
6810 if(this.disabled == state){
6814 this.disabled = state;
6817 this.el.addClass('disabled');
6821 this.el.removeClass('disabled');
6826 setActive : function(state)
6828 if(this.active == state){
6832 this.active = state;
6835 this.el.addClass('active');
6839 this.el.removeClass('active');
6844 isActive: function ()
6849 setBadge : function(str)
6855 this.badgeEl.dom.innerHTML = str;
6870 Roo.namespace('Roo.bootstrap.breadcrumb');
6874 * @class Roo.bootstrap.breadcrumb.Nav
6875 * @extends Roo.bootstrap.Component
6876 * Bootstrap Breadcrumb Nav Class
6878 * @children Roo.bootstrap.breadcrumb.Item
6881 * Create a new breadcrumb.Nav
6882 * @param {Object} config The config object
6886 Roo.bootstrap.breadcrumb.Nav = function(config){
6887 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6892 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6894 getAutoCreate : function()
6911 initEvents: function()
6913 this.olEl = this.el.select('ol',true).first();
6915 getChildContainer : function()
6931 * @class Roo.bootstrap.breadcrumb.Nav
6932 * @extends Roo.bootstrap.Component
6933 * Bootstrap Breadcrumb Nav Class
6935 * @children Roo.bootstrap.breadcrumb.Component
6936 * @cfg {String} html the content of the link.
6937 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6938 * @cfg {Boolean} active is it active
6942 * Create a new breadcrumb.Nav
6943 * @param {Object} config The config object
6946 Roo.bootstrap.breadcrumb.Item = function(config){
6947 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6952 * The img click event for the img.
6953 * @param {Roo.EventObject} e
6960 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6965 getAutoCreate : function()
6970 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6972 if (this.href !== false) {
6979 cfg.html = this.html;
6985 initEvents: function()
6988 this.el.select('a', true).first().on('click',this.onClick, this)
6992 onClick : function(e)
6995 this.fireEvent('click',this, e);
7008 * @class Roo.bootstrap.Row
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Row class (contains columns...)
7014 * @param {Object} config The config object
7017 Roo.bootstrap.Row = function(config){
7018 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7021 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7023 getAutoCreate : function(){
7042 * @class Roo.bootstrap.Pagination
7043 * @extends Roo.bootstrap.Component
7044 * Bootstrap Pagination class
7045 * @cfg {String} size xs | sm | md | lg
7046 * @cfg {Boolean} inverse false | true
7049 * Create a new Pagination
7050 * @param {Object} config The config object
7053 Roo.bootstrap.Pagination = function(config){
7054 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7057 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7063 getAutoCreate : function(){
7069 cfg.cls += ' inverse';
7075 cfg.cls += " " + this.cls;
7093 * @class Roo.bootstrap.PaginationItem
7094 * @extends Roo.bootstrap.Component
7095 * Bootstrap PaginationItem class
7096 * @cfg {String} html text
7097 * @cfg {String} href the link
7098 * @cfg {Boolean} preventDefault (true | false) default true
7099 * @cfg {Boolean} active (true | false) default false
7100 * @cfg {Boolean} disabled default false
7104 * Create a new PaginationItem
7105 * @param {Object} config The config object
7109 Roo.bootstrap.PaginationItem = function(config){
7110 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7115 * The raw click event for the entire grid.
7116 * @param {Roo.EventObject} e
7122 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7126 preventDefault: true,
7131 getAutoCreate : function(){
7137 href : this.href ? this.href : '#',
7138 html : this.html ? this.html : ''
7148 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7152 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7158 initEvents: function() {
7160 this.el.on('click', this.onClick, this);
7163 onClick : function(e)
7165 Roo.log('PaginationItem on click ');
7166 if(this.preventDefault){
7174 this.fireEvent('click', this, e);
7190 * @class Roo.bootstrap.Slider
7191 * @extends Roo.bootstrap.Component
7192 * Bootstrap Slider class
7195 * Create a new Slider
7196 * @param {Object} config The config object
7199 Roo.bootstrap.Slider = function(config){
7200 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7203 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7205 getAutoCreate : function(){
7209 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7213 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7225 * Ext JS Library 1.1.1
7226 * Copyright(c) 2006-2007, Ext JS, LLC.
7228 * Originally Released Under LGPL - original licence link has changed is not relivant.
7231 * <script type="text/javascript">
7236 * @class Roo.grid.ColumnModel
7237 * @extends Roo.util.Observable
7238 * This is the default implementation of a ColumnModel used by the Grid. It defines
7239 * the columns in the grid.
7242 var colModel = new Roo.grid.ColumnModel([
7243 {header: "Ticker", width: 60, sortable: true, locked: true},
7244 {header: "Company Name", width: 150, sortable: true},
7245 {header: "Market Cap.", width: 100, sortable: true},
7246 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7247 {header: "Employees", width: 100, sortable: true, resizable: false}
7252 * The config options listed for this class are options which may appear in each
7253 * individual column definition.
7254 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7256 * @param {Object} config An Array of column config objects. See this class's
7257 * config objects for details.
7259 Roo.grid.ColumnModel = function(config){
7261 * The config passed into the constructor
7263 this.config = config;
7266 // if no id, create one
7267 // if the column does not have a dataIndex mapping,
7268 // map it to the order it is in the config
7269 for(var i = 0, len = config.length; i < len; i++){
7271 if(typeof c.dataIndex == "undefined"){
7274 if(typeof c.renderer == "string"){
7275 c.renderer = Roo.util.Format[c.renderer];
7277 if(typeof c.id == "undefined"){
7280 if(c.editor && c.editor.xtype){
7281 c.editor = Roo.factory(c.editor, Roo.grid);
7283 if(c.editor && c.editor.isFormField){
7284 c.editor = new Roo.grid.GridEditor(c.editor);
7286 this.lookup[c.id] = c;
7290 * The width of columns which have no width specified (defaults to 100)
7293 this.defaultWidth = 100;
7296 * Default sortable of columns which have no sortable specified (defaults to false)
7299 this.defaultSortable = false;
7303 * @event widthchange
7304 * Fires when the width of a column changes.
7305 * @param {ColumnModel} this
7306 * @param {Number} columnIndex The column index
7307 * @param {Number} newWidth The new width
7309 "widthchange": true,
7311 * @event headerchange
7312 * Fires when the text of a header changes.
7313 * @param {ColumnModel} this
7314 * @param {Number} columnIndex The column index
7315 * @param {Number} newText The new header text
7317 "headerchange": true,
7319 * @event hiddenchange
7320 * Fires when a column is hidden or "unhidden".
7321 * @param {ColumnModel} this
7322 * @param {Number} columnIndex The column index
7323 * @param {Boolean} hidden true if hidden, false otherwise
7325 "hiddenchange": true,
7327 * @event columnmoved
7328 * Fires when a column is moved.
7329 * @param {ColumnModel} this
7330 * @param {Number} oldIndex
7331 * @param {Number} newIndex
7333 "columnmoved" : true,
7335 * @event columlockchange
7336 * Fires when a column's locked state is changed
7337 * @param {ColumnModel} this
7338 * @param {Number} colIndex
7339 * @param {Boolean} locked true if locked
7341 "columnlockchange" : true
7343 Roo.grid.ColumnModel.superclass.constructor.call(this);
7345 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7347 * @cfg {String} header The header text to display in the Grid view.
7350 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7351 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7352 * specified, the column's index is used as an index into the Record's data Array.
7355 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7356 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7359 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7360 * Defaults to the value of the {@link #defaultSortable} property.
7361 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7364 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7367 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7370 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7373 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7376 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7377 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7378 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7379 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7382 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7385 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7388 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7391 * @cfg {String} cursor (Optional)
7394 * @cfg {String} tooltip (Optional)
7397 * @cfg {Number} xs (Optional)
7400 * @cfg {Number} sm (Optional)
7403 * @cfg {Number} md (Optional)
7406 * @cfg {Number} lg (Optional)
7409 * Returns the id of the column at the specified index.
7410 * @param {Number} index The column index
7411 * @return {String} the id
7413 getColumnId : function(index){
7414 return this.config[index].id;
7418 * Returns the column for a specified id.
7419 * @param {String} id The column id
7420 * @return {Object} the column
7422 getColumnById : function(id){
7423 return this.lookup[id];
7428 * Returns the column for a specified dataIndex.
7429 * @param {String} dataIndex The column dataIndex
7430 * @return {Object|Boolean} the column or false if not found
7432 getColumnByDataIndex: function(dataIndex){
7433 var index = this.findColumnIndex(dataIndex);
7434 return index > -1 ? this.config[index] : false;
7438 * Returns the index for a specified column id.
7439 * @param {String} id The column id
7440 * @return {Number} the index, or -1 if not found
7442 getIndexById : function(id){
7443 for(var i = 0, len = this.config.length; i < len; i++){
7444 if(this.config[i].id == id){
7452 * Returns the index for a specified column dataIndex.
7453 * @param {String} dataIndex The column dataIndex
7454 * @return {Number} the index, or -1 if not found
7457 findColumnIndex : function(dataIndex){
7458 for(var i = 0, len = this.config.length; i < len; i++){
7459 if(this.config[i].dataIndex == dataIndex){
7467 moveColumn : function(oldIndex, newIndex){
7468 var c = this.config[oldIndex];
7469 this.config.splice(oldIndex, 1);
7470 this.config.splice(newIndex, 0, c);
7471 this.dataMap = null;
7472 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7475 isLocked : function(colIndex){
7476 return this.config[colIndex].locked === true;
7479 setLocked : function(colIndex, value, suppressEvent){
7480 if(this.isLocked(colIndex) == value){
7483 this.config[colIndex].locked = value;
7485 this.fireEvent("columnlockchange", this, colIndex, value);
7489 getTotalLockedWidth : function(){
7491 for(var i = 0; i < this.config.length; i++){
7492 if(this.isLocked(i) && !this.isHidden(i)){
7493 this.totalWidth += this.getColumnWidth(i);
7499 getLockedCount : function(){
7500 for(var i = 0, len = this.config.length; i < len; i++){
7501 if(!this.isLocked(i)){
7506 return this.config.length;
7510 * Returns the number of columns.
7513 getColumnCount : function(visibleOnly){
7514 if(visibleOnly === true){
7516 for(var i = 0, len = this.config.length; i < len; i++){
7517 if(!this.isHidden(i)){
7523 return this.config.length;
7527 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7528 * @param {Function} fn
7529 * @param {Object} scope (optional)
7530 * @return {Array} result
7532 getColumnsBy : function(fn, scope){
7534 for(var i = 0, len = this.config.length; i < len; i++){
7535 var c = this.config[i];
7536 if(fn.call(scope||this, c, i) === true){
7544 * Returns true if the specified column is sortable.
7545 * @param {Number} col The column index
7548 isSortable : function(col){
7549 if(typeof this.config[col].sortable == "undefined"){
7550 return this.defaultSortable;
7552 return this.config[col].sortable;
7556 * Returns the rendering (formatting) function defined for the column.
7557 * @param {Number} col The column index.
7558 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7560 getRenderer : function(col){
7561 if(!this.config[col].renderer){
7562 return Roo.grid.ColumnModel.defaultRenderer;
7564 return this.config[col].renderer;
7568 * Sets the rendering (formatting) function for a column.
7569 * @param {Number} col The column index
7570 * @param {Function} fn The function to use to process the cell's raw data
7571 * to return HTML markup for the grid view. The render function is called with
7572 * the following parameters:<ul>
7573 * <li>Data value.</li>
7574 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7575 * <li>css A CSS style string to apply to the table cell.</li>
7576 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7577 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7578 * <li>Row index</li>
7579 * <li>Column index</li>
7580 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7582 setRenderer : function(col, fn){
7583 this.config[col].renderer = fn;
7587 * Returns the width for the specified column.
7588 * @param {Number} col The column index
7591 getColumnWidth : function(col){
7592 return this.config[col].width * 1 || this.defaultWidth;
7596 * Sets the width for a column.
7597 * @param {Number} col The column index
7598 * @param {Number} width The new width
7600 setColumnWidth : function(col, width, suppressEvent){
7601 this.config[col].width = width;
7602 this.totalWidth = null;
7604 this.fireEvent("widthchange", this, col, width);
7609 * Returns the total width of all columns.
7610 * @param {Boolean} includeHidden True to include hidden column widths
7613 getTotalWidth : function(includeHidden){
7614 if(!this.totalWidth){
7615 this.totalWidth = 0;
7616 for(var i = 0, len = this.config.length; i < len; i++){
7617 if(includeHidden || !this.isHidden(i)){
7618 this.totalWidth += this.getColumnWidth(i);
7622 return this.totalWidth;
7626 * Returns the header for the specified column.
7627 * @param {Number} col The column index
7630 getColumnHeader : function(col){
7631 return this.config[col].header;
7635 * Sets the header for a column.
7636 * @param {Number} col The column index
7637 * @param {String} header The new header
7639 setColumnHeader : function(col, header){
7640 this.config[col].header = header;
7641 this.fireEvent("headerchange", this, col, header);
7645 * Returns the tooltip for the specified column.
7646 * @param {Number} col The column index
7649 getColumnTooltip : function(col){
7650 return this.config[col].tooltip;
7653 * Sets the tooltip for a column.
7654 * @param {Number} col The column index
7655 * @param {String} tooltip The new tooltip
7657 setColumnTooltip : function(col, tooltip){
7658 this.config[col].tooltip = tooltip;
7662 * Returns the dataIndex for the specified column.
7663 * @param {Number} col The column index
7666 getDataIndex : function(col){
7667 return this.config[col].dataIndex;
7671 * Sets the dataIndex for a column.
7672 * @param {Number} col The column index
7673 * @param {Number} dataIndex The new dataIndex
7675 setDataIndex : function(col, dataIndex){
7676 this.config[col].dataIndex = dataIndex;
7682 * Returns true if the cell is editable.
7683 * @param {Number} colIndex The column index
7684 * @param {Number} rowIndex The row index - this is nto actually used..?
7687 isCellEditable : function(colIndex, rowIndex){
7688 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7692 * Returns the editor defined for the cell/column.
7693 * return false or null to disable editing.
7694 * @param {Number} colIndex The column index
7695 * @param {Number} rowIndex The row index
7698 getCellEditor : function(colIndex, rowIndex){
7699 return this.config[colIndex].editor;
7703 * Sets if a column is editable.
7704 * @param {Number} col The column index
7705 * @param {Boolean} editable True if the column is editable
7707 setEditable : function(col, editable){
7708 this.config[col].editable = editable;
7713 * Returns true if the column is hidden.
7714 * @param {Number} colIndex The column index
7717 isHidden : function(colIndex){
7718 return this.config[colIndex].hidden;
7723 * Returns true if the column width cannot be changed
7725 isFixed : function(colIndex){
7726 return this.config[colIndex].fixed;
7730 * Returns true if the column can be resized
7733 isResizable : function(colIndex){
7734 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7737 * Sets if a column is hidden.
7738 * @param {Number} colIndex The column index
7739 * @param {Boolean} hidden True if the column is hidden
7741 setHidden : function(colIndex, hidden){
7742 this.config[colIndex].hidden = hidden;
7743 this.totalWidth = null;
7744 this.fireEvent("hiddenchange", this, colIndex, hidden);
7748 * Sets the editor for a column.
7749 * @param {Number} col The column index
7750 * @param {Object} editor The editor object
7752 setEditor : function(col, editor){
7753 this.config[col].editor = editor;
7757 Roo.grid.ColumnModel.defaultRenderer = function(value)
7759 if(typeof value == "object") {
7762 if(typeof value == "string" && value.length < 1){
7766 return String.format("{0}", value);
7769 // Alias for backwards compatibility
7770 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7773 * Ext JS Library 1.1.1
7774 * Copyright(c) 2006-2007, Ext JS, LLC.
7776 * Originally Released Under LGPL - original licence link has changed is not relivant.
7779 * <script type="text/javascript">
7783 * @class Roo.LoadMask
7784 * A simple utility class for generically masking elements while loading data. If the element being masked has
7785 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7786 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7787 * element's UpdateManager load indicator and will be destroyed after the initial load.
7789 * Create a new LoadMask
7790 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7791 * @param {Object} config The config object
7793 Roo.LoadMask = function(el, config){
7794 this.el = Roo.get(el);
7795 Roo.apply(this, config);
7797 this.store.on('beforeload', this.onBeforeLoad, this);
7798 this.store.on('load', this.onLoad, this);
7799 this.store.on('loadexception', this.onLoadException, this);
7800 this.removeMask = false;
7802 var um = this.el.getUpdateManager();
7803 um.showLoadIndicator = false; // disable the default indicator
7804 um.on('beforeupdate', this.onBeforeLoad, this);
7805 um.on('update', this.onLoad, this);
7806 um.on('failure', this.onLoad, this);
7807 this.removeMask = true;
7811 Roo.LoadMask.prototype = {
7813 * @cfg {Boolean} removeMask
7814 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7815 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7819 * The text to display in a centered loading message box (defaults to 'Loading...')
7823 * @cfg {String} msgCls
7824 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7826 msgCls : 'x-mask-loading',
7829 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7835 * Disables the mask to prevent it from being displayed
7837 disable : function(){
7838 this.disabled = true;
7842 * Enables the mask so that it can be displayed
7844 enable : function(){
7845 this.disabled = false;
7848 onLoadException : function()
7852 if (typeof(arguments[3]) != 'undefined') {
7853 Roo.MessageBox.alert("Error loading",arguments[3]);
7857 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7858 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7865 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7870 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7874 onBeforeLoad : function(){
7876 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7881 destroy : function(){
7883 this.store.un('beforeload', this.onBeforeLoad, this);
7884 this.store.un('load', this.onLoad, this);
7885 this.store.un('loadexception', this.onLoadException, this);
7887 var um = this.el.getUpdateManager();
7888 um.un('beforeupdate', this.onBeforeLoad, this);
7889 um.un('update', this.onLoad, this);
7890 um.un('failure', this.onLoad, this);
7901 * @class Roo.bootstrap.Table
7902 * @extends Roo.bootstrap.Component
7903 * Bootstrap Table class
7904 * @cfg {String} cls table class
7905 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7906 * @cfg {String} bgcolor Specifies the background color for a table
7907 * @cfg {Number} border Specifies whether the table cells should have borders or not
7908 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7909 * @cfg {Number} cellspacing Specifies the space between cells
7910 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7911 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7912 * @cfg {String} sortable Specifies that the table should be sortable
7913 * @cfg {String} summary Specifies a summary of the content of a table
7914 * @cfg {Number} width Specifies the width of a table
7915 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7917 * @cfg {boolean} striped Should the rows be alternative striped
7918 * @cfg {boolean} bordered Add borders to the table
7919 * @cfg {boolean} hover Add hover highlighting
7920 * @cfg {boolean} condensed Format condensed
7921 * @cfg {boolean} responsive Format condensed
7922 * @cfg {Boolean} loadMask (true|false) default false
7923 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7924 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7925 * @cfg {Boolean} rowSelection (true|false) default false
7926 * @cfg {Boolean} cellSelection (true|false) default false
7927 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7928 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7929 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7930 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7934 * Create a new Table
7935 * @param {Object} config The config object
7938 Roo.bootstrap.Table = function(config){
7939 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7944 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7945 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7946 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7947 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7949 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7951 this.sm.grid = this;
7952 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7953 this.sm = this.selModel;
7954 this.sm.xmodule = this.xmodule || false;
7957 if (this.cm && typeof(this.cm.config) == 'undefined') {
7958 this.colModel = new Roo.grid.ColumnModel(this.cm);
7959 this.cm = this.colModel;
7960 this.cm.xmodule = this.xmodule || false;
7963 this.store= Roo.factory(this.store, Roo.data);
7964 this.ds = this.store;
7965 this.ds.xmodule = this.xmodule || false;
7968 if (this.footer && this.store) {
7969 this.footer.dataSource = this.ds;
7970 this.footer = Roo.factory(this.footer);
7977 * Fires when a cell is clicked
7978 * @param {Roo.bootstrap.Table} this
7979 * @param {Roo.Element} el
7980 * @param {Number} rowIndex
7981 * @param {Number} columnIndex
7982 * @param {Roo.EventObject} e
7986 * @event celldblclick
7987 * Fires when a cell is double clicked
7988 * @param {Roo.bootstrap.Table} this
7989 * @param {Roo.Element} el
7990 * @param {Number} rowIndex
7991 * @param {Number} columnIndex
7992 * @param {Roo.EventObject} e
7994 "celldblclick" : true,
7997 * Fires when a row is clicked
7998 * @param {Roo.bootstrap.Table} this
7999 * @param {Roo.Element} el
8000 * @param {Number} rowIndex
8001 * @param {Roo.EventObject} e
8005 * @event rowdblclick
8006 * Fires when a row is double clicked
8007 * @param {Roo.bootstrap.Table} this
8008 * @param {Roo.Element} el
8009 * @param {Number} rowIndex
8010 * @param {Roo.EventObject} e
8012 "rowdblclick" : true,
8015 * Fires when a mouseover occur
8016 * @param {Roo.bootstrap.Table} this
8017 * @param {Roo.Element} el
8018 * @param {Number} rowIndex
8019 * @param {Number} columnIndex
8020 * @param {Roo.EventObject} e
8025 * Fires when a mouseout occur
8026 * @param {Roo.bootstrap.Table} this
8027 * @param {Roo.Element} el
8028 * @param {Number} rowIndex
8029 * @param {Number} columnIndex
8030 * @param {Roo.EventObject} e
8035 * Fires when a row is rendered, so you can change add a style to it.
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8041 * @event rowsrendered
8042 * Fires when all the rows have been rendered
8043 * @param {Roo.bootstrap.Table} this
8045 'rowsrendered' : true,
8047 * @event contextmenu
8048 * The raw contextmenu event for the entire grid.
8049 * @param {Roo.EventObject} e
8051 "contextmenu" : true,
8053 * @event rowcontextmenu
8054 * Fires when a row is right clicked
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Number} rowIndex
8057 * @param {Roo.EventObject} e
8059 "rowcontextmenu" : true,
8061 * @event cellcontextmenu
8062 * Fires when a cell is right clicked
8063 * @param {Roo.bootstrap.Table} this
8064 * @param {Number} rowIndex
8065 * @param {Number} cellIndex
8066 * @param {Roo.EventObject} e
8068 "cellcontextmenu" : true,
8070 * @event headercontextmenu
8071 * Fires when a header is right clicked
8072 * @param {Roo.bootstrap.Table} this
8073 * @param {Number} columnIndex
8074 * @param {Roo.EventObject} e
8076 "headercontextmenu" : true
8080 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8106 rowSelection : false,
8107 cellSelection : false,
8110 // Roo.Element - the tbody
8112 // Roo.Element - thead element
8115 container: false, // used by gridpanel...
8121 auto_hide_footer : false,
8123 getAutoCreate : function()
8125 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8132 if (this.scrollBody) {
8133 cfg.cls += ' table-body-fixed';
8136 cfg.cls += ' table-striped';
8140 cfg.cls += ' table-hover';
8142 if (this.bordered) {
8143 cfg.cls += ' table-bordered';
8145 if (this.condensed) {
8146 cfg.cls += ' table-condensed';
8148 if (this.responsive) {
8149 cfg.cls += ' table-responsive';
8153 cfg.cls+= ' ' +this.cls;
8156 // this lot should be simplifed...
8169 ].forEach(function(k) {
8177 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8180 if(this.store || this.cm){
8181 if(this.headerShow){
8182 cfg.cn.push(this.renderHeader());
8185 cfg.cn.push(this.renderBody());
8187 if(this.footerShow){
8188 cfg.cn.push(this.renderFooter());
8190 // where does this come from?
8191 //cfg.cls+= ' TableGrid';
8194 return { cn : [ cfg ] };
8197 initEvents : function()
8199 if(!this.store || !this.cm){
8202 if (this.selModel) {
8203 this.selModel.initEvents();
8207 //Roo.log('initEvents with ds!!!!');
8209 this.mainBody = this.el.select('tbody', true).first();
8210 this.mainHead = this.el.select('thead', true).first();
8211 this.mainFoot = this.el.select('tfoot', true).first();
8217 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8218 e.on('click', _this.sort, _this);
8221 this.mainBody.on("click", this.onClick, this);
8222 this.mainBody.on("dblclick", this.onDblClick, this);
8224 // why is this done????? = it breaks dialogs??
8225 //this.parent().el.setStyle('position', 'relative');
8229 this.footer.parentId = this.id;
8230 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8233 this.el.select('tfoot tr td').first().addClass('hide');
8238 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8241 this.store.on('load', this.onLoad, this);
8242 this.store.on('beforeload', this.onBeforeLoad, this);
8243 this.store.on('update', this.onUpdate, this);
8244 this.store.on('add', this.onAdd, this);
8245 this.store.on("clear", this.clear, this);
8247 this.el.on("contextmenu", this.onContextMenu, this);
8249 this.mainBody.on('scroll', this.onBodyScroll, this);
8251 this.cm.on("headerchange", this.onHeaderChange, this);
8253 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8257 onContextMenu : function(e, t)
8259 this.processEvent("contextmenu", e);
8262 processEvent : function(name, e)
8264 if (name != 'touchstart' ) {
8265 this.fireEvent(name, e);
8268 var t = e.getTarget();
8270 var cell = Roo.get(t);
8276 if(cell.findParent('tfoot', false, true)){
8280 if(cell.findParent('thead', false, true)){
8282 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8283 cell = Roo.get(t).findParent('th', false, true);
8285 Roo.log("failed to find th in thead?");
8286 Roo.log(e.getTarget());
8291 var cellIndex = cell.dom.cellIndex;
8293 var ename = name == 'touchstart' ? 'click' : name;
8294 this.fireEvent("header" + ename, this, cellIndex, e);
8299 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8300 cell = Roo.get(t).findParent('td', false, true);
8302 Roo.log("failed to find th in tbody?");
8303 Roo.log(e.getTarget());
8308 var row = cell.findParent('tr', false, true);
8309 var cellIndex = cell.dom.cellIndex;
8310 var rowIndex = row.dom.rowIndex - 1;
8314 this.fireEvent("row" + name, this, rowIndex, e);
8318 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8324 onMouseover : function(e, el)
8326 var cell = Roo.get(el);
8332 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8333 cell = cell.findParent('td', false, true);
8336 var row = cell.findParent('tr', false, true);
8337 var cellIndex = cell.dom.cellIndex;
8338 var rowIndex = row.dom.rowIndex - 1; // start from 0
8340 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8344 onMouseout : function(e, el)
8346 var cell = Roo.get(el);
8352 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8353 cell = cell.findParent('td', false, true);
8356 var row = cell.findParent('tr', false, true);
8357 var cellIndex = cell.dom.cellIndex;
8358 var rowIndex = row.dom.rowIndex - 1; // start from 0
8360 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8364 onClick : function(e, el)
8366 var cell = Roo.get(el);
8368 if(!cell || (!this.cellSelection && !this.rowSelection)){
8372 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8373 cell = cell.findParent('td', false, true);
8376 if(!cell || typeof(cell) == 'undefined'){
8380 var row = cell.findParent('tr', false, true);
8382 if(!row || typeof(row) == 'undefined'){
8386 var cellIndex = cell.dom.cellIndex;
8387 var rowIndex = this.getRowIndex(row);
8389 // why??? - should these not be based on SelectionModel?
8390 if(this.cellSelection){
8391 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8394 if(this.rowSelection){
8395 this.fireEvent('rowclick', this, row, rowIndex, e);
8401 onDblClick : function(e,el)
8403 var cell = Roo.get(el);
8405 if(!cell || (!this.cellSelection && !this.rowSelection)){
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 if(!cell || typeof(cell) == 'undefined'){
8417 var row = cell.findParent('tr', false, true);
8419 if(!row || typeof(row) == 'undefined'){
8423 var cellIndex = cell.dom.cellIndex;
8424 var rowIndex = this.getRowIndex(row);
8426 if(this.cellSelection){
8427 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8430 if(this.rowSelection){
8431 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8435 sort : function(e,el)
8437 var col = Roo.get(el);
8439 if(!col.hasClass('sortable')){
8443 var sort = col.attr('sort');
8446 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8450 this.store.sortInfo = {field : sort, direction : dir};
8453 Roo.log("calling footer first");
8454 this.footer.onClick('first');
8457 this.store.load({ params : { start : 0 } });
8461 renderHeader : function()
8469 this.totalWidth = 0;
8471 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8473 var config = cm.config[i];
8477 cls : 'x-hcol-' + i,
8479 html: cm.getColumnHeader(i)
8484 if(typeof(config.sortable) != 'undefined' && config.sortable){
8486 c.html = '<i class="glyphicon"></i>' + c.html;
8489 // could use BS4 hidden-..-down
8491 if(typeof(config.lgHeader) != 'undefined'){
8492 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8495 if(typeof(config.mdHeader) != 'undefined'){
8496 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8499 if(typeof(config.smHeader) != 'undefined'){
8500 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8503 if(typeof(config.xsHeader) != 'undefined'){
8504 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8511 if(typeof(config.tooltip) != 'undefined'){
8512 c.tooltip = config.tooltip;
8515 if(typeof(config.colspan) != 'undefined'){
8516 c.colspan = config.colspan;
8519 if(typeof(config.hidden) != 'undefined' && config.hidden){
8520 c.style += ' display:none;';
8523 if(typeof(config.dataIndex) != 'undefined'){
8524 c.sort = config.dataIndex;
8529 if(typeof(config.align) != 'undefined' && config.align.length){
8530 c.style += ' text-align:' + config.align + ';';
8533 if(typeof(config.width) != 'undefined'){
8534 c.style += ' width:' + config.width + 'px;';
8535 this.totalWidth += config.width;
8537 this.totalWidth += 100; // assume minimum of 100 per column?
8540 if(typeof(config.cls) != 'undefined'){
8541 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8544 ['xs','sm','md','lg'].map(function(size){
8546 if(typeof(config[size]) == 'undefined'){
8550 if (!config[size]) { // 0 = hidden
8551 // BS 4 '0' is treated as hide that column and below.
8552 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8556 c.cls += ' col-' + size + '-' + config[size] + (
8557 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8569 renderBody : function()
8579 colspan : this.cm.getColumnCount()
8589 renderFooter : function()
8599 colspan : this.cm.getColumnCount()
8613 // Roo.log('ds onload');
8618 var ds = this.store;
8620 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8621 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8622 if (_this.store.sortInfo) {
8624 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8625 e.select('i', true).addClass(['glyphicon-arrow-up']);
8628 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8629 e.select('i', true).addClass(['glyphicon-arrow-down']);
8634 var tbody = this.mainBody;
8636 if(ds.getCount() > 0){
8637 ds.data.each(function(d,rowIndex){
8638 var row = this.renderRow(cm, ds, rowIndex);
8640 tbody.createChild(row);
8644 if(row.cellObjects.length){
8645 Roo.each(row.cellObjects, function(r){
8646 _this.renderCellObject(r);
8653 var tfoot = this.el.select('tfoot', true).first();
8655 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8657 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8659 var total = this.ds.getTotalCount();
8661 if(this.footer.pageSize < total){
8662 this.mainFoot.show();
8666 Roo.each(this.el.select('tbody td', true).elements, function(e){
8667 e.on('mouseover', _this.onMouseover, _this);
8670 Roo.each(this.el.select('tbody td', true).elements, function(e){
8671 e.on('mouseout', _this.onMouseout, _this);
8673 this.fireEvent('rowsrendered', this);
8679 onUpdate : function(ds,record)
8681 this.refreshRow(record);
8685 onRemove : function(ds, record, index, isUpdate){
8686 if(isUpdate !== true){
8687 this.fireEvent("beforerowremoved", this, index, record);
8689 var bt = this.mainBody.dom;
8691 var rows = this.el.select('tbody > tr', true).elements;
8693 if(typeof(rows[index]) != 'undefined'){
8694 bt.removeChild(rows[index].dom);
8697 // if(bt.rows[index]){
8698 // bt.removeChild(bt.rows[index]);
8701 if(isUpdate !== true){
8702 //this.stripeRows(index);
8703 //this.syncRowHeights(index, index);
8705 this.fireEvent("rowremoved", this, index, record);
8709 onAdd : function(ds, records, rowIndex)
8711 //Roo.log('on Add called');
8712 // - note this does not handle multiple adding very well..
8713 var bt = this.mainBody.dom;
8714 for (var i =0 ; i < records.length;i++) {
8715 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8716 //Roo.log(records[i]);
8717 //Roo.log(this.store.getAt(rowIndex+i));
8718 this.insertRow(this.store, rowIndex + i, false);
8725 refreshRow : function(record){
8726 var ds = this.store, index;
8727 if(typeof record == 'number'){
8729 record = ds.getAt(index);
8731 index = ds.indexOf(record);
8733 return; // should not happen - but seems to
8736 this.insertRow(ds, index, true);
8738 this.onRemove(ds, record, index+1, true);
8740 //this.syncRowHeights(index, index);
8742 this.fireEvent("rowupdated", this, index, record);
8745 insertRow : function(dm, rowIndex, isUpdate){
8748 this.fireEvent("beforerowsinserted", this, rowIndex);
8750 //var s = this.getScrollState();
8751 var row = this.renderRow(this.cm, this.store, rowIndex);
8752 // insert before rowIndex..
8753 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8757 if(row.cellObjects.length){
8758 Roo.each(row.cellObjects, function(r){
8759 _this.renderCellObject(r);
8764 this.fireEvent("rowsinserted", this, rowIndex);
8765 //this.syncRowHeights(firstRow, lastRow);
8766 //this.stripeRows(firstRow);
8773 getRowDom : function(rowIndex)
8775 var rows = this.el.select('tbody > tr', true).elements;
8777 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8780 // returns the object tree for a tr..
8783 renderRow : function(cm, ds, rowIndex)
8785 var d = ds.getAt(rowIndex);
8789 cls : 'x-row-' + rowIndex,
8793 var cellObjects = [];
8795 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8796 var config = cm.config[i];
8798 var renderer = cm.getRenderer(i);
8802 if(typeof(renderer) !== 'undefined'){
8803 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8805 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8806 // and are rendered into the cells after the row is rendered - using the id for the element.
8808 if(typeof(value) === 'object'){
8818 rowIndex : rowIndex,
8823 this.fireEvent('rowclass', this, rowcfg);
8827 cls : rowcfg.rowClass + ' x-col-' + i,
8829 html: (typeof(value) === 'object') ? '' : value
8836 if(typeof(config.colspan) != 'undefined'){
8837 td.colspan = config.colspan;
8840 if(typeof(config.hidden) != 'undefined' && config.hidden){
8841 td.style += ' display:none;';
8844 if(typeof(config.align) != 'undefined' && config.align.length){
8845 td.style += ' text-align:' + config.align + ';';
8847 if(typeof(config.valign) != 'undefined' && config.valign.length){
8848 td.style += ' vertical-align:' + config.valign + ';';
8851 if(typeof(config.width) != 'undefined'){
8852 td.style += ' width:' + config.width + 'px;';
8855 if(typeof(config.cursor) != 'undefined'){
8856 td.style += ' cursor:' + config.cursor + ';';
8859 if(typeof(config.cls) != 'undefined'){
8860 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8863 ['xs','sm','md','lg'].map(function(size){
8865 if(typeof(config[size]) == 'undefined'){
8871 if (!config[size]) { // 0 = hidden
8872 // BS 4 '0' is treated as hide that column and below.
8873 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8877 td.cls += ' col-' + size + '-' + config[size] + (
8878 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8888 row.cellObjects = cellObjects;
8896 onBeforeLoad : function()
8905 this.el.select('tbody', true).first().dom.innerHTML = '';
8908 * Show or hide a row.
8909 * @param {Number} rowIndex to show or hide
8910 * @param {Boolean} state hide
8912 setRowVisibility : function(rowIndex, state)
8914 var bt = this.mainBody.dom;
8916 var rows = this.el.select('tbody > tr', true).elements;
8918 if(typeof(rows[rowIndex]) == 'undefined'){
8921 rows[rowIndex].dom.style.display = state ? '' : 'none';
8925 getSelectionModel : function(){
8927 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8929 return this.selModel;
8932 * Render the Roo.bootstrap object from renderder
8934 renderCellObject : function(r)
8938 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8940 var t = r.cfg.render(r.container);
8943 Roo.each(r.cfg.cn, function(c){
8945 container: t.getChildContainer(),
8948 _this.renderCellObject(child);
8953 getRowIndex : function(row)
8957 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8968 * Returns the grid's underlying element = used by panel.Grid
8969 * @return {Element} The element
8971 getGridEl : function(){
8975 * Forces a resize - used by panel.Grid
8976 * @return {Element} The element
8978 autoSize : function()
8980 //var ctr = Roo.get(this.container.dom.parentElement);
8981 var ctr = Roo.get(this.el.dom);
8983 var thd = this.getGridEl().select('thead',true).first();
8984 var tbd = this.getGridEl().select('tbody', true).first();
8985 var tfd = this.getGridEl().select('tfoot', true).first();
8987 var cw = ctr.getWidth();
8988 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8992 tbd.setWidth(ctr.getWidth());
8993 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8994 // this needs fixing for various usage - currently only hydra job advers I think..
8996 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8998 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9001 cw = Math.max(cw, this.totalWidth);
9002 this.getGridEl().select('tbody tr',true).setWidth(cw);
9004 // resize 'expandable coloumn?
9006 return; // we doe not have a view in this design..
9009 onBodyScroll: function()
9011 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9013 this.mainHead.setStyle({
9014 'position' : 'relative',
9015 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9021 var scrollHeight = this.mainBody.dom.scrollHeight;
9023 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9025 var height = this.mainBody.getHeight();
9027 if(scrollHeight - height == scrollTop) {
9029 var total = this.ds.getTotalCount();
9031 if(this.footer.cursor + this.footer.pageSize < total){
9033 this.footer.ds.load({
9035 start : this.footer.cursor + this.footer.pageSize,
9036 limit : this.footer.pageSize
9046 onHeaderChange : function()
9048 var header = this.renderHeader();
9049 var table = this.el.select('table', true).first();
9051 this.mainHead.remove();
9052 this.mainHead = table.createChild(header, this.mainBody, false);
9055 onHiddenChange : function(colModel, colIndex, hidden)
9057 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9058 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9060 this.CSS.updateRule(thSelector, "display", "");
9061 this.CSS.updateRule(tdSelector, "display", "");
9064 this.CSS.updateRule(thSelector, "display", "none");
9065 this.CSS.updateRule(tdSelector, "display", "none");
9068 this.onHeaderChange();
9072 setColumnWidth: function(col_index, width)
9074 // width = "md-2 xs-2..."
9075 if(!this.colModel.config[col_index]) {
9079 var w = width.split(" ");
9081 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9083 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9086 for(var j = 0; j < w.length; j++) {
9092 var size_cls = w[j].split("-");
9094 if(!Number.isInteger(size_cls[1] * 1)) {
9098 if(!this.colModel.config[col_index][size_cls[0]]) {
9102 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9106 h_row[0].classList.replace(
9107 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9108 "col-"+size_cls[0]+"-"+size_cls[1]
9111 for(var i = 0; i < rows.length; i++) {
9113 var size_cls = w[j].split("-");
9115 if(!Number.isInteger(size_cls[1] * 1)) {
9119 if(!this.colModel.config[col_index][size_cls[0]]) {
9123 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9127 rows[i].classList.replace(
9128 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9129 "col-"+size_cls[0]+"-"+size_cls[1]
9133 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9148 * @class Roo.bootstrap.TableCell
9149 * @extends Roo.bootstrap.Component
9150 * Bootstrap TableCell class
9151 * @cfg {String} html cell contain text
9152 * @cfg {String} cls cell class
9153 * @cfg {String} tag cell tag (td|th) default td
9154 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9155 * @cfg {String} align Aligns the content in a cell
9156 * @cfg {String} axis Categorizes cells
9157 * @cfg {String} bgcolor Specifies the background color of a cell
9158 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9159 * @cfg {Number} colspan Specifies the number of columns a cell should span
9160 * @cfg {String} headers Specifies one or more header cells a cell is related to
9161 * @cfg {Number} height Sets the height of a cell
9162 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9163 * @cfg {Number} rowspan Sets the number of rows a cell should span
9164 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9165 * @cfg {String} valign Vertical aligns the content in a cell
9166 * @cfg {Number} width Specifies the width of a cell
9169 * Create a new TableCell
9170 * @param {Object} config The config object
9173 Roo.bootstrap.TableCell = function(config){
9174 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9177 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9197 getAutoCreate : function(){
9198 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9218 cfg.align=this.align
9224 cfg.bgcolor=this.bgcolor
9227 cfg.charoff=this.charoff
9230 cfg.colspan=this.colspan
9233 cfg.headers=this.headers
9236 cfg.height=this.height
9239 cfg.nowrap=this.nowrap
9242 cfg.rowspan=this.rowspan
9245 cfg.scope=this.scope
9248 cfg.valign=this.valign
9251 cfg.width=this.width
9270 * @class Roo.bootstrap.TableRow
9271 * @extends Roo.bootstrap.Component
9272 * Bootstrap TableRow class
9273 * @cfg {String} cls row class
9274 * @cfg {String} align Aligns the content in a table row
9275 * @cfg {String} bgcolor Specifies a background color for a table row
9276 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9277 * @cfg {String} valign Vertical aligns the content in a table row
9280 * Create a new TableRow
9281 * @param {Object} config The config object
9284 Roo.bootstrap.TableRow = function(config){
9285 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9288 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9296 getAutoCreate : function(){
9297 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9307 cfg.align = this.align;
9310 cfg.bgcolor = this.bgcolor;
9313 cfg.charoff = this.charoff;
9316 cfg.valign = this.valign;
9334 * @class Roo.bootstrap.TableBody
9335 * @extends Roo.bootstrap.Component
9336 * Bootstrap TableBody class
9337 * @cfg {String} cls element class
9338 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9339 * @cfg {String} align Aligns the content inside the element
9340 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9341 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9344 * Create a new TableBody
9345 * @param {Object} config The config object
9348 Roo.bootstrap.TableBody = function(config){
9349 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9352 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9360 getAutoCreate : function(){
9361 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9375 cfg.align = this.align;
9378 cfg.charoff = this.charoff;
9381 cfg.valign = this.valign;
9388 // initEvents : function()
9395 // this.store = Roo.factory(this.store, Roo.data);
9396 // this.store.on('load', this.onLoad, this);
9398 // this.store.load();
9402 // onLoad: function ()
9404 // this.fireEvent('load', this);
9414 * Ext JS Library 1.1.1
9415 * Copyright(c) 2006-2007, Ext JS, LLC.
9417 * Originally Released Under LGPL - original licence link has changed is not relivant.
9420 * <script type="text/javascript">
9423 // as we use this in bootstrap.
9424 Roo.namespace('Roo.form');
9426 * @class Roo.form.Action
9427 * Internal Class used to handle form actions
9429 * @param {Roo.form.BasicForm} el The form element or its id
9430 * @param {Object} config Configuration options
9435 // define the action interface
9436 Roo.form.Action = function(form, options){
9438 this.options = options || {};
9441 * Client Validation Failed
9444 Roo.form.Action.CLIENT_INVALID = 'client';
9446 * Server Validation Failed
9449 Roo.form.Action.SERVER_INVALID = 'server';
9451 * Connect to Server Failed
9454 Roo.form.Action.CONNECT_FAILURE = 'connect';
9456 * Reading Data from Server Failed
9459 Roo.form.Action.LOAD_FAILURE = 'load';
9461 Roo.form.Action.prototype = {
9463 failureType : undefined,
9464 response : undefined,
9468 run : function(options){
9473 success : function(response){
9478 handleResponse : function(response){
9482 // default connection failure
9483 failure : function(response){
9485 this.response = response;
9486 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9487 this.form.afterAction(this, false);
9490 processResponse : function(response){
9491 this.response = response;
9492 if(!response.responseText){
9495 this.result = this.handleResponse(response);
9499 // utility functions used internally
9500 getUrl : function(appendParams){
9501 var url = this.options.url || this.form.url || this.form.el.dom.action;
9503 var p = this.getParams();
9505 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9511 getMethod : function(){
9512 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9515 getParams : function(){
9516 var bp = this.form.baseParams;
9517 var p = this.options.params;
9519 if(typeof p == "object"){
9520 p = Roo.urlEncode(Roo.applyIf(p, bp));
9521 }else if(typeof p == 'string' && bp){
9522 p += '&' + Roo.urlEncode(bp);
9525 p = Roo.urlEncode(bp);
9530 createCallback : function(){
9532 success: this.success,
9533 failure: this.failure,
9535 timeout: (this.form.timeout*1000),
9536 upload: this.form.fileUpload ? this.success : undefined
9541 Roo.form.Action.Submit = function(form, options){
9542 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9545 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9548 haveProgress : false,
9549 uploadComplete : false,
9551 // uploadProgress indicator.
9552 uploadProgress : function()
9554 if (!this.form.progressUrl) {
9558 if (!this.haveProgress) {
9559 Roo.MessageBox.progress("Uploading", "Uploading");
9561 if (this.uploadComplete) {
9562 Roo.MessageBox.hide();
9566 this.haveProgress = true;
9568 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9570 var c = new Roo.data.Connection();
9572 url : this.form.progressUrl,
9577 success : function(req){
9578 //console.log(data);
9582 rdata = Roo.decode(req.responseText)
9584 Roo.log("Invalid data from server..");
9588 if (!rdata || !rdata.success) {
9590 Roo.MessageBox.alert(Roo.encode(rdata));
9593 var data = rdata.data;
9595 if (this.uploadComplete) {
9596 Roo.MessageBox.hide();
9601 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9602 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9605 this.uploadProgress.defer(2000,this);
9608 failure: function(data) {
9609 Roo.log('progress url failed ');
9620 // run get Values on the form, so it syncs any secondary forms.
9621 this.form.getValues();
9623 var o = this.options;
9624 var method = this.getMethod();
9625 var isPost = method == 'POST';
9626 if(o.clientValidation === false || this.form.isValid()){
9628 if (this.form.progressUrl) {
9629 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9630 (new Date() * 1) + '' + Math.random());
9635 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9636 form:this.form.el.dom,
9637 url:this.getUrl(!isPost),
9639 params:isPost ? this.getParams() : null,
9640 isUpload: this.form.fileUpload,
9641 formData : this.form.formData
9644 this.uploadProgress();
9646 }else if (o.clientValidation !== false){ // client validation failed
9647 this.failureType = Roo.form.Action.CLIENT_INVALID;
9648 this.form.afterAction(this, false);
9652 success : function(response)
9654 this.uploadComplete= true;
9655 if (this.haveProgress) {
9656 Roo.MessageBox.hide();
9660 var result = this.processResponse(response);
9661 if(result === true || result.success){
9662 this.form.afterAction(this, true);
9666 this.form.markInvalid(result.errors);
9667 this.failureType = Roo.form.Action.SERVER_INVALID;
9669 this.form.afterAction(this, false);
9671 failure : function(response)
9673 this.uploadComplete= true;
9674 if (this.haveProgress) {
9675 Roo.MessageBox.hide();
9678 this.response = response;
9679 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9680 this.form.afterAction(this, false);
9683 handleResponse : function(response){
9684 if(this.form.errorReader){
9685 var rs = this.form.errorReader.read(response);
9688 for(var i = 0, len = rs.records.length; i < len; i++) {
9689 var r = rs.records[i];
9693 if(errors.length < 1){
9697 success : rs.success,
9703 ret = Roo.decode(response.responseText);
9707 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9717 Roo.form.Action.Load = function(form, options){
9718 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9719 this.reader = this.form.reader;
9722 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9727 Roo.Ajax.request(Roo.apply(
9728 this.createCallback(), {
9729 method:this.getMethod(),
9730 url:this.getUrl(false),
9731 params:this.getParams()
9735 success : function(response){
9737 var result = this.processResponse(response);
9738 if(result === true || !result.success || !result.data){
9739 this.failureType = Roo.form.Action.LOAD_FAILURE;
9740 this.form.afterAction(this, false);
9743 this.form.clearInvalid();
9744 this.form.setValues(result.data);
9745 this.form.afterAction(this, true);
9748 handleResponse : function(response){
9749 if(this.form.reader){
9750 var rs = this.form.reader.read(response);
9751 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9753 success : rs.success,
9757 return Roo.decode(response.responseText);
9761 Roo.form.Action.ACTION_TYPES = {
9762 'load' : Roo.form.Action.Load,
9763 'submit' : Roo.form.Action.Submit
9772 * @class Roo.bootstrap.Form
9773 * @extends Roo.bootstrap.Component
9774 * Bootstrap Form class
9775 * @cfg {String} method GET | POST (default POST)
9776 * @cfg {String} labelAlign top | left (default top)
9777 * @cfg {String} align left | right - for navbars
9778 * @cfg {Boolean} loadMask load mask when submit (default true)
9783 * @param {Object} config The config object
9787 Roo.bootstrap.Form = function(config){
9789 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9791 Roo.bootstrap.Form.popover.apply();
9795 * @event clientvalidation
9796 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9797 * @param {Form} this
9798 * @param {Boolean} valid true if the form has passed client-side validation
9800 clientvalidation: true,
9802 * @event beforeaction
9803 * Fires before any action is performed. Return false to cancel the action.
9804 * @param {Form} this
9805 * @param {Action} action The action to be performed
9809 * @event actionfailed
9810 * Fires when an action fails.
9811 * @param {Form} this
9812 * @param {Action} action The action that failed
9814 actionfailed : true,
9816 * @event actioncomplete
9817 * Fires when an action is completed.
9818 * @param {Form} this
9819 * @param {Action} action The action that completed
9821 actioncomplete : true
9825 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9828 * @cfg {String} method
9829 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9834 * The URL to use for form actions if one isn't supplied in the action options.
9837 * @cfg {Boolean} fileUpload
9838 * Set to true if this form is a file upload.
9842 * @cfg {Object} baseParams
9843 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9847 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9851 * @cfg {Sting} align (left|right) for navbar forms
9856 activeAction : null,
9859 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9860 * element by passing it or its id or mask the form itself by passing in true.
9863 waitMsgTarget : false,
9868 * @cfg {Boolean} errorMask (true|false) default false
9873 * @cfg {Number} maskOffset Default 100
9878 * @cfg {Boolean} maskBody
9882 getAutoCreate : function(){
9886 method : this.method || 'POST',
9887 id : this.id || Roo.id(),
9890 if (this.parent().xtype.match(/^Nav/)) {
9891 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9895 if (this.labelAlign == 'left' ) {
9896 cfg.cls += ' form-horizontal';
9902 initEvents : function()
9904 this.el.on('submit', this.onSubmit, this);
9905 // this was added as random key presses on the form where triggering form submit.
9906 this.el.on('keypress', function(e) {
9907 if (e.getCharCode() != 13) {
9910 // we might need to allow it for textareas.. and some other items.
9911 // check e.getTarget().
9913 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9917 Roo.log("keypress blocked");
9925 onSubmit : function(e){
9930 * Returns true if client-side validation on the form is successful.
9933 isValid : function(){
9934 var items = this.getItems();
9938 items.each(function(f){
9944 Roo.log('invalid field: ' + f.name);
9948 if(!target && f.el.isVisible(true)){
9954 if(this.errorMask && !valid){
9955 Roo.bootstrap.Form.popover.mask(this, target);
9962 * Returns true if any fields in this form have changed since their original load.
9965 isDirty : function(){
9967 var items = this.getItems();
9968 items.each(function(f){
9978 * Performs a predefined action (submit or load) or custom actions you define on this form.
9979 * @param {String} actionName The name of the action type
9980 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9981 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9982 * accept other config options):
9984 Property Type Description
9985 ---------------- --------------- ----------------------------------------------------------------------------------
9986 url String The url for the action (defaults to the form's url)
9987 method String The form method to use (defaults to the form's method, or POST if not defined)
9988 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9989 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9990 validate the form on the client (defaults to false)
9992 * @return {BasicForm} this
9994 doAction : function(action, options){
9995 if(typeof action == 'string'){
9996 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9998 if(this.fireEvent('beforeaction', this, action) !== false){
9999 this.beforeAction(action);
10000 action.run.defer(100, action);
10006 beforeAction : function(action){
10007 var o = action.options;
10012 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10014 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10017 // not really supported yet.. ??
10019 //if(this.waitMsgTarget === true){
10020 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10021 //}else if(this.waitMsgTarget){
10022 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10023 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10025 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10031 afterAction : function(action, success){
10032 this.activeAction = null;
10033 var o = action.options;
10038 Roo.get(document.body).unmask();
10044 //if(this.waitMsgTarget === true){
10045 // this.el.unmask();
10046 //}else if(this.waitMsgTarget){
10047 // this.waitMsgTarget.unmask();
10049 // Roo.MessageBox.updateProgress(1);
10050 // Roo.MessageBox.hide();
10057 Roo.callback(o.success, o.scope, [this, action]);
10058 this.fireEvent('actioncomplete', this, action);
10062 // failure condition..
10063 // we have a scenario where updates need confirming.
10064 // eg. if a locking scenario exists..
10065 // we look for { errors : { needs_confirm : true }} in the response.
10067 (typeof(action.result) != 'undefined') &&
10068 (typeof(action.result.errors) != 'undefined') &&
10069 (typeof(action.result.errors.needs_confirm) != 'undefined')
10072 Roo.log("not supported yet");
10075 Roo.MessageBox.confirm(
10076 "Change requires confirmation",
10077 action.result.errorMsg,
10082 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10092 Roo.callback(o.failure, o.scope, [this, action]);
10093 // show an error message if no failed handler is set..
10094 if (!this.hasListener('actionfailed')) {
10095 Roo.log("need to add dialog support");
10097 Roo.MessageBox.alert("Error",
10098 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10099 action.result.errorMsg :
10100 "Saving Failed, please check your entries or try again"
10105 this.fireEvent('actionfailed', this, action);
10110 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10111 * @param {String} id The value to search for
10114 findField : function(id){
10115 var items = this.getItems();
10116 var field = items.get(id);
10118 items.each(function(f){
10119 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10126 return field || null;
10129 * Mark fields in this form invalid in bulk.
10130 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10131 * @return {BasicForm} this
10133 markInvalid : function(errors){
10134 if(errors instanceof Array){
10135 for(var i = 0, len = errors.length; i < len; i++){
10136 var fieldError = errors[i];
10137 var f = this.findField(fieldError.id);
10139 f.markInvalid(fieldError.msg);
10145 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10146 field.markInvalid(errors[id]);
10150 //Roo.each(this.childForms || [], function (f) {
10151 // f.markInvalid(errors);
10158 * Set values for fields in this form in bulk.
10159 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10160 * @return {BasicForm} this
10162 setValues : function(values){
10163 if(values instanceof Array){ // array of objects
10164 for(var i = 0, len = values.length; i < len; i++){
10166 var f = this.findField(v.id);
10168 f.setValue(v.value);
10169 if(this.trackResetOnLoad){
10170 f.originalValue = f.getValue();
10174 }else{ // object hash
10177 if(typeof values[id] != 'function' && (field = this.findField(id))){
10179 if (field.setFromData &&
10180 field.valueField &&
10181 field.displayField &&
10182 // combos' with local stores can
10183 // be queried via setValue()
10184 // to set their value..
10185 (field.store && !field.store.isLocal)
10189 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10190 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10191 field.setFromData(sd);
10193 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10195 field.setFromData(values);
10198 field.setValue(values[id]);
10202 if(this.trackResetOnLoad){
10203 field.originalValue = field.getValue();
10209 //Roo.each(this.childForms || [], function (f) {
10210 // f.setValues(values);
10217 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10218 * they are returned as an array.
10219 * @param {Boolean} asString
10222 getValues : function(asString){
10223 //if (this.childForms) {
10224 // copy values from the child forms
10225 // Roo.each(this.childForms, function (f) {
10226 // this.setValues(f.getValues());
10232 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10233 if(asString === true){
10236 return Roo.urlDecode(fs);
10240 * Returns the fields in this form as an object with key/value pairs.
10241 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10244 getFieldValues : function(with_hidden)
10246 var items = this.getItems();
10248 items.each(function(f){
10250 if (!f.getName()) {
10254 var v = f.getValue();
10256 if (f.inputType =='radio') {
10257 if (typeof(ret[f.getName()]) == 'undefined') {
10258 ret[f.getName()] = ''; // empty..
10261 if (!f.el.dom.checked) {
10265 v = f.el.dom.value;
10269 if(f.xtype == 'MoneyField'){
10270 ret[f.currencyName] = f.getCurrency();
10273 // not sure if this supported any more..
10274 if ((typeof(v) == 'object') && f.getRawValue) {
10275 v = f.getRawValue() ; // dates..
10277 // combo boxes where name != hiddenName...
10278 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10279 ret[f.name] = f.getRawValue();
10281 ret[f.getName()] = v;
10288 * Clears all invalid messages in this form.
10289 * @return {BasicForm} this
10291 clearInvalid : function(){
10292 var items = this.getItems();
10294 items.each(function(f){
10302 * Resets this form.
10303 * @return {BasicForm} this
10305 reset : function(){
10306 var items = this.getItems();
10307 items.each(function(f){
10311 Roo.each(this.childForms || [], function (f) {
10319 getItems : function()
10321 var r=new Roo.util.MixedCollection(false, function(o){
10322 return o.id || (o.id = Roo.id());
10324 var iter = function(el) {
10331 Roo.each(el.items,function(e) {
10340 hideFields : function(items)
10342 Roo.each(items, function(i){
10344 var f = this.findField(i);
10355 showFields : function(items)
10357 Roo.each(items, function(i){
10359 var f = this.findField(i);
10372 Roo.apply(Roo.bootstrap.Form, {
10388 intervalID : false,
10394 if(this.isApplied){
10399 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10400 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10401 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10402 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10405 this.maskEl.top.enableDisplayMode("block");
10406 this.maskEl.left.enableDisplayMode("block");
10407 this.maskEl.bottom.enableDisplayMode("block");
10408 this.maskEl.right.enableDisplayMode("block");
10410 this.toolTip = new Roo.bootstrap.Tooltip({
10411 cls : 'roo-form-error-popover',
10413 'left' : ['r-l', [-2,0], 'right'],
10414 'right' : ['l-r', [2,0], 'left'],
10415 'bottom' : ['tl-bl', [0,2], 'top'],
10416 'top' : [ 'bl-tl', [0,-2], 'bottom']
10420 this.toolTip.render(Roo.get(document.body));
10422 this.toolTip.el.enableDisplayMode("block");
10424 Roo.get(document.body).on('click', function(){
10428 Roo.get(document.body).on('touchstart', function(){
10432 this.isApplied = true
10435 mask : function(form, target)
10439 this.target = target;
10441 if(!this.form.errorMask || !target.el){
10445 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10447 Roo.log(scrollable);
10449 var ot = this.target.el.calcOffsetsTo(scrollable);
10451 var scrollTo = ot[1] - this.form.maskOffset;
10453 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10455 scrollable.scrollTo('top', scrollTo);
10457 var box = this.target.el.getBox();
10459 var zIndex = Roo.bootstrap.Modal.zIndex++;
10462 this.maskEl.top.setStyle('position', 'absolute');
10463 this.maskEl.top.setStyle('z-index', zIndex);
10464 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10465 this.maskEl.top.setLeft(0);
10466 this.maskEl.top.setTop(0);
10467 this.maskEl.top.show();
10469 this.maskEl.left.setStyle('position', 'absolute');
10470 this.maskEl.left.setStyle('z-index', zIndex);
10471 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10472 this.maskEl.left.setLeft(0);
10473 this.maskEl.left.setTop(box.y - this.padding);
10474 this.maskEl.left.show();
10476 this.maskEl.bottom.setStyle('position', 'absolute');
10477 this.maskEl.bottom.setStyle('z-index', zIndex);
10478 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10479 this.maskEl.bottom.setLeft(0);
10480 this.maskEl.bottom.setTop(box.bottom + this.padding);
10481 this.maskEl.bottom.show();
10483 this.maskEl.right.setStyle('position', 'absolute');
10484 this.maskEl.right.setStyle('z-index', zIndex);
10485 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10486 this.maskEl.right.setLeft(box.right + this.padding);
10487 this.maskEl.right.setTop(box.y - this.padding);
10488 this.maskEl.right.show();
10490 this.toolTip.bindEl = this.target.el;
10492 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10494 var tip = this.target.blankText;
10496 if(this.target.getValue() !== '' ) {
10498 if (this.target.invalidText.length) {
10499 tip = this.target.invalidText;
10500 } else if (this.target.regexText.length){
10501 tip = this.target.regexText;
10505 this.toolTip.show(tip);
10507 this.intervalID = window.setInterval(function() {
10508 Roo.bootstrap.Form.popover.unmask();
10511 window.onwheel = function(){ return false;};
10513 (function(){ this.isMasked = true; }).defer(500, this);
10517 unmask : function()
10519 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10523 this.maskEl.top.setStyle('position', 'absolute');
10524 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10525 this.maskEl.top.hide();
10527 this.maskEl.left.setStyle('position', 'absolute');
10528 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10529 this.maskEl.left.hide();
10531 this.maskEl.bottom.setStyle('position', 'absolute');
10532 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10533 this.maskEl.bottom.hide();
10535 this.maskEl.right.setStyle('position', 'absolute');
10536 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10537 this.maskEl.right.hide();
10539 this.toolTip.hide();
10541 this.toolTip.el.hide();
10543 window.onwheel = function(){ return true;};
10545 if(this.intervalID){
10546 window.clearInterval(this.intervalID);
10547 this.intervalID = false;
10550 this.isMasked = false;
10560 * Ext JS Library 1.1.1
10561 * Copyright(c) 2006-2007, Ext JS, LLC.
10563 * Originally Released Under LGPL - original licence link has changed is not relivant.
10566 * <script type="text/javascript">
10569 * @class Roo.form.VTypes
10570 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10573 Roo.form.VTypes = function(){
10574 // closure these in so they are only created once.
10575 var alpha = /^[a-zA-Z_]+$/;
10576 var alphanum = /^[a-zA-Z0-9_]+$/;
10577 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10578 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10580 // All these messages and functions are configurable
10583 * The function used to validate email addresses
10584 * @param {String} value The email address
10586 'email' : function(v){
10587 return email.test(v);
10590 * The error text to display when the email validation function returns false
10593 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10595 * The keystroke filter mask to be applied on email input
10598 'emailMask' : /[a-z0-9_\.\-@]/i,
10601 * The function used to validate URLs
10602 * @param {String} value The URL
10604 'url' : function(v){
10605 return url.test(v);
10608 * The error text to display when the url validation function returns false
10611 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10614 * The function used to validate alpha values
10615 * @param {String} value The value
10617 'alpha' : function(v){
10618 return alpha.test(v);
10621 * The error text to display when the alpha validation function returns false
10624 'alphaText' : 'This field should only contain letters and _',
10626 * The keystroke filter mask to be applied on alpha input
10629 'alphaMask' : /[a-z_]/i,
10632 * The function used to validate alphanumeric values
10633 * @param {String} value The value
10635 'alphanum' : function(v){
10636 return alphanum.test(v);
10639 * The error text to display when the alphanumeric validation function returns false
10642 'alphanumText' : 'This field should only contain letters, numbers and _',
10644 * The keystroke filter mask to be applied on alphanumeric input
10647 'alphanumMask' : /[a-z0-9_]/i
10657 * @class Roo.bootstrap.Input
10658 * @extends Roo.bootstrap.Component
10659 * Bootstrap Input class
10660 * @cfg {Boolean} disabled is it disabled
10661 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10662 * @cfg {String} name name of the input
10663 * @cfg {string} fieldLabel - the label associated
10664 * @cfg {string} placeholder - placeholder to put in text.
10665 * @cfg {string} before - input group add on before
10666 * @cfg {string} after - input group add on after
10667 * @cfg {string} size - (lg|sm) or leave empty..
10668 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10669 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10670 * @cfg {Number} md colspan out of 12 for computer-sized screens
10671 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10672 * @cfg {string} value default value of the input
10673 * @cfg {Number} labelWidth set the width of label
10674 * @cfg {Number} labellg set the width of label (1-12)
10675 * @cfg {Number} labelmd set the width of label (1-12)
10676 * @cfg {Number} labelsm set the width of label (1-12)
10677 * @cfg {Number} labelxs set the width of label (1-12)
10678 * @cfg {String} labelAlign (top|left)
10679 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10680 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10681 * @cfg {String} indicatorpos (left|right) default left
10682 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10683 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10684 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10686 * @cfg {String} align (left|center|right) Default left
10687 * @cfg {Boolean} forceFeedback (true|false) Default false
10690 * Create a new Input
10691 * @param {Object} config The config object
10694 Roo.bootstrap.Input = function(config){
10696 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10701 * Fires when this field receives input focus.
10702 * @param {Roo.form.Field} this
10707 * Fires when this field loses input focus.
10708 * @param {Roo.form.Field} this
10712 * @event specialkey
10713 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10714 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10715 * @param {Roo.form.Field} this
10716 * @param {Roo.EventObject} e The event object
10721 * Fires just before the field blurs if the field value has changed.
10722 * @param {Roo.form.Field} this
10723 * @param {Mixed} newValue The new value
10724 * @param {Mixed} oldValue The original value
10729 * Fires after the field has been marked as invalid.
10730 * @param {Roo.form.Field} this
10731 * @param {String} msg The validation message
10736 * Fires after the field has been validated with no errors.
10737 * @param {Roo.form.Field} this
10742 * Fires after the key up
10743 * @param {Roo.form.Field} this
10744 * @param {Roo.EventObject} e The event Object
10749 * Fires after the user pastes into input
10750 * @param {Roo.form.Field} this
10751 * @param {Roo.EventObject} e The event Object
10757 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10759 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10760 automatic validation (defaults to "keyup").
10762 validationEvent : "keyup",
10764 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10766 validateOnBlur : true,
10768 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10770 validationDelay : 250,
10772 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10774 focusClass : "x-form-focus", // not needed???
10778 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10780 invalidClass : "has-warning",
10783 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10785 validClass : "has-success",
10788 * @cfg {Boolean} hasFeedback (true|false) default true
10790 hasFeedback : true,
10793 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10795 invalidFeedbackClass : "glyphicon-warning-sign",
10798 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10800 validFeedbackClass : "glyphicon-ok",
10803 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10805 selectOnFocus : false,
10808 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10812 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10817 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10819 disableKeyFilter : false,
10822 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10826 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10830 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10832 blankText : "Please complete this mandatory field",
10835 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10839 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10841 maxLength : Number.MAX_VALUE,
10843 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10845 minLengthText : "The minimum length for this field is {0}",
10847 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10849 maxLengthText : "The maximum length for this field is {0}",
10853 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10854 * If available, this function will be called only after the basic validators all return true, and will be passed the
10855 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10859 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10860 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10861 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10865 * @cfg {String} regexText -- Depricated - use Invalid Text
10870 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10876 autocomplete: false,
10880 inputType : 'text',
10883 placeholder: false,
10888 preventMark: false,
10889 isFormField : true,
10892 labelAlign : false,
10895 formatedValue : false,
10896 forceFeedback : false,
10898 indicatorpos : 'left',
10908 parentLabelAlign : function()
10911 while (parent.parent()) {
10912 parent = parent.parent();
10913 if (typeof(parent.labelAlign) !='undefined') {
10914 return parent.labelAlign;
10921 getAutoCreate : function()
10923 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10929 if(this.inputType != 'hidden'){
10930 cfg.cls = 'form-group' //input-group
10936 type : this.inputType,
10937 value : this.value,
10938 cls : 'form-control',
10939 placeholder : this.placeholder || '',
10940 autocomplete : this.autocomplete || 'new-password'
10942 if (this.inputType == 'file') {
10943 input.style = 'overflow:hidden'; // why not in CSS?
10946 if(this.capture.length){
10947 input.capture = this.capture;
10950 if(this.accept.length){
10951 input.accept = this.accept + "/*";
10955 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10958 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10959 input.maxLength = this.maxLength;
10962 if (this.disabled) {
10963 input.disabled=true;
10966 if (this.readOnly) {
10967 input.readonly=true;
10971 input.name = this.name;
10975 input.cls += ' input-' + this.size;
10979 ['xs','sm','md','lg'].map(function(size){
10980 if (settings[size]) {
10981 cfg.cls += ' col-' + size + '-' + settings[size];
10985 var inputblock = input;
10989 cls: 'glyphicon form-control-feedback'
10992 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10995 cls : 'has-feedback',
11003 if (this.before || this.after) {
11006 cls : 'input-group',
11010 if (this.before && typeof(this.before) == 'string') {
11012 inputblock.cn.push({
11014 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11018 if (this.before && typeof(this.before) == 'object') {
11019 this.before = Roo.factory(this.before);
11021 inputblock.cn.push({
11023 cls : 'roo-input-before input-group-prepend input-group-' +
11024 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11028 inputblock.cn.push(input);
11030 if (this.after && typeof(this.after) == 'string') {
11031 inputblock.cn.push({
11033 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11037 if (this.after && typeof(this.after) == 'object') {
11038 this.after = Roo.factory(this.after);
11040 inputblock.cn.push({
11042 cls : 'roo-input-after input-group-append input-group-' +
11043 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11047 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11048 inputblock.cls += ' has-feedback';
11049 inputblock.cn.push(feedback);
11054 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11055 tooltip : 'This field is required'
11057 if (this.allowBlank ) {
11058 indicator.style = this.allowBlank ? ' display:none' : '';
11060 if (align ==='left' && this.fieldLabel.length) {
11062 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11069 cls : 'control-label col-form-label',
11070 html : this.fieldLabel
11081 var labelCfg = cfg.cn[1];
11082 var contentCfg = cfg.cn[2];
11084 if(this.indicatorpos == 'right'){
11089 cls : 'control-label col-form-label',
11093 html : this.fieldLabel
11107 labelCfg = cfg.cn[0];
11108 contentCfg = cfg.cn[1];
11112 if(this.labelWidth > 12){
11113 labelCfg.style = "width: " + this.labelWidth + 'px';
11116 if(this.labelWidth < 13 && this.labelmd == 0){
11117 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11120 if(this.labellg > 0){
11121 labelCfg.cls += ' col-lg-' + this.labellg;
11122 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11125 if(this.labelmd > 0){
11126 labelCfg.cls += ' col-md-' + this.labelmd;
11127 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11130 if(this.labelsm > 0){
11131 labelCfg.cls += ' col-sm-' + this.labelsm;
11132 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11135 if(this.labelxs > 0){
11136 labelCfg.cls += ' col-xs-' + this.labelxs;
11137 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11141 } else if ( this.fieldLabel.length) {
11148 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11149 tooltip : 'This field is required',
11150 style : this.allowBlank ? ' display:none' : ''
11154 //cls : 'input-group-addon',
11155 html : this.fieldLabel
11163 if(this.indicatorpos == 'right'){
11168 //cls : 'input-group-addon',
11169 html : this.fieldLabel
11174 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11175 tooltip : 'This field is required',
11176 style : this.allowBlank ? ' display:none' : ''
11196 if (this.parentType === 'Navbar' && this.parent().bar) {
11197 cfg.cls += ' navbar-form';
11200 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11201 // on BS4 we do this only if not form
11202 cfg.cls += ' navbar-form';
11210 * return the real input element.
11212 inputEl: function ()
11214 return this.el.select('input.form-control',true).first();
11217 tooltipEl : function()
11219 return this.inputEl();
11222 indicatorEl : function()
11224 if (Roo.bootstrap.version == 4) {
11225 return false; // not enabled in v4 yet.
11228 var indicator = this.el.select('i.roo-required-indicator',true).first();
11238 setDisabled : function(v)
11240 var i = this.inputEl().dom;
11242 i.removeAttribute('disabled');
11246 i.setAttribute('disabled','true');
11248 initEvents : function()
11251 this.inputEl().on("keydown" , this.fireKey, this);
11252 this.inputEl().on("focus", this.onFocus, this);
11253 this.inputEl().on("blur", this.onBlur, this);
11255 this.inputEl().relayEvent('keyup', this);
11256 this.inputEl().relayEvent('paste', this);
11258 this.indicator = this.indicatorEl();
11260 if(this.indicator){
11261 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11264 // reference to original value for reset
11265 this.originalValue = this.getValue();
11266 //Roo.form.TextField.superclass.initEvents.call(this);
11267 if(this.validationEvent == 'keyup'){
11268 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11269 this.inputEl().on('keyup', this.filterValidation, this);
11271 else if(this.validationEvent !== false){
11272 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11275 if(this.selectOnFocus){
11276 this.on("focus", this.preFocus, this);
11279 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11280 this.inputEl().on("keypress", this.filterKeys, this);
11282 this.inputEl().relayEvent('keypress', this);
11285 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11286 this.el.on("click", this.autoSize, this);
11289 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11290 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11293 if (typeof(this.before) == 'object') {
11294 this.before.render(this.el.select('.roo-input-before',true).first());
11296 if (typeof(this.after) == 'object') {
11297 this.after.render(this.el.select('.roo-input-after',true).first());
11300 this.inputEl().on('change', this.onChange, this);
11303 filterValidation : function(e){
11304 if(!e.isNavKeyPress()){
11305 this.validationTask.delay(this.validationDelay);
11309 * Validates the field value
11310 * @return {Boolean} True if the value is valid, else false
11312 validate : function(){
11313 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11314 if(this.disabled || this.validateValue(this.getRawValue())){
11319 this.markInvalid();
11325 * Validates a value according to the field's validation rules and marks the field as invalid
11326 * if the validation fails
11327 * @param {Mixed} value The value to validate
11328 * @return {Boolean} True if the value is valid, else false
11330 validateValue : function(value)
11332 if(this.getVisibilityEl().hasClass('hidden')){
11336 if(value.length < 1) { // if it's blank
11337 if(this.allowBlank){
11343 if(value.length < this.minLength){
11346 if(value.length > this.maxLength){
11350 var vt = Roo.form.VTypes;
11351 if(!vt[this.vtype](value, this)){
11355 if(typeof this.validator == "function"){
11356 var msg = this.validator(value);
11360 if (typeof(msg) == 'string') {
11361 this.invalidText = msg;
11365 if(this.regex && !this.regex.test(value)){
11373 fireKey : function(e){
11374 //Roo.log('field ' + e.getKey());
11375 if(e.isNavKeyPress()){
11376 this.fireEvent("specialkey", this, e);
11379 focus : function (selectText){
11381 this.inputEl().focus();
11382 if(selectText === true){
11383 this.inputEl().dom.select();
11389 onFocus : function(){
11390 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11391 // this.el.addClass(this.focusClass);
11393 if(!this.hasFocus){
11394 this.hasFocus = true;
11395 this.startValue = this.getValue();
11396 this.fireEvent("focus", this);
11400 beforeBlur : Roo.emptyFn,
11404 onBlur : function(){
11406 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11407 //this.el.removeClass(this.focusClass);
11409 this.hasFocus = false;
11410 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11413 var v = this.getValue();
11414 if(String(v) !== String(this.startValue)){
11415 this.fireEvent('change', this, v, this.startValue);
11417 this.fireEvent("blur", this);
11420 onChange : function(e)
11422 var v = this.getValue();
11423 if(String(v) !== String(this.startValue)){
11424 this.fireEvent('change', this, v, this.startValue);
11430 * Resets the current field value to the originally loaded value and clears any validation messages
11432 reset : function(){
11433 this.setValue(this.originalValue);
11437 * Returns the name of the field
11438 * @return {Mixed} name The name field
11440 getName: function(){
11444 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11445 * @return {Mixed} value The field value
11447 getValue : function(){
11449 var v = this.inputEl().getValue();
11454 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11455 * @return {Mixed} value The field value
11457 getRawValue : function(){
11458 var v = this.inputEl().getValue();
11464 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11465 * @param {Mixed} value The value to set
11467 setRawValue : function(v){
11468 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11471 selectText : function(start, end){
11472 var v = this.getRawValue();
11474 start = start === undefined ? 0 : start;
11475 end = end === undefined ? v.length : end;
11476 var d = this.inputEl().dom;
11477 if(d.setSelectionRange){
11478 d.setSelectionRange(start, end);
11479 }else if(d.createTextRange){
11480 var range = d.createTextRange();
11481 range.moveStart("character", start);
11482 range.moveEnd("character", v.length-end);
11489 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11490 * @param {Mixed} value The value to set
11492 setValue : function(v){
11495 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11501 processValue : function(value){
11502 if(this.stripCharsRe){
11503 var newValue = value.replace(this.stripCharsRe, '');
11504 if(newValue !== value){
11505 this.setRawValue(newValue);
11512 preFocus : function(){
11514 if(this.selectOnFocus){
11515 this.inputEl().dom.select();
11518 filterKeys : function(e){
11519 var k = e.getKey();
11520 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11523 var c = e.getCharCode(), cc = String.fromCharCode(c);
11524 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11527 if(!this.maskRe.test(cc)){
11532 * Clear any invalid styles/messages for this field
11534 clearInvalid : function(){
11536 if(!this.el || this.preventMark){ // not rendered
11541 this.el.removeClass([this.invalidClass, 'is-invalid']);
11543 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11545 var feedback = this.el.select('.form-control-feedback', true).first();
11548 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11553 if(this.indicator){
11554 this.indicator.removeClass('visible');
11555 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11558 this.fireEvent('valid', this);
11562 * Mark this field as valid
11564 markValid : function()
11566 if(!this.el || this.preventMark){ // not rendered...
11570 this.el.removeClass([this.invalidClass, this.validClass]);
11571 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11573 var feedback = this.el.select('.form-control-feedback', true).first();
11576 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11579 if(this.indicator){
11580 this.indicator.removeClass('visible');
11581 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11589 if(this.allowBlank && !this.getRawValue().length){
11592 if (Roo.bootstrap.version == 3) {
11593 this.el.addClass(this.validClass);
11595 this.inputEl().addClass('is-valid');
11598 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11600 var feedback = this.el.select('.form-control-feedback', true).first();
11603 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11604 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11609 this.fireEvent('valid', this);
11613 * Mark this field as invalid
11614 * @param {String} msg The validation message
11616 markInvalid : function(msg)
11618 if(!this.el || this.preventMark){ // not rendered
11622 this.el.removeClass([this.invalidClass, this.validClass]);
11623 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11625 var feedback = this.el.select('.form-control-feedback', true).first();
11628 this.el.select('.form-control-feedback', true).first().removeClass(
11629 [this.invalidFeedbackClass, this.validFeedbackClass]);
11636 if(this.allowBlank && !this.getRawValue().length){
11640 if(this.indicator){
11641 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11642 this.indicator.addClass('visible');
11644 if (Roo.bootstrap.version == 3) {
11645 this.el.addClass(this.invalidClass);
11647 this.inputEl().addClass('is-invalid');
11652 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11654 var feedback = this.el.select('.form-control-feedback', true).first();
11657 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11659 if(this.getValue().length || this.forceFeedback){
11660 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11667 this.fireEvent('invalid', this, msg);
11670 SafariOnKeyDown : function(event)
11672 // this is a workaround for a password hang bug on chrome/ webkit.
11673 if (this.inputEl().dom.type != 'password') {
11677 var isSelectAll = false;
11679 if(this.inputEl().dom.selectionEnd > 0){
11680 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11682 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11683 event.preventDefault();
11688 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11690 event.preventDefault();
11691 // this is very hacky as keydown always get's upper case.
11693 var cc = String.fromCharCode(event.getCharCode());
11694 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11698 adjustWidth : function(tag, w){
11699 tag = tag.toLowerCase();
11700 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11701 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11702 if(tag == 'input'){
11705 if(tag == 'textarea'){
11708 }else if(Roo.isOpera){
11709 if(tag == 'input'){
11712 if(tag == 'textarea'){
11720 setFieldLabel : function(v)
11722 if(!this.rendered){
11726 if(this.indicatorEl()){
11727 var ar = this.el.select('label > span',true);
11729 if (ar.elements.length) {
11730 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11731 this.fieldLabel = v;
11735 var br = this.el.select('label',true);
11737 if(br.elements.length) {
11738 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11739 this.fieldLabel = v;
11743 Roo.log('Cannot Found any of label > span || label in input');
11747 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11748 this.fieldLabel = v;
11763 * @class Roo.bootstrap.TextArea
11764 * @extends Roo.bootstrap.Input
11765 * Bootstrap TextArea class
11766 * @cfg {Number} cols Specifies the visible width of a text area
11767 * @cfg {Number} rows Specifies the visible number of lines in a text area
11768 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11769 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11770 * @cfg {string} html text
11773 * Create a new TextArea
11774 * @param {Object} config The config object
11777 Roo.bootstrap.TextArea = function(config){
11778 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11792 getAutoCreate : function(){
11794 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11800 if(this.inputType != 'hidden'){
11801 cfg.cls = 'form-group' //input-group
11809 value : this.value || '',
11810 html: this.html || '',
11811 cls : 'form-control',
11812 placeholder : this.placeholder || ''
11816 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11817 input.maxLength = this.maxLength;
11821 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11825 input.cols = this.cols;
11828 if (this.readOnly) {
11829 input.readonly = true;
11833 input.name = this.name;
11837 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11841 ['xs','sm','md','lg'].map(function(size){
11842 if (settings[size]) {
11843 cfg.cls += ' col-' + size + '-' + settings[size];
11847 var inputblock = input;
11849 if(this.hasFeedback && !this.allowBlank){
11853 cls: 'glyphicon form-control-feedback'
11857 cls : 'has-feedback',
11866 if (this.before || this.after) {
11869 cls : 'input-group',
11873 inputblock.cn.push({
11875 cls : 'input-group-addon',
11880 inputblock.cn.push(input);
11882 if(this.hasFeedback && !this.allowBlank){
11883 inputblock.cls += ' has-feedback';
11884 inputblock.cn.push(feedback);
11888 inputblock.cn.push({
11890 cls : 'input-group-addon',
11897 if (align ==='left' && this.fieldLabel.length) {
11902 cls : 'control-label',
11903 html : this.fieldLabel
11914 if(this.labelWidth > 12){
11915 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11918 if(this.labelWidth < 13 && this.labelmd == 0){
11919 this.labelmd = this.labelWidth;
11922 if(this.labellg > 0){
11923 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11924 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11927 if(this.labelmd > 0){
11928 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11929 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11932 if(this.labelsm > 0){
11933 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11934 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11937 if(this.labelxs > 0){
11938 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11939 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11942 } else if ( this.fieldLabel.length) {
11947 //cls : 'input-group-addon',
11948 html : this.fieldLabel
11966 if (this.disabled) {
11967 input.disabled=true;
11974 * return the real textarea element.
11976 inputEl: function ()
11978 return this.el.select('textarea.form-control',true).first();
11982 * Clear any invalid styles/messages for this field
11984 clearInvalid : function()
11987 if(!this.el || this.preventMark){ // not rendered
11991 var label = this.el.select('label', true).first();
11992 var icon = this.el.select('i.fa-star', true).first();
11997 this.el.removeClass( this.validClass);
11998 this.inputEl().removeClass('is-invalid');
12000 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12002 var feedback = this.el.select('.form-control-feedback', true).first();
12005 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12010 this.fireEvent('valid', this);
12014 * Mark this field as valid
12016 markValid : function()
12018 if(!this.el || this.preventMark){ // not rendered
12022 this.el.removeClass([this.invalidClass, this.validClass]);
12023 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12025 var feedback = this.el.select('.form-control-feedback', true).first();
12028 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12031 if(this.disabled || this.allowBlank){
12035 var label = this.el.select('label', true).first();
12036 var icon = this.el.select('i.fa-star', true).first();
12041 if (Roo.bootstrap.version == 3) {
12042 this.el.addClass(this.validClass);
12044 this.inputEl().addClass('is-valid');
12048 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12050 var feedback = this.el.select('.form-control-feedback', true).first();
12053 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12054 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12059 this.fireEvent('valid', this);
12063 * Mark this field as invalid
12064 * @param {String} msg The validation message
12066 markInvalid : function(msg)
12068 if(!this.el || this.preventMark){ // not rendered
12072 this.el.removeClass([this.invalidClass, this.validClass]);
12073 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12075 var feedback = this.el.select('.form-control-feedback', true).first();
12078 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12081 if(this.disabled || this.allowBlank){
12085 var label = this.el.select('label', true).first();
12086 var icon = this.el.select('i.fa-star', true).first();
12088 if(!this.getValue().length && label && !icon){
12089 this.el.createChild({
12091 cls : 'text-danger fa fa-lg fa-star',
12092 tooltip : 'This field is required',
12093 style : 'margin-right:5px;'
12097 if (Roo.bootstrap.version == 3) {
12098 this.el.addClass(this.invalidClass);
12100 this.inputEl().addClass('is-invalid');
12103 // fixme ... this may be depricated need to test..
12104 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12106 var feedback = this.el.select('.form-control-feedback', true).first();
12109 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12111 if(this.getValue().length || this.forceFeedback){
12112 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12119 this.fireEvent('invalid', this, msg);
12127 * trigger field - base class for combo..
12132 * @class Roo.bootstrap.TriggerField
12133 * @extends Roo.bootstrap.Input
12134 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12135 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12136 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12137 * for which you can provide a custom implementation. For example:
12139 var trigger = new Roo.bootstrap.TriggerField();
12140 trigger.onTriggerClick = myTriggerFn;
12141 trigger.applyTo('my-field');
12144 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12145 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12146 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12147 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12148 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12151 * Create a new TriggerField.
12152 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12153 * to the base TextField)
12155 Roo.bootstrap.TriggerField = function(config){
12156 this.mimicing = false;
12157 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12160 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12162 * @cfg {String} triggerClass A CSS class to apply to the trigger
12165 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12170 * @cfg {Boolean} removable (true|false) special filter default false
12174 /** @cfg {Boolean} grow @hide */
12175 /** @cfg {Number} growMin @hide */
12176 /** @cfg {Number} growMax @hide */
12182 autoSize: Roo.emptyFn,
12186 deferHeight : true,
12189 actionMode : 'wrap',
12194 getAutoCreate : function(){
12196 var align = this.labelAlign || this.parentLabelAlign();
12201 cls: 'form-group' //input-group
12208 type : this.inputType,
12209 cls : 'form-control',
12210 autocomplete: 'new-password',
12211 placeholder : this.placeholder || ''
12215 input.name = this.name;
12218 input.cls += ' input-' + this.size;
12221 if (this.disabled) {
12222 input.disabled=true;
12225 var inputblock = input;
12227 if(this.hasFeedback && !this.allowBlank){
12231 cls: 'glyphicon form-control-feedback'
12234 if(this.removable && !this.editable ){
12236 cls : 'has-feedback',
12242 cls : 'roo-combo-removable-btn close'
12249 cls : 'has-feedback',
12258 if(this.removable && !this.editable ){
12260 cls : 'roo-removable',
12266 cls : 'roo-combo-removable-btn close'
12273 if (this.before || this.after) {
12276 cls : 'input-group',
12280 inputblock.cn.push({
12282 cls : 'input-group-addon input-group-prepend input-group-text',
12287 inputblock.cn.push(input);
12289 if(this.hasFeedback && !this.allowBlank){
12290 inputblock.cls += ' has-feedback';
12291 inputblock.cn.push(feedback);
12295 inputblock.cn.push({
12297 cls : 'input-group-addon input-group-append input-group-text',
12306 var ibwrap = inputblock;
12311 cls: 'roo-select2-choices',
12315 cls: 'roo-select2-search-field',
12327 cls: 'roo-select2-container input-group',
12332 cls: 'form-hidden-field'
12338 if(!this.multiple && this.showToggleBtn){
12344 if (this.caret != false) {
12347 cls: 'fa fa-' + this.caret
12354 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12356 Roo.bootstrap.version == 3 ? caret : '',
12359 cls: 'combobox-clear',
12373 combobox.cls += ' roo-select2-container-multi';
12377 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12378 tooltip : 'This field is required'
12380 if (Roo.bootstrap.version == 4) {
12383 style : 'display:none'
12388 if (align ==='left' && this.fieldLabel.length) {
12390 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12397 cls : 'control-label',
12398 html : this.fieldLabel
12410 var labelCfg = cfg.cn[1];
12411 var contentCfg = cfg.cn[2];
12413 if(this.indicatorpos == 'right'){
12418 cls : 'control-label',
12422 html : this.fieldLabel
12436 labelCfg = cfg.cn[0];
12437 contentCfg = cfg.cn[1];
12440 if(this.labelWidth > 12){
12441 labelCfg.style = "width: " + this.labelWidth + 'px';
12444 if(this.labelWidth < 13 && this.labelmd == 0){
12445 this.labelmd = this.labelWidth;
12448 if(this.labellg > 0){
12449 labelCfg.cls += ' col-lg-' + this.labellg;
12450 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12453 if(this.labelmd > 0){
12454 labelCfg.cls += ' col-md-' + this.labelmd;
12455 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12458 if(this.labelsm > 0){
12459 labelCfg.cls += ' col-sm-' + this.labelsm;
12460 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12463 if(this.labelxs > 0){
12464 labelCfg.cls += ' col-xs-' + this.labelxs;
12465 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12468 } else if ( this.fieldLabel.length) {
12469 // Roo.log(" label");
12474 //cls : 'input-group-addon',
12475 html : this.fieldLabel
12483 if(this.indicatorpos == 'right'){
12491 html : this.fieldLabel
12505 // Roo.log(" no label && no align");
12512 ['xs','sm','md','lg'].map(function(size){
12513 if (settings[size]) {
12514 cfg.cls += ' col-' + size + '-' + settings[size];
12525 onResize : function(w, h){
12526 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12527 // if(typeof w == 'number'){
12528 // var x = w - this.trigger.getWidth();
12529 // this.inputEl().setWidth(this.adjustWidth('input', x));
12530 // this.trigger.setStyle('left', x+'px');
12535 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12538 getResizeEl : function(){
12539 return this.inputEl();
12543 getPositionEl : function(){
12544 return this.inputEl();
12548 alignErrorIcon : function(){
12549 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12553 initEvents : function(){
12557 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12558 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12559 if(!this.multiple && this.showToggleBtn){
12560 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12561 if(this.hideTrigger){
12562 this.trigger.setDisplayed(false);
12564 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12568 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12571 if(this.removable && !this.editable && !this.tickable){
12572 var close = this.closeTriggerEl();
12575 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12576 close.on('click', this.removeBtnClick, this, close);
12580 //this.trigger.addClassOnOver('x-form-trigger-over');
12581 //this.trigger.addClassOnClick('x-form-trigger-click');
12584 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12588 closeTriggerEl : function()
12590 var close = this.el.select('.roo-combo-removable-btn', true).first();
12591 return close ? close : false;
12594 removeBtnClick : function(e, h, el)
12596 e.preventDefault();
12598 if(this.fireEvent("remove", this) !== false){
12600 this.fireEvent("afterremove", this)
12604 createList : function()
12606 this.list = Roo.get(document.body).createChild({
12607 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12608 cls: 'typeahead typeahead-long dropdown-menu shadow',
12609 style: 'display:none'
12612 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12617 initTrigger : function(){
12622 onDestroy : function(){
12624 this.trigger.removeAllListeners();
12625 // this.trigger.remove();
12628 // this.wrap.remove();
12630 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12634 onFocus : function(){
12635 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12637 if(!this.mimicing){
12638 this.wrap.addClass('x-trigger-wrap-focus');
12639 this.mimicing = true;
12640 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12641 if(this.monitorTab){
12642 this.el.on("keydown", this.checkTab, this);
12649 checkTab : function(e){
12650 if(e.getKey() == e.TAB){
12651 this.triggerBlur();
12656 onBlur : function(){
12661 mimicBlur : function(e, t){
12663 if(!this.wrap.contains(t) && this.validateBlur()){
12664 this.triggerBlur();
12670 triggerBlur : function(){
12671 this.mimicing = false;
12672 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12673 if(this.monitorTab){
12674 this.el.un("keydown", this.checkTab, this);
12676 //this.wrap.removeClass('x-trigger-wrap-focus');
12677 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12681 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12682 validateBlur : function(e, t){
12687 onDisable : function(){
12688 this.inputEl().dom.disabled = true;
12689 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12691 // this.wrap.addClass('x-item-disabled');
12696 onEnable : function(){
12697 this.inputEl().dom.disabled = false;
12698 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12700 // this.el.removeClass('x-item-disabled');
12705 onShow : function(){
12706 var ae = this.getActionEl();
12709 ae.dom.style.display = '';
12710 ae.dom.style.visibility = 'visible';
12716 onHide : function(){
12717 var ae = this.getActionEl();
12718 ae.dom.style.display = 'none';
12722 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12723 * by an implementing function.
12725 * @param {EventObject} e
12727 onTriggerClick : Roo.emptyFn
12735 * @class Roo.bootstrap.CardUploader
12736 * @extends Roo.bootstrap.Button
12737 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12738 * @cfg {Number} errorTimeout default 3000
12739 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12740 * @cfg {Array} html The button text.
12744 * Create a new CardUploader
12745 * @param {Object} config The config object
12748 Roo.bootstrap.CardUploader = function(config){
12752 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12755 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12763 * When a image is clicked on - and needs to display a slideshow or similar..
12764 * @param {Roo.bootstrap.Card} this
12765 * @param {Object} The image information data
12771 * When a the download link is clicked
12772 * @param {Roo.bootstrap.Card} this
12773 * @param {Object} The image information data contains
12780 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12783 errorTimeout : 3000,
12787 fileCollection : false,
12790 getAutoCreate : function()
12794 cls :'form-group' ,
12799 //cls : 'input-group-addon',
12800 html : this.fieldLabel
12808 value : this.value,
12809 cls : 'd-none form-control'
12814 multiple : 'multiple',
12816 cls : 'd-none roo-card-upload-selector'
12820 cls : 'roo-card-uploader-button-container w-100 mb-2'
12823 cls : 'card-columns roo-card-uploader-container'
12833 getChildContainer : function() /// what children are added to.
12835 return this.containerEl;
12838 getButtonContainer : function() /// what children are added to.
12840 return this.el.select(".roo-card-uploader-button-container").first();
12843 initEvents : function()
12846 Roo.bootstrap.Input.prototype.initEvents.call(this);
12850 xns: Roo.bootstrap,
12853 container_method : 'getButtonContainer' ,
12854 html : this.html, // fix changable?
12857 'click' : function(btn, e) {
12866 this.urlAPI = (window.createObjectURL && window) ||
12867 (window.URL && URL.revokeObjectURL && URL) ||
12868 (window.webkitURL && webkitURL);
12873 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12875 this.selectorEl.on('change', this.onFileSelected, this);
12878 this.images.forEach(function(img) {
12881 this.images = false;
12883 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12889 onClick : function(e)
12891 e.preventDefault();
12893 this.selectorEl.dom.click();
12897 onFileSelected : function(e)
12899 e.preventDefault();
12901 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12905 Roo.each(this.selectorEl.dom.files, function(file){
12906 this.addFile(file);
12915 addFile : function(file)
12918 if(typeof(file) === 'string'){
12919 throw "Add file by name?"; // should not happen
12923 if(!file || !this.urlAPI){
12933 var url = _this.urlAPI.createObjectURL( file);
12936 id : Roo.bootstrap.CardUploader.ID--,
12937 is_uploaded : false,
12941 mimetype : file.type,
12949 * addCard - add an Attachment to the uploader
12950 * @param data - the data about the image to upload
12954 title : "Title of file",
12955 is_uploaded : false,
12956 src : "http://.....",
12957 srcfile : { the File upload object },
12958 mimetype : file.type,
12961 .. any other data...
12967 addCard : function (data)
12969 // hidden input element?
12970 // if the file is not an image...
12971 //then we need to use something other that and header_image
12976 xns : Roo.bootstrap,
12977 xtype : 'CardFooter',
12980 xns : Roo.bootstrap,
12986 xns : Roo.bootstrap,
12988 html : String.format("<small>{0}</small>", data.title),
12989 cls : 'col-10 text-left',
12994 click : function() {
12996 t.fireEvent( "download", t, data );
13002 xns : Roo.bootstrap,
13004 style: 'max-height: 28px; ',
13010 click : function() {
13011 t.removeCard(data.id)
13023 var cn = this.addxtype(
13026 xns : Roo.bootstrap,
13029 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13030 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13031 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13036 initEvents : function() {
13037 Roo.bootstrap.Card.prototype.initEvents.call(this);
13039 this.imgEl = this.el.select('.card-img-top').first();
13041 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13042 this.imgEl.set({ 'pointer' : 'cursor' });
13045 this.getCardFooter().addClass('p-1');
13052 // dont' really need ot update items.
13053 // this.items.push(cn);
13054 this.fileCollection.add(cn);
13056 if (!data.srcfile) {
13057 this.updateInput();
13062 var reader = new FileReader();
13063 reader.addEventListener("load", function() {
13064 data.srcdata = reader.result;
13067 reader.readAsDataURL(data.srcfile);
13072 removeCard : function(id)
13075 var card = this.fileCollection.get(id);
13076 card.data.is_deleted = 1;
13077 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13078 //this.fileCollection.remove(card);
13079 //this.items = this.items.filter(function(e) { return e != card });
13080 // dont' really need ot update items.
13081 card.el.dom.parentNode.removeChild(card.el.dom);
13082 this.updateInput();
13088 this.fileCollection.each(function(card) {
13089 if (card.el.dom && card.el.dom.parentNode) {
13090 card.el.dom.parentNode.removeChild(card.el.dom);
13093 this.fileCollection.clear();
13094 this.updateInput();
13097 updateInput : function()
13100 this.fileCollection.each(function(e) {
13104 this.inputEl().dom.value = JSON.stringify(data);
13114 Roo.bootstrap.CardUploader.ID = -1;/*
13116 * Ext JS Library 1.1.1
13117 * Copyright(c) 2006-2007, Ext JS, LLC.
13119 * Originally Released Under LGPL - original licence link has changed is not relivant.
13122 * <script type="text/javascript">
13127 * @class Roo.data.SortTypes
13129 * Defines the default sorting (casting?) comparison functions used when sorting data.
13131 Roo.data.SortTypes = {
13133 * Default sort that does nothing
13134 * @param {Mixed} s The value being converted
13135 * @return {Mixed} The comparison value
13137 none : function(s){
13142 * The regular expression used to strip tags
13146 stripTagsRE : /<\/?[^>]+>/gi,
13149 * Strips all HTML tags to sort on text only
13150 * @param {Mixed} s The value being converted
13151 * @return {String} The comparison value
13153 asText : function(s){
13154 return String(s).replace(this.stripTagsRE, "");
13158 * Strips all HTML tags to sort on text only - Case insensitive
13159 * @param {Mixed} s The value being converted
13160 * @return {String} The comparison value
13162 asUCText : function(s){
13163 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13167 * Case insensitive string
13168 * @param {Mixed} s The value being converted
13169 * @return {String} The comparison value
13171 asUCString : function(s) {
13172 return String(s).toUpperCase();
13177 * @param {Mixed} s The value being converted
13178 * @return {Number} The comparison value
13180 asDate : function(s) {
13184 if(s instanceof Date){
13185 return s.getTime();
13187 return Date.parse(String(s));
13192 * @param {Mixed} s The value being converted
13193 * @return {Float} The comparison value
13195 asFloat : function(s) {
13196 var val = parseFloat(String(s).replace(/,/g, ""));
13205 * @param {Mixed} s The value being converted
13206 * @return {Number} The comparison value
13208 asInt : function(s) {
13209 var val = parseInt(String(s).replace(/,/g, ""));
13217 * Ext JS Library 1.1.1
13218 * Copyright(c) 2006-2007, Ext JS, LLC.
13220 * Originally Released Under LGPL - original licence link has changed is not relivant.
13223 * <script type="text/javascript">
13227 * @class Roo.data.Record
13228 * Instances of this class encapsulate both record <em>definition</em> information, and record
13229 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13230 * to access Records cached in an {@link Roo.data.Store} object.<br>
13232 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13233 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13236 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13238 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13239 * {@link #create}. The parameters are the same.
13240 * @param {Array} data An associative Array of data values keyed by the field name.
13241 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13242 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13243 * not specified an integer id is generated.
13245 Roo.data.Record = function(data, id){
13246 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13251 * Generate a constructor for a specific record layout.
13252 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13253 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13254 * Each field definition object may contain the following properties: <ul>
13255 * <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,
13256 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13257 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13258 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13259 * is being used, then this is a string containing the javascript expression to reference the data relative to
13260 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13261 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13262 * this may be omitted.</p></li>
13263 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13264 * <ul><li>auto (Default, implies no conversion)</li>
13269 * <li>date</li></ul></p></li>
13270 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13271 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13272 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13273 * by the Reader into an object that will be stored in the Record. It is passed the
13274 * following parameters:<ul>
13275 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13277 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13279 * <br>usage:<br><pre><code>
13280 var TopicRecord = Roo.data.Record.create(
13281 {name: 'title', mapping: 'topic_title'},
13282 {name: 'author', mapping: 'username'},
13283 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13284 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13285 {name: 'lastPoster', mapping: 'user2'},
13286 {name: 'excerpt', mapping: 'post_text'}
13289 var myNewRecord = new TopicRecord({
13290 title: 'Do my job please',
13293 lastPost: new Date(),
13294 lastPoster: 'Animal',
13295 excerpt: 'No way dude!'
13297 myStore.add(myNewRecord);
13302 Roo.data.Record.create = function(o){
13303 var f = function(){
13304 f.superclass.constructor.apply(this, arguments);
13306 Roo.extend(f, Roo.data.Record);
13307 var p = f.prototype;
13308 p.fields = new Roo.util.MixedCollection(false, function(field){
13311 for(var i = 0, len = o.length; i < len; i++){
13312 p.fields.add(new Roo.data.Field(o[i]));
13314 f.getField = function(name){
13315 return p.fields.get(name);
13320 Roo.data.Record.AUTO_ID = 1000;
13321 Roo.data.Record.EDIT = 'edit';
13322 Roo.data.Record.REJECT = 'reject';
13323 Roo.data.Record.COMMIT = 'commit';
13325 Roo.data.Record.prototype = {
13327 * Readonly flag - true if this record has been modified.
13336 join : function(store){
13337 this.store = store;
13341 * Set the named field to the specified value.
13342 * @param {String} name The name of the field to set.
13343 * @param {Object} value The value to set the field to.
13345 set : function(name, value){
13346 if(this.data[name] == value){
13350 if(!this.modified){
13351 this.modified = {};
13353 if(typeof this.modified[name] == 'undefined'){
13354 this.modified[name] = this.data[name];
13356 this.data[name] = value;
13357 if(!this.editing && this.store){
13358 this.store.afterEdit(this);
13363 * Get the value of the named field.
13364 * @param {String} name The name of the field to get the value of.
13365 * @return {Object} The value of the field.
13367 get : function(name){
13368 return this.data[name];
13372 beginEdit : function(){
13373 this.editing = true;
13374 this.modified = {};
13378 cancelEdit : function(){
13379 this.editing = false;
13380 delete this.modified;
13384 endEdit : function(){
13385 this.editing = false;
13386 if(this.dirty && this.store){
13387 this.store.afterEdit(this);
13392 * Usually called by the {@link Roo.data.Store} which owns the Record.
13393 * Rejects all changes made to the Record since either creation, or the last commit operation.
13394 * Modified fields are reverted to their original values.
13396 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13397 * of reject operations.
13399 reject : function(){
13400 var m = this.modified;
13402 if(typeof m[n] != "function"){
13403 this.data[n] = m[n];
13406 this.dirty = false;
13407 delete this.modified;
13408 this.editing = false;
13410 this.store.afterReject(this);
13415 * Usually called by the {@link Roo.data.Store} which owns the Record.
13416 * Commits all changes made to the Record since either creation, or the last commit operation.
13418 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13419 * of commit operations.
13421 commit : function(){
13422 this.dirty = false;
13423 delete this.modified;
13424 this.editing = false;
13426 this.store.afterCommit(this);
13431 hasError : function(){
13432 return this.error != null;
13436 clearError : function(){
13441 * Creates a copy of this record.
13442 * @param {String} id (optional) A new record id if you don't want to use this record's id
13445 copy : function(newId) {
13446 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13450 * Ext JS Library 1.1.1
13451 * Copyright(c) 2006-2007, Ext JS, LLC.
13453 * Originally Released Under LGPL - original licence link has changed is not relivant.
13456 * <script type="text/javascript">
13462 * @class Roo.data.Store
13463 * @extends Roo.util.Observable
13464 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13465 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13467 * 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
13468 * has no knowledge of the format of the data returned by the Proxy.<br>
13470 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13471 * instances from the data object. These records are cached and made available through accessor functions.
13473 * Creates a new Store.
13474 * @param {Object} config A config object containing the objects needed for the Store to access data,
13475 * and read the data into Records.
13477 Roo.data.Store = function(config){
13478 this.data = new Roo.util.MixedCollection(false);
13479 this.data.getKey = function(o){
13482 this.baseParams = {};
13484 this.paramNames = {
13489 "multisort" : "_multisort"
13492 if(config && config.data){
13493 this.inlineData = config.data;
13494 delete config.data;
13497 Roo.apply(this, config);
13499 if(this.reader){ // reader passed
13500 this.reader = Roo.factory(this.reader, Roo.data);
13501 this.reader.xmodule = this.xmodule || false;
13502 if(!this.recordType){
13503 this.recordType = this.reader.recordType;
13505 if(this.reader.onMetaChange){
13506 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13510 if(this.recordType){
13511 this.fields = this.recordType.prototype.fields;
13513 this.modified = [];
13517 * @event datachanged
13518 * Fires when the data cache has changed, and a widget which is using this Store
13519 * as a Record cache should refresh its view.
13520 * @param {Store} this
13522 datachanged : true,
13524 * @event metachange
13525 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13526 * @param {Store} this
13527 * @param {Object} meta The JSON metadata
13532 * Fires when Records have been added to the Store
13533 * @param {Store} this
13534 * @param {Roo.data.Record[]} records The array of Records added
13535 * @param {Number} index The index at which the record(s) were added
13540 * Fires when a Record has been removed from the Store
13541 * @param {Store} this
13542 * @param {Roo.data.Record} record The Record that was removed
13543 * @param {Number} index The index at which the record was removed
13548 * Fires when a Record has been updated
13549 * @param {Store} this
13550 * @param {Roo.data.Record} record The Record that was updated
13551 * @param {String} operation The update operation being performed. Value may be one of:
13553 Roo.data.Record.EDIT
13554 Roo.data.Record.REJECT
13555 Roo.data.Record.COMMIT
13561 * Fires when the data cache has been cleared.
13562 * @param {Store} this
13566 * @event beforeload
13567 * Fires before a request is made for a new data object. If the beforeload handler returns false
13568 * the load action will be canceled.
13569 * @param {Store} this
13570 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13574 * @event beforeloadadd
13575 * Fires after a new set of Records has been loaded.
13576 * @param {Store} this
13577 * @param {Roo.data.Record[]} records The Records that were loaded
13578 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13580 beforeloadadd : true,
13583 * Fires after a new set of Records has been loaded, before they are added to the store.
13584 * @param {Store} this
13585 * @param {Roo.data.Record[]} records The Records that were loaded
13586 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13587 * @params {Object} return from reader
13591 * @event loadexception
13592 * Fires if an exception occurs in the Proxy during loading.
13593 * Called with the signature of the Proxy's "loadexception" event.
13594 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13597 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13598 * @param {Object} load options
13599 * @param {Object} jsonData from your request (normally this contains the Exception)
13601 loadexception : true
13605 this.proxy = Roo.factory(this.proxy, Roo.data);
13606 this.proxy.xmodule = this.xmodule || false;
13607 this.relayEvents(this.proxy, ["loadexception"]);
13609 this.sortToggle = {};
13610 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13612 Roo.data.Store.superclass.constructor.call(this);
13614 if(this.inlineData){
13615 this.loadData(this.inlineData);
13616 delete this.inlineData;
13620 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13622 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13623 * without a remote query - used by combo/forms at present.
13627 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13630 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13633 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13634 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13637 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13638 * on any HTTP request
13641 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13644 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13648 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13649 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13651 remoteSort : false,
13654 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13655 * loaded or when a record is removed. (defaults to false).
13657 pruneModifiedRecords : false,
13660 lastOptions : null,
13663 * Add Records to the Store and fires the add event.
13664 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13666 add : function(records){
13667 records = [].concat(records);
13668 for(var i = 0, len = records.length; i < len; i++){
13669 records[i].join(this);
13671 var index = this.data.length;
13672 this.data.addAll(records);
13673 this.fireEvent("add", this, records, index);
13677 * Remove a Record from the Store and fires the remove event.
13678 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13680 remove : function(record){
13681 var index = this.data.indexOf(record);
13682 this.data.removeAt(index);
13684 if(this.pruneModifiedRecords){
13685 this.modified.remove(record);
13687 this.fireEvent("remove", this, record, index);
13691 * Remove all Records from the Store and fires the clear event.
13693 removeAll : function(){
13695 if(this.pruneModifiedRecords){
13696 this.modified = [];
13698 this.fireEvent("clear", this);
13702 * Inserts Records to the Store at the given index and fires the add event.
13703 * @param {Number} index The start index at which to insert the passed Records.
13704 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13706 insert : function(index, records){
13707 records = [].concat(records);
13708 for(var i = 0, len = records.length; i < len; i++){
13709 this.data.insert(index, records[i]);
13710 records[i].join(this);
13712 this.fireEvent("add", this, records, index);
13716 * Get the index within the cache of the passed Record.
13717 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13718 * @return {Number} The index of the passed Record. Returns -1 if not found.
13720 indexOf : function(record){
13721 return this.data.indexOf(record);
13725 * Get the index within the cache of the Record with the passed id.
13726 * @param {String} id The id of the Record to find.
13727 * @return {Number} The index of the Record. Returns -1 if not found.
13729 indexOfId : function(id){
13730 return this.data.indexOfKey(id);
13734 * Get the Record with the specified id.
13735 * @param {String} id The id of the Record to find.
13736 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13738 getById : function(id){
13739 return this.data.key(id);
13743 * Get the Record at the specified index.
13744 * @param {Number} index The index of the Record to find.
13745 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13747 getAt : function(index){
13748 return this.data.itemAt(index);
13752 * Returns a range of Records between specified indices.
13753 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13754 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13755 * @return {Roo.data.Record[]} An array of Records
13757 getRange : function(start, end){
13758 return this.data.getRange(start, end);
13762 storeOptions : function(o){
13763 o = Roo.apply({}, o);
13766 this.lastOptions = o;
13770 * Loads the Record cache from the configured Proxy using the configured Reader.
13772 * If using remote paging, then the first load call must specify the <em>start</em>
13773 * and <em>limit</em> properties in the options.params property to establish the initial
13774 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13776 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13777 * and this call will return before the new data has been loaded. Perform any post-processing
13778 * in a callback function, or in a "load" event handler.</strong>
13780 * @param {Object} options An object containing properties which control loading options:<ul>
13781 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13782 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13783 * passed the following arguments:<ul>
13784 * <li>r : Roo.data.Record[]</li>
13785 * <li>options: Options object from the load call</li>
13786 * <li>success: Boolean success indicator</li></ul></li>
13787 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13788 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13791 load : function(options){
13792 options = options || {};
13793 if(this.fireEvent("beforeload", this, options) !== false){
13794 this.storeOptions(options);
13795 var p = Roo.apply(options.params || {}, this.baseParams);
13796 // if meta was not loaded from remote source.. try requesting it.
13797 if (!this.reader.metaFromRemote) {
13798 p._requestMeta = 1;
13800 if(this.sortInfo && this.remoteSort){
13801 var pn = this.paramNames;
13802 p[pn["sort"]] = this.sortInfo.field;
13803 p[pn["dir"]] = this.sortInfo.direction;
13805 if (this.multiSort) {
13806 var pn = this.paramNames;
13807 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13810 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13815 * Reloads the Record cache from the configured Proxy using the configured Reader and
13816 * the options from the last load operation performed.
13817 * @param {Object} options (optional) An object containing properties which may override the options
13818 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13819 * the most recently used options are reused).
13821 reload : function(options){
13822 this.load(Roo.applyIf(options||{}, this.lastOptions));
13826 // Called as a callback by the Reader during a load operation.
13827 loadRecords : function(o, options, success){
13828 if(!o || success === false){
13829 if(success !== false){
13830 this.fireEvent("load", this, [], options, o);
13832 if(options.callback){
13833 options.callback.call(options.scope || this, [], options, false);
13837 // if data returned failure - throw an exception.
13838 if (o.success === false) {
13839 // show a message if no listener is registered.
13840 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13841 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13843 // loadmask wil be hooked into this..
13844 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13847 var r = o.records, t = o.totalRecords || r.length;
13849 this.fireEvent("beforeloadadd", this, r, options, o);
13851 if(!options || options.add !== true){
13852 if(this.pruneModifiedRecords){
13853 this.modified = [];
13855 for(var i = 0, len = r.length; i < len; i++){
13859 this.data = this.snapshot;
13860 delete this.snapshot;
13863 this.data.addAll(r);
13864 this.totalLength = t;
13866 this.fireEvent("datachanged", this);
13868 this.totalLength = Math.max(t, this.data.length+r.length);
13872 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13874 var e = new Roo.data.Record({});
13876 e.set(this.parent.displayField, this.parent.emptyTitle);
13877 e.set(this.parent.valueField, '');
13882 this.fireEvent("load", this, r, options, o);
13883 if(options.callback){
13884 options.callback.call(options.scope || this, r, options, true);
13890 * Loads data from a passed data block. A Reader which understands the format of the data
13891 * must have been configured in the constructor.
13892 * @param {Object} data The data block from which to read the Records. The format of the data expected
13893 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13894 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13896 loadData : function(o, append){
13897 var r = this.reader.readRecords(o);
13898 this.loadRecords(r, {add: append}, true);
13902 * using 'cn' the nested child reader read the child array into it's child stores.
13903 * @param {Object} rec The record with a 'children array
13905 loadDataFromChildren : function(rec)
13907 this.loadData(this.reader.toLoadData(rec));
13912 * Gets the number of cached records.
13914 * <em>If using paging, this may not be the total size of the dataset. If the data object
13915 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13916 * the data set size</em>
13918 getCount : function(){
13919 return this.data.length || 0;
13923 * Gets the total number of records in the dataset as returned by the server.
13925 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13926 * the dataset size</em>
13928 getTotalCount : function(){
13929 return this.totalLength || 0;
13933 * Returns the sort state of the Store as an object with two properties:
13935 field {String} The name of the field by which the Records are sorted
13936 direction {String} The sort order, "ASC" or "DESC"
13939 getSortState : function(){
13940 return this.sortInfo;
13944 applySort : function(){
13945 if(this.sortInfo && !this.remoteSort){
13946 var s = this.sortInfo, f = s.field;
13947 var st = this.fields.get(f).sortType;
13948 var fn = function(r1, r2){
13949 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13950 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13952 this.data.sort(s.direction, fn);
13953 if(this.snapshot && this.snapshot != this.data){
13954 this.snapshot.sort(s.direction, fn);
13960 * Sets the default sort column and order to be used by the next load operation.
13961 * @param {String} fieldName The name of the field to sort by.
13962 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13964 setDefaultSort : function(field, dir){
13965 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13969 * Sort the Records.
13970 * If remote sorting is used, the sort is performed on the server, and the cache is
13971 * reloaded. If local sorting is used, the cache is sorted internally.
13972 * @param {String} fieldName The name of the field to sort by.
13973 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13975 sort : function(fieldName, dir){
13976 var f = this.fields.get(fieldName);
13978 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13980 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13981 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13986 this.sortToggle[f.name] = dir;
13987 this.sortInfo = {field: f.name, direction: dir};
13988 if(!this.remoteSort){
13990 this.fireEvent("datachanged", this);
13992 this.load(this.lastOptions);
13997 * Calls the specified function for each of the Records in the cache.
13998 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13999 * Returning <em>false</em> aborts and exits the iteration.
14000 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14002 each : function(fn, scope){
14003 this.data.each(fn, scope);
14007 * Gets all records modified since the last commit. Modified records are persisted across load operations
14008 * (e.g., during paging).
14009 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14011 getModifiedRecords : function(){
14012 return this.modified;
14016 createFilterFn : function(property, value, anyMatch){
14017 if(!value.exec){ // not a regex
14018 value = String(value);
14019 if(value.length == 0){
14022 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14024 return function(r){
14025 return value.test(r.data[property]);
14030 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14031 * @param {String} property A field on your records
14032 * @param {Number} start The record index to start at (defaults to 0)
14033 * @param {Number} end The last record index to include (defaults to length - 1)
14034 * @return {Number} The sum
14036 sum : function(property, start, end){
14037 var rs = this.data.items, v = 0;
14038 start = start || 0;
14039 end = (end || end === 0) ? end : rs.length-1;
14041 for(var i = start; i <= end; i++){
14042 v += (rs[i].data[property] || 0);
14048 * Filter the records by a specified property.
14049 * @param {String} field A field on your records
14050 * @param {String/RegExp} value Either a string that the field
14051 * should start with or a RegExp to test against the field
14052 * @param {Boolean} anyMatch True to match any part not just the beginning
14054 filter : function(property, value, anyMatch){
14055 var fn = this.createFilterFn(property, value, anyMatch);
14056 return fn ? this.filterBy(fn) : this.clearFilter();
14060 * Filter by a function. The specified function will be called with each
14061 * record in this data source. If the function returns true the record is included,
14062 * otherwise it is filtered.
14063 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14064 * @param {Object} scope (optional) The scope of the function (defaults to this)
14066 filterBy : function(fn, scope){
14067 this.snapshot = this.snapshot || this.data;
14068 this.data = this.queryBy(fn, scope||this);
14069 this.fireEvent("datachanged", this);
14073 * Query the records by a specified property.
14074 * @param {String} field A field on your records
14075 * @param {String/RegExp} value Either a string that the field
14076 * should start with or a RegExp to test against the field
14077 * @param {Boolean} anyMatch True to match any part not just the beginning
14078 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14080 query : function(property, value, anyMatch){
14081 var fn = this.createFilterFn(property, value, anyMatch);
14082 return fn ? this.queryBy(fn) : this.data.clone();
14086 * Query by a function. The specified function will be called with each
14087 * record in this data source. If the function returns true the record is included
14089 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14090 * @param {Object} scope (optional) The scope of the function (defaults to this)
14091 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14093 queryBy : function(fn, scope){
14094 var data = this.snapshot || this.data;
14095 return data.filterBy(fn, scope||this);
14099 * Collects unique values for a particular dataIndex from this store.
14100 * @param {String} dataIndex The property to collect
14101 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14102 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14103 * @return {Array} An array of the unique values
14105 collect : function(dataIndex, allowNull, bypassFilter){
14106 var d = (bypassFilter === true && this.snapshot) ?
14107 this.snapshot.items : this.data.items;
14108 var v, sv, r = [], l = {};
14109 for(var i = 0, len = d.length; i < len; i++){
14110 v = d[i].data[dataIndex];
14112 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14121 * Revert to a view of the Record cache with no filtering applied.
14122 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14124 clearFilter : function(suppressEvent){
14125 if(this.snapshot && this.snapshot != this.data){
14126 this.data = this.snapshot;
14127 delete this.snapshot;
14128 if(suppressEvent !== true){
14129 this.fireEvent("datachanged", this);
14135 afterEdit : function(record){
14136 if(this.modified.indexOf(record) == -1){
14137 this.modified.push(record);
14139 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14143 afterReject : function(record){
14144 this.modified.remove(record);
14145 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14149 afterCommit : function(record){
14150 this.modified.remove(record);
14151 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14155 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14156 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14158 commitChanges : function(){
14159 var m = this.modified.slice(0);
14160 this.modified = [];
14161 for(var i = 0, len = m.length; i < len; i++){
14167 * Cancel outstanding changes on all changed records.
14169 rejectChanges : function(){
14170 var m = this.modified.slice(0);
14171 this.modified = [];
14172 for(var i = 0, len = m.length; i < len; i++){
14177 onMetaChange : function(meta, rtype, o){
14178 this.recordType = rtype;
14179 this.fields = rtype.prototype.fields;
14180 delete this.snapshot;
14181 this.sortInfo = meta.sortInfo || this.sortInfo;
14182 this.modified = [];
14183 this.fireEvent('metachange', this, this.reader.meta);
14186 moveIndex : function(data, type)
14188 var index = this.indexOf(data);
14190 var newIndex = index + type;
14194 this.insert(newIndex, data);
14199 * Ext JS Library 1.1.1
14200 * Copyright(c) 2006-2007, Ext JS, LLC.
14202 * Originally Released Under LGPL - original licence link has changed is not relivant.
14205 * <script type="text/javascript">
14209 * @class Roo.data.SimpleStore
14210 * @extends Roo.data.Store
14211 * Small helper class to make creating Stores from Array data easier.
14212 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14213 * @cfg {Array} fields An array of field definition objects, or field name strings.
14214 * @cfg {Object} an existing reader (eg. copied from another store)
14215 * @cfg {Array} data The multi-dimensional array of data
14217 * @param {Object} config
14219 Roo.data.SimpleStore = function(config)
14221 Roo.data.SimpleStore.superclass.constructor.call(this, {
14223 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14226 Roo.data.Record.create(config.fields)
14228 proxy : new Roo.data.MemoryProxy(config.data)
14232 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14234 * Ext JS Library 1.1.1
14235 * Copyright(c) 2006-2007, Ext JS, LLC.
14237 * Originally Released Under LGPL - original licence link has changed is not relivant.
14240 * <script type="text/javascript">
14245 * @extends Roo.data.Store
14246 * @class Roo.data.JsonStore
14247 * Small helper class to make creating Stores for JSON data easier. <br/>
14249 var store = new Roo.data.JsonStore({
14250 url: 'get-images.php',
14252 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14255 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14256 * JsonReader and HttpProxy (unless inline data is provided).</b>
14257 * @cfg {Array} fields An array of field definition objects, or field name strings.
14259 * @param {Object} config
14261 Roo.data.JsonStore = function(c){
14262 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14263 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14264 reader: new Roo.data.JsonReader(c, c.fields)
14267 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14269 * Ext JS Library 1.1.1
14270 * Copyright(c) 2006-2007, Ext JS, LLC.
14272 * Originally Released Under LGPL - original licence link has changed is not relivant.
14275 * <script type="text/javascript">
14279 Roo.data.Field = function(config){
14280 if(typeof config == "string"){
14281 config = {name: config};
14283 Roo.apply(this, config);
14286 this.type = "auto";
14289 var st = Roo.data.SortTypes;
14290 // named sortTypes are supported, here we look them up
14291 if(typeof this.sortType == "string"){
14292 this.sortType = st[this.sortType];
14295 // set default sortType for strings and dates
14296 if(!this.sortType){
14299 this.sortType = st.asUCString;
14302 this.sortType = st.asDate;
14305 this.sortType = st.none;
14310 var stripRe = /[\$,%]/g;
14312 // prebuilt conversion function for this field, instead of
14313 // switching every time we're reading a value
14315 var cv, dateFormat = this.dateFormat;
14320 cv = function(v){ return v; };
14323 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14327 return v !== undefined && v !== null && v !== '' ?
14328 parseInt(String(v).replace(stripRe, ""), 10) : '';
14333 return v !== undefined && v !== null && v !== '' ?
14334 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14339 cv = function(v){ return v === true || v === "true" || v == 1; };
14346 if(v instanceof Date){
14350 if(dateFormat == "timestamp"){
14351 return new Date(v*1000);
14353 return Date.parseDate(v, dateFormat);
14355 var parsed = Date.parse(v);
14356 return parsed ? new Date(parsed) : null;
14365 Roo.data.Field.prototype = {
14373 * Ext JS Library 1.1.1
14374 * Copyright(c) 2006-2007, Ext JS, LLC.
14376 * Originally Released Under LGPL - original licence link has changed is not relivant.
14379 * <script type="text/javascript">
14382 // Base class for reading structured data from a data source. This class is intended to be
14383 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14386 * @class Roo.data.DataReader
14387 * Base class for reading structured data from a data source. This class is intended to be
14388 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14391 Roo.data.DataReader = function(meta, recordType){
14395 this.recordType = recordType instanceof Array ?
14396 Roo.data.Record.create(recordType) : recordType;
14399 Roo.data.DataReader.prototype = {
14402 readerType : 'Data',
14404 * Create an empty record
14405 * @param {Object} data (optional) - overlay some values
14406 * @return {Roo.data.Record} record created.
14408 newRow : function(d) {
14410 this.recordType.prototype.fields.each(function(c) {
14412 case 'int' : da[c.name] = 0; break;
14413 case 'date' : da[c.name] = new Date(); break;
14414 case 'float' : da[c.name] = 0.0; break;
14415 case 'boolean' : da[c.name] = false; break;
14416 default : da[c.name] = ""; break;
14420 return new this.recordType(Roo.apply(da, d));
14426 * Ext JS Library 1.1.1
14427 * Copyright(c) 2006-2007, Ext JS, LLC.
14429 * Originally Released Under LGPL - original licence link has changed is not relivant.
14432 * <script type="text/javascript">
14436 * @class Roo.data.DataProxy
14437 * @extends Roo.data.Observable
14438 * This class is an abstract base class for implementations which provide retrieval of
14439 * unformatted data objects.<br>
14441 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14442 * (of the appropriate type which knows how to parse the data object) to provide a block of
14443 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14445 * Custom implementations must implement the load method as described in
14446 * {@link Roo.data.HttpProxy#load}.
14448 Roo.data.DataProxy = function(){
14451 * @event beforeload
14452 * Fires before a network request is made to retrieve a data object.
14453 * @param {Object} This DataProxy object.
14454 * @param {Object} params The params parameter to the load function.
14459 * Fires before the load method's callback is called.
14460 * @param {Object} This DataProxy object.
14461 * @param {Object} o The data object.
14462 * @param {Object} arg The callback argument object passed to the load function.
14466 * @event loadexception
14467 * Fires if an Exception occurs during data retrieval.
14468 * @param {Object} This DataProxy object.
14469 * @param {Object} o The data object.
14470 * @param {Object} arg The callback argument object passed to the load function.
14471 * @param {Object} e The Exception.
14473 loadexception : true
14475 Roo.data.DataProxy.superclass.constructor.call(this);
14478 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14481 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14485 * Ext JS Library 1.1.1
14486 * Copyright(c) 2006-2007, Ext JS, LLC.
14488 * Originally Released Under LGPL - original licence link has changed is not relivant.
14491 * <script type="text/javascript">
14494 * @class Roo.data.MemoryProxy
14495 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14496 * to the Reader when its load method is called.
14498 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14500 Roo.data.MemoryProxy = function(data){
14504 Roo.data.MemoryProxy.superclass.constructor.call(this);
14508 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14511 * Load data from the requested source (in this case an in-memory
14512 * data object passed to the constructor), read the data object into
14513 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14514 * process that block using the passed callback.
14515 * @param {Object} params This parameter is not used by the MemoryProxy class.
14516 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14517 * object into a block of Roo.data.Records.
14518 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14519 * The function must be passed <ul>
14520 * <li>The Record block object</li>
14521 * <li>The "arg" argument from the load function</li>
14522 * <li>A boolean success indicator</li>
14524 * @param {Object} scope The scope in which to call the callback
14525 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14527 load : function(params, reader, callback, scope, arg){
14528 params = params || {};
14531 result = reader.readRecords(params.data ? params.data :this.data);
14533 this.fireEvent("loadexception", this, arg, null, e);
14534 callback.call(scope, null, arg, false);
14537 callback.call(scope, result, arg, true);
14541 update : function(params, records){
14546 * Ext JS Library 1.1.1
14547 * Copyright(c) 2006-2007, Ext JS, LLC.
14549 * Originally Released Under LGPL - original licence link has changed is not relivant.
14552 * <script type="text/javascript">
14555 * @class Roo.data.HttpProxy
14556 * @extends Roo.data.DataProxy
14557 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14558 * configured to reference a certain URL.<br><br>
14560 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14561 * from which the running page was served.<br><br>
14563 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14565 * Be aware that to enable the browser to parse an XML document, the server must set
14566 * the Content-Type header in the HTTP response to "text/xml".
14568 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14569 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14570 * will be used to make the request.
14572 Roo.data.HttpProxy = function(conn){
14573 Roo.data.HttpProxy.superclass.constructor.call(this);
14574 // is conn a conn config or a real conn?
14576 this.useAjax = !conn || !conn.events;
14580 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14581 // thse are take from connection...
14584 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14587 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14588 * extra parameters to each request made by this object. (defaults to undefined)
14591 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14592 * to each request made by this object. (defaults to undefined)
14595 * @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)
14598 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14601 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14607 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14611 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14612 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14613 * a finer-grained basis than the DataProxy events.
14615 getConnection : function(){
14616 return this.useAjax ? Roo.Ajax : this.conn;
14620 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14621 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14622 * process that block using the passed callback.
14623 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14624 * for the request to the remote server.
14625 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14626 * object into a block of Roo.data.Records.
14627 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14628 * The function must be passed <ul>
14629 * <li>The Record block object</li>
14630 * <li>The "arg" argument from the load function</li>
14631 * <li>A boolean success indicator</li>
14633 * @param {Object} scope The scope in which to call the callback
14634 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14636 load : function(params, reader, callback, scope, arg){
14637 if(this.fireEvent("beforeload", this, params) !== false){
14639 params : params || {},
14641 callback : callback,
14646 callback : this.loadResponse,
14650 Roo.applyIf(o, this.conn);
14651 if(this.activeRequest){
14652 Roo.Ajax.abort(this.activeRequest);
14654 this.activeRequest = Roo.Ajax.request(o);
14656 this.conn.request(o);
14659 callback.call(scope||this, null, arg, false);
14664 loadResponse : function(o, success, response){
14665 delete this.activeRequest;
14667 this.fireEvent("loadexception", this, o, response);
14668 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14673 result = o.reader.read(response);
14675 this.fireEvent("loadexception", this, o, response, e);
14676 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14680 this.fireEvent("load", this, o, o.request.arg);
14681 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14685 update : function(dataSet){
14690 updateResponse : function(dataSet){
14695 * Ext JS Library 1.1.1
14696 * Copyright(c) 2006-2007, Ext JS, LLC.
14698 * Originally Released Under LGPL - original licence link has changed is not relivant.
14701 * <script type="text/javascript">
14705 * @class Roo.data.ScriptTagProxy
14706 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14707 * other than the originating domain of the running page.<br><br>
14709 * <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
14710 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14712 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14713 * source code that is used as the source inside a <script> tag.<br><br>
14715 * In order for the browser to process the returned data, the server must wrap the data object
14716 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14717 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14718 * depending on whether the callback name was passed:
14721 boolean scriptTag = false;
14722 String cb = request.getParameter("callback");
14725 response.setContentType("text/javascript");
14727 response.setContentType("application/x-json");
14729 Writer out = response.getWriter();
14731 out.write(cb + "(");
14733 out.print(dataBlock.toJsonString());
14740 * @param {Object} config A configuration object.
14742 Roo.data.ScriptTagProxy = function(config){
14743 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14744 Roo.apply(this, config);
14745 this.head = document.getElementsByTagName("head")[0];
14748 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14750 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14752 * @cfg {String} url The URL from which to request the data object.
14755 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14759 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14760 * the server the name of the callback function set up by the load call to process the returned data object.
14761 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14762 * javascript output which calls this named function passing the data object as its only parameter.
14764 callbackParam : "callback",
14766 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14767 * name to the request.
14772 * Load data from the configured URL, read the data object into
14773 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14774 * process that block using the passed callback.
14775 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14776 * for the request to the remote server.
14777 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14778 * object into a block of Roo.data.Records.
14779 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14780 * The function must be passed <ul>
14781 * <li>The Record block object</li>
14782 * <li>The "arg" argument from the load function</li>
14783 * <li>A boolean success indicator</li>
14785 * @param {Object} scope The scope in which to call the callback
14786 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14788 load : function(params, reader, callback, scope, arg){
14789 if(this.fireEvent("beforeload", this, params) !== false){
14791 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14793 var url = this.url;
14794 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14796 url += "&_dc=" + (new Date().getTime());
14798 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14801 cb : "stcCallback"+transId,
14802 scriptId : "stcScript"+transId,
14806 callback : callback,
14812 window[trans.cb] = function(o){
14813 conn.handleResponse(o, trans);
14816 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14818 if(this.autoAbort !== false){
14822 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14824 var script = document.createElement("script");
14825 script.setAttribute("src", url);
14826 script.setAttribute("type", "text/javascript");
14827 script.setAttribute("id", trans.scriptId);
14828 this.head.appendChild(script);
14830 this.trans = trans;
14832 callback.call(scope||this, null, arg, false);
14837 isLoading : function(){
14838 return this.trans ? true : false;
14842 * Abort the current server request.
14844 abort : function(){
14845 if(this.isLoading()){
14846 this.destroyTrans(this.trans);
14851 destroyTrans : function(trans, isLoaded){
14852 this.head.removeChild(document.getElementById(trans.scriptId));
14853 clearTimeout(trans.timeoutId);
14855 window[trans.cb] = undefined;
14857 delete window[trans.cb];
14860 // if hasn't been loaded, wait for load to remove it to prevent script error
14861 window[trans.cb] = function(){
14862 window[trans.cb] = undefined;
14864 delete window[trans.cb];
14871 handleResponse : function(o, trans){
14872 this.trans = false;
14873 this.destroyTrans(trans, true);
14876 result = trans.reader.readRecords(o);
14878 this.fireEvent("loadexception", this, o, trans.arg, e);
14879 trans.callback.call(trans.scope||window, null, trans.arg, false);
14882 this.fireEvent("load", this, o, trans.arg);
14883 trans.callback.call(trans.scope||window, result, trans.arg, true);
14887 handleFailure : function(trans){
14888 this.trans = false;
14889 this.destroyTrans(trans, false);
14890 this.fireEvent("loadexception", this, null, trans.arg);
14891 trans.callback.call(trans.scope||window, null, trans.arg, false);
14895 * Ext JS Library 1.1.1
14896 * Copyright(c) 2006-2007, Ext JS, LLC.
14898 * Originally Released Under LGPL - original licence link has changed is not relivant.
14901 * <script type="text/javascript">
14905 * @class Roo.data.JsonReader
14906 * @extends Roo.data.DataReader
14907 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14908 * based on mappings in a provided Roo.data.Record constructor.
14910 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14911 * in the reply previously.
14916 var RecordDef = Roo.data.Record.create([
14917 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14918 {name: 'occupation'} // This field will use "occupation" as the mapping.
14920 var myReader = new Roo.data.JsonReader({
14921 totalProperty: "results", // The property which contains the total dataset size (optional)
14922 root: "rows", // The property which contains an Array of row objects
14923 id: "id" // The property within each row object that provides an ID for the record (optional)
14927 * This would consume a JSON file like this:
14929 { 'results': 2, 'rows': [
14930 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14931 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14934 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14935 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14936 * paged from the remote server.
14937 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14938 * @cfg {String} root name of the property which contains the Array of row objects.
14939 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14940 * @cfg {Array} fields Array of field definition objects
14942 * Create a new JsonReader
14943 * @param {Object} meta Metadata configuration options
14944 * @param {Object} recordType Either an Array of field definition objects,
14945 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14947 Roo.data.JsonReader = function(meta, recordType){
14950 // set some defaults:
14951 Roo.applyIf(meta, {
14952 totalProperty: 'total',
14953 successProperty : 'success',
14958 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14960 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14962 readerType : 'Json',
14965 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14966 * Used by Store query builder to append _requestMeta to params.
14969 metaFromRemote : false,
14971 * This method is only used by a DataProxy which has retrieved data from a remote server.
14972 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14973 * @return {Object} data A data block which is used by an Roo.data.Store object as
14974 * a cache of Roo.data.Records.
14976 read : function(response){
14977 var json = response.responseText;
14979 var o = /* eval:var:o */ eval("("+json+")");
14981 throw {message: "JsonReader.read: Json object not found"};
14987 this.metaFromRemote = true;
14988 this.meta = o.metaData;
14989 this.recordType = Roo.data.Record.create(o.metaData.fields);
14990 this.onMetaChange(this.meta, this.recordType, o);
14992 return this.readRecords(o);
14995 // private function a store will implement
14996 onMetaChange : function(meta, recordType, o){
15003 simpleAccess: function(obj, subsc) {
15010 getJsonAccessor: function(){
15012 return function(expr) {
15014 return(re.test(expr))
15015 ? new Function("obj", "return obj." + expr)
15020 return Roo.emptyFn;
15025 * Create a data block containing Roo.data.Records from an XML document.
15026 * @param {Object} o An object which contains an Array of row objects in the property specified
15027 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15028 * which contains the total size of the dataset.
15029 * @return {Object} data A data block which is used by an Roo.data.Store object as
15030 * a cache of Roo.data.Records.
15032 readRecords : function(o){
15034 * After any data loads, the raw JSON data is available for further custom processing.
15038 var s = this.meta, Record = this.recordType,
15039 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15041 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15043 if(s.totalProperty) {
15044 this.getTotal = this.getJsonAccessor(s.totalProperty);
15046 if(s.successProperty) {
15047 this.getSuccess = this.getJsonAccessor(s.successProperty);
15049 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15051 var g = this.getJsonAccessor(s.id);
15052 this.getId = function(rec) {
15054 return (r === undefined || r === "") ? null : r;
15057 this.getId = function(){return null;};
15060 for(var jj = 0; jj < fl; jj++){
15062 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15063 this.ef[jj] = this.getJsonAccessor(map);
15067 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15068 if(s.totalProperty){
15069 var vt = parseInt(this.getTotal(o), 10);
15074 if(s.successProperty){
15075 var vs = this.getSuccess(o);
15076 if(vs === false || vs === 'false'){
15081 for(var i = 0; i < c; i++){
15084 var id = this.getId(n);
15085 for(var j = 0; j < fl; j++){
15087 var v = this.ef[j](n);
15089 Roo.log('missing convert for ' + f.name);
15093 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15095 var record = new Record(values, id);
15097 records[i] = record;
15103 totalRecords : totalRecords
15106 // used when loading children.. @see loadDataFromChildren
15107 toLoadData: function(rec)
15109 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15110 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15111 return { data : data, total : data.length };
15116 * Ext JS Library 1.1.1
15117 * Copyright(c) 2006-2007, Ext JS, LLC.
15119 * Originally Released Under LGPL - original licence link has changed is not relivant.
15122 * <script type="text/javascript">
15126 * @class Roo.data.ArrayReader
15127 * @extends Roo.data.DataReader
15128 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15129 * Each element of that Array represents a row of data fields. The
15130 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15131 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15135 var RecordDef = Roo.data.Record.create([
15136 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15137 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15139 var myReader = new Roo.data.ArrayReader({
15140 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15144 * This would consume an Array like this:
15146 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15150 * Create a new JsonReader
15151 * @param {Object} meta Metadata configuration options.
15152 * @param {Object|Array} recordType Either an Array of field definition objects
15154 * @cfg {Array} fields Array of field definition objects
15155 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15156 * as specified to {@link Roo.data.Record#create},
15157 * or an {@link Roo.data.Record} object
15160 * created using {@link Roo.data.Record#create}.
15162 Roo.data.ArrayReader = function(meta, recordType)
15164 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15167 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15170 * Create a data block containing Roo.data.Records from an XML document.
15171 * @param {Object} o An Array of row objects which represents the dataset.
15172 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15173 * a cache of Roo.data.Records.
15175 readRecords : function(o)
15177 var sid = this.meta ? this.meta.id : null;
15178 var recordType = this.recordType, fields = recordType.prototype.fields;
15181 for(var i = 0; i < root.length; i++){
15184 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15185 for(var j = 0, jlen = fields.length; j < jlen; j++){
15186 var f = fields.items[j];
15187 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15188 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15190 values[f.name] = v;
15192 var record = new recordType(values, id);
15194 records[records.length] = record;
15198 totalRecords : records.length
15201 // used when loading children.. @see loadDataFromChildren
15202 toLoadData: function(rec)
15204 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15205 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15216 * @class Roo.bootstrap.ComboBox
15217 * @extends Roo.bootstrap.TriggerField
15218 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15219 * @cfg {Boolean} append (true|false) default false
15220 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15221 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15222 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15223 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15224 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15225 * @cfg {Boolean} animate default true
15226 * @cfg {Boolean} emptyResultText only for touch device
15227 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15228 * @cfg {String} emptyTitle default ''
15229 * @cfg {Number} width fixed with? experimental
15231 * Create a new ComboBox.
15232 * @param {Object} config Configuration options
15234 Roo.bootstrap.ComboBox = function(config){
15235 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15239 * Fires when the dropdown list is expanded
15240 * @param {Roo.bootstrap.ComboBox} combo This combo box
15245 * Fires when the dropdown list is collapsed
15246 * @param {Roo.bootstrap.ComboBox} combo This combo box
15250 * @event beforeselect
15251 * Fires before a list item is selected. Return false to cancel the selection.
15252 * @param {Roo.bootstrap.ComboBox} combo This combo box
15253 * @param {Roo.data.Record} record The data record returned from the underlying store
15254 * @param {Number} index The index of the selected item in the dropdown list
15256 'beforeselect' : true,
15259 * Fires when a list item is selected
15260 * @param {Roo.bootstrap.ComboBox} combo This combo box
15261 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15262 * @param {Number} index The index of the selected item in the dropdown list
15266 * @event beforequery
15267 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15268 * The event object passed has these properties:
15269 * @param {Roo.bootstrap.ComboBox} combo This combo box
15270 * @param {String} query The query
15271 * @param {Boolean} forceAll true to force "all" query
15272 * @param {Boolean} cancel true to cancel the query
15273 * @param {Object} e The query event object
15275 'beforequery': true,
15278 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15279 * @param {Roo.bootstrap.ComboBox} combo This combo box
15284 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15285 * @param {Roo.bootstrap.ComboBox} combo This combo box
15286 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15291 * Fires when the remove value from the combobox array
15292 * @param {Roo.bootstrap.ComboBox} combo This combo box
15296 * @event afterremove
15297 * Fires when the remove value from the combobox array
15298 * @param {Roo.bootstrap.ComboBox} combo This combo box
15300 'afterremove' : true,
15302 * @event specialfilter
15303 * Fires when specialfilter
15304 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 'specialfilter' : true,
15309 * Fires when tick the element
15310 * @param {Roo.bootstrap.ComboBox} combo This combo box
15314 * @event touchviewdisplay
15315 * Fires when touch view require special display (default is using displayField)
15316 * @param {Roo.bootstrap.ComboBox} combo This combo box
15317 * @param {Object} cfg set html .
15319 'touchviewdisplay' : true
15324 this.tickItems = [];
15326 this.selectedIndex = -1;
15327 if(this.mode == 'local'){
15328 if(config.queryDelay === undefined){
15329 this.queryDelay = 10;
15331 if(config.minChars === undefined){
15337 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15340 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15341 * rendering into an Roo.Editor, defaults to false)
15344 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15345 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15348 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15351 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15352 * the dropdown list (defaults to undefined, with no header element)
15356 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15360 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15362 listWidth: undefined,
15364 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15365 * mode = 'remote' or 'text' if mode = 'local')
15367 displayField: undefined,
15370 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15371 * mode = 'remote' or 'value' if mode = 'local').
15372 * Note: use of a valueField requires the user make a selection
15373 * in order for a value to be mapped.
15375 valueField: undefined,
15377 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15382 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15383 * field's data value (defaults to the underlying DOM element's name)
15385 hiddenName: undefined,
15387 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15391 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15393 selectedClass: 'active',
15396 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15400 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15401 * anchor positions (defaults to 'tl-bl')
15403 listAlign: 'tl-bl?',
15405 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15409 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15410 * query specified by the allQuery config option (defaults to 'query')
15412 triggerAction: 'query',
15414 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15415 * (defaults to 4, does not apply if editable = false)
15419 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15420 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15424 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15425 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15429 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15430 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15434 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15435 * when editable = true (defaults to false)
15437 selectOnFocus:false,
15439 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15441 queryParam: 'query',
15443 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15444 * when mode = 'remote' (defaults to 'Loading...')
15446 loadingText: 'Loading...',
15448 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15452 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15456 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15457 * traditional select (defaults to true)
15461 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15465 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15469 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15470 * listWidth has a higher value)
15474 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15475 * allow the user to set arbitrary text into the field (defaults to false)
15477 forceSelection:false,
15479 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15480 * if typeAhead = true (defaults to 250)
15482 typeAheadDelay : 250,
15484 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15485 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15487 valueNotFoundText : undefined,
15489 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15491 blockFocus : false,
15494 * @cfg {Boolean} disableClear Disable showing of clear button.
15496 disableClear : false,
15498 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15500 alwaysQuery : false,
15503 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15508 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15510 invalidClass : "has-warning",
15513 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15515 validClass : "has-success",
15518 * @cfg {Boolean} specialFilter (true|false) special filter default false
15520 specialFilter : false,
15523 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15525 mobileTouchView : true,
15528 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15530 useNativeIOS : false,
15533 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15535 mobile_restrict_height : false,
15537 ios_options : false,
15549 btnPosition : 'right',
15550 triggerList : true,
15551 showToggleBtn : true,
15553 emptyResultText: 'Empty',
15554 triggerText : 'Select',
15558 // element that contains real text value.. (when hidden is used..)
15560 getAutoCreate : function()
15565 * Render classic select for iso
15568 if(Roo.isIOS && this.useNativeIOS){
15569 cfg = this.getAutoCreateNativeIOS();
15577 if(Roo.isTouch && this.mobileTouchView){
15578 cfg = this.getAutoCreateTouchView();
15585 if(!this.tickable){
15586 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15591 * ComboBox with tickable selections
15594 var align = this.labelAlign || this.parentLabelAlign();
15597 cls : 'form-group roo-combobox-tickable' //input-group
15600 var btn_text_select = '';
15601 var btn_text_done = '';
15602 var btn_text_cancel = '';
15604 if (this.btn_text_show) {
15605 btn_text_select = 'Select';
15606 btn_text_done = 'Done';
15607 btn_text_cancel = 'Cancel';
15612 cls : 'tickable-buttons',
15617 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15618 //html : this.triggerText
15619 html: btn_text_select
15625 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15627 html: btn_text_done
15633 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15635 html: btn_text_cancel
15641 buttons.cn.unshift({
15643 cls: 'roo-select2-search-field-input'
15649 Roo.each(buttons.cn, function(c){
15651 c.cls += ' btn-' + _this.size;
15654 if (_this.disabled) {
15661 style : 'display: contents',
15666 cls: 'form-hidden-field'
15670 cls: 'roo-select2-choices',
15674 cls: 'roo-select2-search-field',
15685 cls: 'roo-select2-container input-group roo-select2-container-multi',
15691 // cls: 'typeahead typeahead-long dropdown-menu',
15692 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15697 if(this.hasFeedback && !this.allowBlank){
15701 cls: 'glyphicon form-control-feedback'
15704 combobox.cn.push(feedback);
15711 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15712 tooltip : 'This field is required'
15714 if (Roo.bootstrap.version == 4) {
15717 style : 'display:none'
15720 if (align ==='left' && this.fieldLabel.length) {
15722 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15729 cls : 'control-label col-form-label',
15730 html : this.fieldLabel
15742 var labelCfg = cfg.cn[1];
15743 var contentCfg = cfg.cn[2];
15746 if(this.indicatorpos == 'right'){
15752 cls : 'control-label col-form-label',
15756 html : this.fieldLabel
15772 labelCfg = cfg.cn[0];
15773 contentCfg = cfg.cn[1];
15777 if(this.labelWidth > 12){
15778 labelCfg.style = "width: " + this.labelWidth + 'px';
15780 if(this.width * 1 > 0){
15781 contentCfg.style = "width: " + this.width + 'px';
15783 if(this.labelWidth < 13 && this.labelmd == 0){
15784 this.labelmd = this.labelWidth;
15787 if(this.labellg > 0){
15788 labelCfg.cls += ' col-lg-' + this.labellg;
15789 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15792 if(this.labelmd > 0){
15793 labelCfg.cls += ' col-md-' + this.labelmd;
15794 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15797 if(this.labelsm > 0){
15798 labelCfg.cls += ' col-sm-' + this.labelsm;
15799 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15802 if(this.labelxs > 0){
15803 labelCfg.cls += ' col-xs-' + this.labelxs;
15804 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15808 } else if ( this.fieldLabel.length) {
15809 // Roo.log(" label");
15814 //cls : 'input-group-addon',
15815 html : this.fieldLabel
15820 if(this.indicatorpos == 'right'){
15824 //cls : 'input-group-addon',
15825 html : this.fieldLabel
15835 // Roo.log(" no label && no align");
15842 ['xs','sm','md','lg'].map(function(size){
15843 if (settings[size]) {
15844 cfg.cls += ' col-' + size + '-' + settings[size];
15852 _initEventsCalled : false,
15855 initEvents: function()
15857 if (this._initEventsCalled) { // as we call render... prevent looping...
15860 this._initEventsCalled = true;
15863 throw "can not find store for combo";
15866 this.indicator = this.indicatorEl();
15868 this.store = Roo.factory(this.store, Roo.data);
15869 this.store.parent = this;
15871 // if we are building from html. then this element is so complex, that we can not really
15872 // use the rendered HTML.
15873 // so we have to trash and replace the previous code.
15874 if (Roo.XComponent.build_from_html) {
15875 // remove this element....
15876 var e = this.el.dom, k=0;
15877 while (e ) { e = e.previousSibling; ++k;}
15882 this.rendered = false;
15884 this.render(this.parent().getChildContainer(true), k);
15887 if(Roo.isIOS && this.useNativeIOS){
15888 this.initIOSView();
15896 if(Roo.isTouch && this.mobileTouchView){
15897 this.initTouchView();
15902 this.initTickableEvents();
15906 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15908 if(this.hiddenName){
15910 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15912 this.hiddenField.dom.value =
15913 this.hiddenValue !== undefined ? this.hiddenValue :
15914 this.value !== undefined ? this.value : '';
15916 // prevent input submission
15917 this.el.dom.removeAttribute('name');
15918 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15923 // this.el.dom.setAttribute('autocomplete', 'off');
15926 var cls = 'x-combo-list';
15928 //this.list = new Roo.Layer({
15929 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15935 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15936 _this.list.setWidth(lw);
15939 this.list.on('mouseover', this.onViewOver, this);
15940 this.list.on('mousemove', this.onViewMove, this);
15941 this.list.on('scroll', this.onViewScroll, this);
15944 this.list.swallowEvent('mousewheel');
15945 this.assetHeight = 0;
15948 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15949 this.assetHeight += this.header.getHeight();
15952 this.innerList = this.list.createChild({cls:cls+'-inner'});
15953 this.innerList.on('mouseover', this.onViewOver, this);
15954 this.innerList.on('mousemove', this.onViewMove, this);
15955 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15957 if(this.allowBlank && !this.pageSize && !this.disableClear){
15958 this.footer = this.list.createChild({cls:cls+'-ft'});
15959 this.pageTb = new Roo.Toolbar(this.footer);
15963 this.footer = this.list.createChild({cls:cls+'-ft'});
15964 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15965 {pageSize: this.pageSize});
15969 if (this.pageTb && this.allowBlank && !this.disableClear) {
15971 this.pageTb.add(new Roo.Toolbar.Fill(), {
15972 cls: 'x-btn-icon x-btn-clear',
15974 handler: function()
15977 _this.clearValue();
15978 _this.onSelect(false, -1);
15983 this.assetHeight += this.footer.getHeight();
15988 this.tpl = Roo.bootstrap.version == 4 ?
15989 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15990 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15993 this.view = new Roo.View(this.list, this.tpl, {
15994 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15996 //this.view.wrapEl.setDisplayed(false);
15997 this.view.on('click', this.onViewClick, this);
16000 this.store.on('beforeload', this.onBeforeLoad, this);
16001 this.store.on('load', this.onLoad, this);
16002 this.store.on('loadexception', this.onLoadException, this);
16004 if(this.resizable){
16005 this.resizer = new Roo.Resizable(this.list, {
16006 pinned:true, handles:'se'
16008 this.resizer.on('resize', function(r, w, h){
16009 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16010 this.listWidth = w;
16011 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16012 this.restrictHeight();
16014 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16017 if(!this.editable){
16018 this.editable = true;
16019 this.setEditable(false);
16024 if (typeof(this.events.add.listeners) != 'undefined') {
16026 this.addicon = this.wrap.createChild(
16027 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16029 this.addicon.on('click', function(e) {
16030 this.fireEvent('add', this);
16033 if (typeof(this.events.edit.listeners) != 'undefined') {
16035 this.editicon = this.wrap.createChild(
16036 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16037 if (this.addicon) {
16038 this.editicon.setStyle('margin-left', '40px');
16040 this.editicon.on('click', function(e) {
16042 // we fire even if inothing is selected..
16043 this.fireEvent('edit', this, this.lastData );
16049 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16050 "up" : function(e){
16051 this.inKeyMode = true;
16055 "down" : function(e){
16056 if(!this.isExpanded()){
16057 this.onTriggerClick();
16059 this.inKeyMode = true;
16064 "enter" : function(e){
16065 // this.onViewClick();
16069 if(this.fireEvent("specialkey", this, e)){
16070 this.onViewClick(false);
16076 "esc" : function(e){
16080 "tab" : function(e){
16083 if(this.fireEvent("specialkey", this, e)){
16084 this.onViewClick(false);
16092 doRelay : function(foo, bar, hname){
16093 if(hname == 'down' || this.scope.isExpanded()){
16094 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16103 this.queryDelay = Math.max(this.queryDelay || 10,
16104 this.mode == 'local' ? 10 : 250);
16107 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16109 if(this.typeAhead){
16110 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16112 if(this.editable !== false){
16113 this.inputEl().on("keyup", this.onKeyUp, this);
16115 if(this.forceSelection){
16116 this.inputEl().on('blur', this.doForce, this);
16120 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16121 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16125 initTickableEvents: function()
16129 if(this.hiddenName){
16131 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16133 this.hiddenField.dom.value =
16134 this.hiddenValue !== undefined ? this.hiddenValue :
16135 this.value !== undefined ? this.value : '';
16137 // prevent input submission
16138 this.el.dom.removeAttribute('name');
16139 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16144 // this.list = this.el.select('ul.dropdown-menu',true).first();
16146 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16147 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16148 if(this.triggerList){
16149 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16152 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16153 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16155 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16156 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16158 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16159 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16161 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16162 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16163 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16166 this.cancelBtn.hide();
16171 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16172 _this.list.setWidth(lw);
16175 this.list.on('mouseover', this.onViewOver, this);
16176 this.list.on('mousemove', this.onViewMove, this);
16178 this.list.on('scroll', this.onViewScroll, this);
16181 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16182 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16185 this.view = new Roo.View(this.list, this.tpl, {
16190 selectedClass: this.selectedClass
16193 //this.view.wrapEl.setDisplayed(false);
16194 this.view.on('click', this.onViewClick, this);
16198 this.store.on('beforeload', this.onBeforeLoad, this);
16199 this.store.on('load', this.onLoad, this);
16200 this.store.on('loadexception', this.onLoadException, this);
16203 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16204 "up" : function(e){
16205 this.inKeyMode = true;
16209 "down" : function(e){
16210 this.inKeyMode = true;
16214 "enter" : function(e){
16215 if(this.fireEvent("specialkey", this, e)){
16216 this.onViewClick(false);
16222 "esc" : function(e){
16223 this.onTickableFooterButtonClick(e, false, false);
16226 "tab" : function(e){
16227 this.fireEvent("specialkey", this, e);
16229 this.onTickableFooterButtonClick(e, false, false);
16236 doRelay : function(e, fn, key){
16237 if(this.scope.isExpanded()){
16238 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16247 this.queryDelay = Math.max(this.queryDelay || 10,
16248 this.mode == 'local' ? 10 : 250);
16251 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16253 if(this.typeAhead){
16254 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16257 if(this.editable !== false){
16258 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16261 this.indicator = this.indicatorEl();
16263 if(this.indicator){
16264 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16265 this.indicator.hide();
16270 onDestroy : function(){
16272 this.view.setStore(null);
16273 this.view.el.removeAllListeners();
16274 this.view.el.remove();
16275 this.view.purgeListeners();
16278 this.list.dom.innerHTML = '';
16282 this.store.un('beforeload', this.onBeforeLoad, this);
16283 this.store.un('load', this.onLoad, this);
16284 this.store.un('loadexception', this.onLoadException, this);
16286 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16290 fireKey : function(e){
16291 if(e.isNavKeyPress() && !this.list.isVisible()){
16292 this.fireEvent("specialkey", this, e);
16297 onResize: function(w, h)
16301 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16303 // if(typeof w != 'number'){
16304 // // we do not handle it!?!?
16307 // var tw = this.trigger.getWidth();
16308 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16309 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16311 // this.inputEl().setWidth( this.adjustWidth('input', x));
16313 // //this.trigger.setStyle('left', x+'px');
16315 // if(this.list && this.listWidth === undefined){
16316 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16317 // this.list.setWidth(lw);
16318 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16326 * Allow or prevent the user from directly editing the field text. If false is passed,
16327 * the user will only be able to select from the items defined in the dropdown list. This method
16328 * is the runtime equivalent of setting the 'editable' config option at config time.
16329 * @param {Boolean} value True to allow the user to directly edit the field text
16331 setEditable : function(value){
16332 if(value == this.editable){
16335 this.editable = value;
16337 this.inputEl().dom.setAttribute('readOnly', true);
16338 this.inputEl().on('mousedown', this.onTriggerClick, this);
16339 this.inputEl().addClass('x-combo-noedit');
16341 this.inputEl().dom.setAttribute('readOnly', false);
16342 this.inputEl().un('mousedown', this.onTriggerClick, this);
16343 this.inputEl().removeClass('x-combo-noedit');
16349 onBeforeLoad : function(combo,opts){
16350 if(!this.hasFocus){
16354 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16356 this.restrictHeight();
16357 this.selectedIndex = -1;
16361 onLoad : function(){
16363 this.hasQuery = false;
16365 if(!this.hasFocus){
16369 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16370 this.loading.hide();
16373 if(this.store.getCount() > 0){
16376 this.restrictHeight();
16377 if(this.lastQuery == this.allQuery){
16378 if(this.editable && !this.tickable){
16379 this.inputEl().dom.select();
16383 !this.selectByValue(this.value, true) &&
16386 !this.store.lastOptions ||
16387 typeof(this.store.lastOptions.add) == 'undefined' ||
16388 this.store.lastOptions.add != true
16391 this.select(0, true);
16394 if(this.autoFocus){
16397 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16398 this.taTask.delay(this.typeAheadDelay);
16402 this.onEmptyResults();
16408 onLoadException : function()
16410 this.hasQuery = false;
16412 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16413 this.loading.hide();
16416 if(this.tickable && this.editable){
16421 // only causes errors at present
16422 //Roo.log(this.store.reader.jsonData);
16423 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16425 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16431 onTypeAhead : function(){
16432 if(this.store.getCount() > 0){
16433 var r = this.store.getAt(0);
16434 var newValue = r.data[this.displayField];
16435 var len = newValue.length;
16436 var selStart = this.getRawValue().length;
16438 if(selStart != len){
16439 this.setRawValue(newValue);
16440 this.selectText(selStart, newValue.length);
16446 onSelect : function(record, index){
16448 if(this.fireEvent('beforeselect', this, record, index) !== false){
16450 this.setFromData(index > -1 ? record.data : false);
16453 this.fireEvent('select', this, record, index);
16458 * Returns the currently selected field value or empty string if no value is set.
16459 * @return {String} value The selected value
16461 getValue : function()
16463 if(Roo.isIOS && this.useNativeIOS){
16464 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16468 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16471 if(this.valueField){
16472 return typeof this.value != 'undefined' ? this.value : '';
16474 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16478 getRawValue : function()
16480 if(Roo.isIOS && this.useNativeIOS){
16481 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16484 var v = this.inputEl().getValue();
16490 * Clears any text/value currently set in the field
16492 clearValue : function(){
16494 if(this.hiddenField){
16495 this.hiddenField.dom.value = '';
16498 this.setRawValue('');
16499 this.lastSelectionText = '';
16500 this.lastData = false;
16502 var close = this.closeTriggerEl();
16513 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16514 * will be displayed in the field. If the value does not match the data value of an existing item,
16515 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16516 * Otherwise the field will be blank (although the value will still be set).
16517 * @param {String} value The value to match
16519 setValue : function(v)
16521 if(Roo.isIOS && this.useNativeIOS){
16522 this.setIOSValue(v);
16532 if(this.valueField){
16533 var r = this.findRecord(this.valueField, v);
16535 text = r.data[this.displayField];
16536 }else if(this.valueNotFoundText !== undefined){
16537 text = this.valueNotFoundText;
16540 this.lastSelectionText = text;
16541 if(this.hiddenField){
16542 this.hiddenField.dom.value = v;
16544 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16547 var close = this.closeTriggerEl();
16550 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16556 * @property {Object} the last set data for the element
16561 * Sets the value of the field based on a object which is related to the record format for the store.
16562 * @param {Object} value the value to set as. or false on reset?
16564 setFromData : function(o){
16571 var dv = ''; // display value
16572 var vv = ''; // value value..
16574 if (this.displayField) {
16575 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16577 // this is an error condition!!!
16578 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16581 if(this.valueField){
16582 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16585 var close = this.closeTriggerEl();
16588 if(dv.length || vv * 1 > 0){
16590 this.blockFocus=true;
16596 if(this.hiddenField){
16597 this.hiddenField.dom.value = vv;
16599 this.lastSelectionText = dv;
16600 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16604 // no hidden field.. - we store the value in 'value', but still display
16605 // display field!!!!
16606 this.lastSelectionText = dv;
16607 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16614 reset : function(){
16615 // overridden so that last data is reset..
16622 this.setValue(this.originalValue);
16623 //this.clearInvalid();
16624 this.lastData = false;
16626 this.view.clearSelections();
16632 findRecord : function(prop, value){
16634 if(this.store.getCount() > 0){
16635 this.store.each(function(r){
16636 if(r.data[prop] == value){
16646 getName: function()
16648 // returns hidden if it's set..
16649 if (!this.rendered) {return ''};
16650 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16654 onViewMove : function(e, t){
16655 this.inKeyMode = false;
16659 onViewOver : function(e, t){
16660 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16663 var item = this.view.findItemFromChild(t);
16666 var index = this.view.indexOf(item);
16667 this.select(index, false);
16672 onViewClick : function(view, doFocus, el, e)
16674 var index = this.view.getSelectedIndexes()[0];
16676 var r = this.store.getAt(index);
16680 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16687 Roo.each(this.tickItems, function(v,k){
16689 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16691 _this.tickItems.splice(k, 1);
16693 if(typeof(e) == 'undefined' && view == false){
16694 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16706 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16707 this.tickItems.push(r.data);
16710 if(typeof(e) == 'undefined' && view == false){
16711 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16718 this.onSelect(r, index);
16720 if(doFocus !== false && !this.blockFocus){
16721 this.inputEl().focus();
16726 restrictHeight : function(){
16727 //this.innerList.dom.style.height = '';
16728 //var inner = this.innerList.dom;
16729 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16730 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16731 //this.list.beginUpdate();
16732 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16733 this.list.alignTo(this.inputEl(), this.listAlign);
16734 this.list.alignTo(this.inputEl(), this.listAlign);
16735 //this.list.endUpdate();
16739 onEmptyResults : function(){
16741 if(this.tickable && this.editable){
16742 this.hasFocus = false;
16743 this.restrictHeight();
16751 * Returns true if the dropdown list is expanded, else false.
16753 isExpanded : function(){
16754 return this.list.isVisible();
16758 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16759 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16760 * @param {String} value The data value of the item to select
16761 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16762 * selected item if it is not currently in view (defaults to true)
16763 * @return {Boolean} True if the value matched an item in the list, else false
16765 selectByValue : function(v, scrollIntoView){
16766 if(v !== undefined && v !== null){
16767 var r = this.findRecord(this.valueField || this.displayField, v);
16769 this.select(this.store.indexOf(r), scrollIntoView);
16777 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16778 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16779 * @param {Number} index The zero-based index of the list item to select
16780 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16781 * selected item if it is not currently in view (defaults to true)
16783 select : function(index, scrollIntoView){
16784 this.selectedIndex = index;
16785 this.view.select(index);
16786 if(scrollIntoView !== false){
16787 var el = this.view.getNode(index);
16789 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16792 this.list.scrollChildIntoView(el, false);
16798 selectNext : function(){
16799 var ct = this.store.getCount();
16801 if(this.selectedIndex == -1){
16803 }else if(this.selectedIndex < ct-1){
16804 this.select(this.selectedIndex+1);
16810 selectPrev : function(){
16811 var ct = this.store.getCount();
16813 if(this.selectedIndex == -1){
16815 }else if(this.selectedIndex != 0){
16816 this.select(this.selectedIndex-1);
16822 onKeyUp : function(e){
16823 if(this.editable !== false && !e.isSpecialKey()){
16824 this.lastKey = e.getKey();
16825 this.dqTask.delay(this.queryDelay);
16830 validateBlur : function(){
16831 return !this.list || !this.list.isVisible();
16835 initQuery : function(){
16837 var v = this.getRawValue();
16839 if(this.tickable && this.editable){
16840 v = this.tickableInputEl().getValue();
16847 doForce : function(){
16848 if(this.inputEl().dom.value.length > 0){
16849 this.inputEl().dom.value =
16850 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16856 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16857 * query allowing the query action to be canceled if needed.
16858 * @param {String} query The SQL query to execute
16859 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16860 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16861 * saved in the current store (defaults to false)
16863 doQuery : function(q, forceAll){
16865 if(q === undefined || q === null){
16870 forceAll: forceAll,
16874 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16879 forceAll = qe.forceAll;
16880 if(forceAll === true || (q.length >= this.minChars)){
16882 this.hasQuery = true;
16884 if(this.lastQuery != q || this.alwaysQuery){
16885 this.lastQuery = q;
16886 if(this.mode == 'local'){
16887 this.selectedIndex = -1;
16889 this.store.clearFilter();
16892 if(this.specialFilter){
16893 this.fireEvent('specialfilter', this);
16898 this.store.filter(this.displayField, q);
16901 this.store.fireEvent("datachanged", this.store);
16908 this.store.baseParams[this.queryParam] = q;
16910 var options = {params : this.getParams(q)};
16913 options.add = true;
16914 options.params.start = this.page * this.pageSize;
16917 this.store.load(options);
16920 * this code will make the page width larger, at the beginning, the list not align correctly,
16921 * we should expand the list on onLoad
16922 * so command out it
16927 this.selectedIndex = -1;
16932 this.loadNext = false;
16936 getParams : function(q){
16938 //p[this.queryParam] = q;
16942 p.limit = this.pageSize;
16948 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16950 collapse : function(){
16951 if(!this.isExpanded()){
16957 this.hasFocus = false;
16961 this.cancelBtn.hide();
16962 this.trigger.show();
16965 this.tickableInputEl().dom.value = '';
16966 this.tickableInputEl().blur();
16971 Roo.get(document).un('mousedown', this.collapseIf, this);
16972 Roo.get(document).un('mousewheel', this.collapseIf, this);
16973 if (!this.editable) {
16974 Roo.get(document).un('keydown', this.listKeyPress, this);
16976 this.fireEvent('collapse', this);
16982 collapseIf : function(e){
16983 var in_combo = e.within(this.el);
16984 var in_list = e.within(this.list);
16985 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16987 if (in_combo || in_list || is_list) {
16988 //e.stopPropagation();
16993 this.onTickableFooterButtonClick(e, false, false);
17001 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17003 expand : function(){
17005 if(this.isExpanded() || !this.hasFocus){
17009 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17010 this.list.setWidth(lw);
17016 this.restrictHeight();
17020 this.tickItems = Roo.apply([], this.item);
17023 this.cancelBtn.show();
17024 this.trigger.hide();
17027 this.tickableInputEl().focus();
17032 Roo.get(document).on('mousedown', this.collapseIf, this);
17033 Roo.get(document).on('mousewheel', this.collapseIf, this);
17034 if (!this.editable) {
17035 Roo.get(document).on('keydown', this.listKeyPress, this);
17038 this.fireEvent('expand', this);
17042 // Implements the default empty TriggerField.onTriggerClick function
17043 onTriggerClick : function(e)
17045 Roo.log('trigger click');
17047 if(this.disabled || !this.triggerList){
17052 this.loadNext = false;
17054 if(this.isExpanded()){
17056 if (!this.blockFocus) {
17057 this.inputEl().focus();
17061 this.hasFocus = true;
17062 if(this.triggerAction == 'all') {
17063 this.doQuery(this.allQuery, true);
17065 this.doQuery(this.getRawValue());
17067 if (!this.blockFocus) {
17068 this.inputEl().focus();
17073 onTickableTriggerClick : function(e)
17080 this.loadNext = false;
17081 this.hasFocus = true;
17083 if(this.triggerAction == 'all') {
17084 this.doQuery(this.allQuery, true);
17086 this.doQuery(this.getRawValue());
17090 onSearchFieldClick : function(e)
17092 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17093 this.onTickableFooterButtonClick(e, false, false);
17097 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17102 this.loadNext = false;
17103 this.hasFocus = true;
17105 if(this.triggerAction == 'all') {
17106 this.doQuery(this.allQuery, true);
17108 this.doQuery(this.getRawValue());
17112 listKeyPress : function(e)
17114 //Roo.log('listkeypress');
17115 // scroll to first matching element based on key pres..
17116 if (e.isSpecialKey()) {
17119 var k = String.fromCharCode(e.getKey()).toUpperCase();
17122 var csel = this.view.getSelectedNodes();
17123 var cselitem = false;
17125 var ix = this.view.indexOf(csel[0]);
17126 cselitem = this.store.getAt(ix);
17127 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17133 this.store.each(function(v) {
17135 // start at existing selection.
17136 if (cselitem.id == v.id) {
17142 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17143 match = this.store.indexOf(v);
17149 if (match === false) {
17150 return true; // no more action?
17153 this.view.select(match);
17154 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17155 sn.scrollIntoView(sn.dom.parentNode, false);
17158 onViewScroll : function(e, t){
17160 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){
17164 this.hasQuery = true;
17166 this.loading = this.list.select('.loading', true).first();
17168 if(this.loading === null){
17169 this.list.createChild({
17171 cls: 'loading roo-select2-more-results roo-select2-active',
17172 html: 'Loading more results...'
17175 this.loading = this.list.select('.loading', true).first();
17177 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17179 this.loading.hide();
17182 this.loading.show();
17187 this.loadNext = true;
17189 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17194 addItem : function(o)
17196 var dv = ''; // display value
17198 if (this.displayField) {
17199 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17201 // this is an error condition!!!
17202 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17209 var choice = this.choices.createChild({
17211 cls: 'roo-select2-search-choice',
17220 cls: 'roo-select2-search-choice-close fa fa-times',
17225 }, this.searchField);
17227 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17229 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17237 this.inputEl().dom.value = '';
17242 onRemoveItem : function(e, _self, o)
17244 e.preventDefault();
17246 this.lastItem = Roo.apply([], this.item);
17248 var index = this.item.indexOf(o.data) * 1;
17251 Roo.log('not this item?!');
17255 this.item.splice(index, 1);
17260 this.fireEvent('remove', this, e);
17266 syncValue : function()
17268 if(!this.item.length){
17275 Roo.each(this.item, function(i){
17276 if(_this.valueField){
17277 value.push(i[_this.valueField]);
17284 this.value = value.join(',');
17286 if(this.hiddenField){
17287 this.hiddenField.dom.value = this.value;
17290 this.store.fireEvent("datachanged", this.store);
17295 clearItem : function()
17297 if(!this.multiple){
17303 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17311 if(this.tickable && !Roo.isTouch){
17312 this.view.refresh();
17316 inputEl: function ()
17318 if(Roo.isIOS && this.useNativeIOS){
17319 return this.el.select('select.roo-ios-select', true).first();
17322 if(Roo.isTouch && this.mobileTouchView){
17323 return this.el.select('input.form-control',true).first();
17327 return this.searchField;
17330 return this.el.select('input.form-control',true).first();
17333 onTickableFooterButtonClick : function(e, btn, el)
17335 e.preventDefault();
17337 this.lastItem = Roo.apply([], this.item);
17339 if(btn && btn.name == 'cancel'){
17340 this.tickItems = Roo.apply([], this.item);
17349 Roo.each(this.tickItems, function(o){
17357 validate : function()
17359 if(this.getVisibilityEl().hasClass('hidden')){
17363 var v = this.getRawValue();
17366 v = this.getValue();
17369 if(this.disabled || this.allowBlank || v.length){
17374 this.markInvalid();
17378 tickableInputEl : function()
17380 if(!this.tickable || !this.editable){
17381 return this.inputEl();
17384 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17388 getAutoCreateTouchView : function()
17393 cls: 'form-group' //input-group
17399 type : this.inputType,
17400 cls : 'form-control x-combo-noedit',
17401 autocomplete: 'new-password',
17402 placeholder : this.placeholder || '',
17407 input.name = this.name;
17411 input.cls += ' input-' + this.size;
17414 if (this.disabled) {
17415 input.disabled = true;
17419 cls : 'roo-combobox-wrap',
17426 inputblock.cls += ' input-group';
17428 inputblock.cn.unshift({
17430 cls : 'input-group-addon input-group-prepend input-group-text',
17435 if(this.removable && !this.multiple){
17436 inputblock.cls += ' roo-removable';
17438 inputblock.cn.push({
17441 cls : 'roo-combo-removable-btn close'
17445 if(this.hasFeedback && !this.allowBlank){
17447 inputblock.cls += ' has-feedback';
17449 inputblock.cn.push({
17451 cls: 'glyphicon form-control-feedback'
17458 inputblock.cls += (this.before) ? '' : ' input-group';
17460 inputblock.cn.push({
17462 cls : 'input-group-addon input-group-append input-group-text',
17468 var ibwrap = inputblock;
17473 cls: 'roo-select2-choices',
17477 cls: 'roo-select2-search-field',
17490 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17495 cls: 'form-hidden-field'
17501 if(!this.multiple && this.showToggleBtn){
17507 if (this.caret != false) {
17510 cls: 'fa fa-' + this.caret
17517 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17519 Roo.bootstrap.version == 3 ? caret : '',
17522 cls: 'combobox-clear',
17536 combobox.cls += ' roo-select2-container-multi';
17539 var align = this.labelAlign || this.parentLabelAlign();
17541 if (align ==='left' && this.fieldLabel.length) {
17546 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17547 tooltip : 'This field is required'
17551 cls : 'control-label col-form-label',
17552 html : this.fieldLabel
17556 cls : 'roo-combobox-wrap ',
17563 var labelCfg = cfg.cn[1];
17564 var contentCfg = cfg.cn[2];
17567 if(this.indicatorpos == 'right'){
17572 cls : 'control-label col-form-label',
17576 html : this.fieldLabel
17580 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17581 tooltip : 'This field is required'
17586 cls : "roo-combobox-wrap ",
17594 labelCfg = cfg.cn[0];
17595 contentCfg = cfg.cn[1];
17600 if(this.labelWidth > 12){
17601 labelCfg.style = "width: " + this.labelWidth + 'px';
17604 if(this.labelWidth < 13 && this.labelmd == 0){
17605 this.labelmd = this.labelWidth;
17608 if(this.labellg > 0){
17609 labelCfg.cls += ' col-lg-' + this.labellg;
17610 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17613 if(this.labelmd > 0){
17614 labelCfg.cls += ' col-md-' + this.labelmd;
17615 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17618 if(this.labelsm > 0){
17619 labelCfg.cls += ' col-sm-' + this.labelsm;
17620 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17623 if(this.labelxs > 0){
17624 labelCfg.cls += ' col-xs-' + this.labelxs;
17625 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17629 } else if ( this.fieldLabel.length) {
17633 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17634 tooltip : 'This field is required'
17638 cls : 'control-label',
17639 html : this.fieldLabel
17650 if(this.indicatorpos == 'right'){
17654 cls : 'control-label',
17655 html : this.fieldLabel,
17659 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17660 tooltip : 'This field is required'
17677 var settings = this;
17679 ['xs','sm','md','lg'].map(function(size){
17680 if (settings[size]) {
17681 cfg.cls += ' col-' + size + '-' + settings[size];
17688 initTouchView : function()
17690 this.renderTouchView();
17692 this.touchViewEl.on('scroll', function(){
17693 this.el.dom.scrollTop = 0;
17696 this.originalValue = this.getValue();
17698 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17700 this.inputEl().on("click", this.showTouchView, this);
17701 if (this.triggerEl) {
17702 this.triggerEl.on("click", this.showTouchView, this);
17706 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17707 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17709 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17711 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17712 this.store.on('load', this.onTouchViewLoad, this);
17713 this.store.on('loadexception', this.onTouchViewLoadException, this);
17715 if(this.hiddenName){
17717 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17719 this.hiddenField.dom.value =
17720 this.hiddenValue !== undefined ? this.hiddenValue :
17721 this.value !== undefined ? this.value : '';
17723 this.el.dom.removeAttribute('name');
17724 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17728 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17729 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17732 if(this.removable && !this.multiple){
17733 var close = this.closeTriggerEl();
17735 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17736 close.on('click', this.removeBtnClick, this, close);
17740 * fix the bug in Safari iOS8
17742 this.inputEl().on("focus", function(e){
17743 document.activeElement.blur();
17746 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17753 renderTouchView : function()
17755 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17756 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17758 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17759 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17761 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17762 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17763 this.touchViewBodyEl.setStyle('overflow', 'auto');
17765 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17766 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17768 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17769 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17773 showTouchView : function()
17779 this.touchViewHeaderEl.hide();
17781 if(this.modalTitle.length){
17782 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17783 this.touchViewHeaderEl.show();
17786 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17787 this.touchViewEl.show();
17789 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17791 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17792 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17794 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17796 if(this.modalTitle.length){
17797 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17800 this.touchViewBodyEl.setHeight(bodyHeight);
17804 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17806 this.touchViewEl.addClass(['in','show']);
17809 if(this._touchViewMask){
17810 Roo.get(document.body).addClass("x-body-masked");
17811 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17812 this._touchViewMask.setStyle('z-index', 10000);
17813 this._touchViewMask.addClass('show');
17816 this.doTouchViewQuery();
17820 hideTouchView : function()
17822 this.touchViewEl.removeClass(['in','show']);
17826 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17828 this.touchViewEl.setStyle('display', 'none');
17831 if(this._touchViewMask){
17832 this._touchViewMask.removeClass('show');
17833 Roo.get(document.body).removeClass("x-body-masked");
17837 setTouchViewValue : function()
17844 Roo.each(this.tickItems, function(o){
17849 this.hideTouchView();
17852 doTouchViewQuery : function()
17861 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17865 if(!this.alwaysQuery || this.mode == 'local'){
17866 this.onTouchViewLoad();
17873 onTouchViewBeforeLoad : function(combo,opts)
17879 onTouchViewLoad : function()
17881 if(this.store.getCount() < 1){
17882 this.onTouchViewEmptyResults();
17886 this.clearTouchView();
17888 var rawValue = this.getRawValue();
17890 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17892 this.tickItems = [];
17894 this.store.data.each(function(d, rowIndex){
17895 var row = this.touchViewListGroup.createChild(template);
17897 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17898 row.addClass(d.data.cls);
17901 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17904 html : d.data[this.displayField]
17907 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17908 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17911 row.removeClass('selected');
17912 if(!this.multiple && this.valueField &&
17913 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17916 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17917 row.addClass('selected');
17920 if(this.multiple && this.valueField &&
17921 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17925 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17926 this.tickItems.push(d.data);
17929 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17933 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17935 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17937 if(this.modalTitle.length){
17938 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17941 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17943 if(this.mobile_restrict_height && listHeight < bodyHeight){
17944 this.touchViewBodyEl.setHeight(listHeight);
17949 if(firstChecked && listHeight > bodyHeight){
17950 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17955 onTouchViewLoadException : function()
17957 this.hideTouchView();
17960 onTouchViewEmptyResults : function()
17962 this.clearTouchView();
17964 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17966 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17970 clearTouchView : function()
17972 this.touchViewListGroup.dom.innerHTML = '';
17975 onTouchViewClick : function(e, el, o)
17977 e.preventDefault();
17980 var rowIndex = o.rowIndex;
17982 var r = this.store.getAt(rowIndex);
17984 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17986 if(!this.multiple){
17987 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17988 c.dom.removeAttribute('checked');
17991 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17993 this.setFromData(r.data);
17995 var close = this.closeTriggerEl();
18001 this.hideTouchView();
18003 this.fireEvent('select', this, r, rowIndex);
18008 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18009 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18010 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18014 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18015 this.addItem(r.data);
18016 this.tickItems.push(r.data);
18020 getAutoCreateNativeIOS : function()
18023 cls: 'form-group' //input-group,
18028 cls : 'roo-ios-select'
18032 combobox.name = this.name;
18035 if (this.disabled) {
18036 combobox.disabled = true;
18039 var settings = this;
18041 ['xs','sm','md','lg'].map(function(size){
18042 if (settings[size]) {
18043 cfg.cls += ' col-' + size + '-' + settings[size];
18053 initIOSView : function()
18055 this.store.on('load', this.onIOSViewLoad, this);
18060 onIOSViewLoad : function()
18062 if(this.store.getCount() < 1){
18066 this.clearIOSView();
18068 if(this.allowBlank) {
18070 var default_text = '-- SELECT --';
18072 if(this.placeholder.length){
18073 default_text = this.placeholder;
18076 if(this.emptyTitle.length){
18077 default_text += ' - ' + this.emptyTitle + ' -';
18080 var opt = this.inputEl().createChild({
18083 html : default_text
18087 o[this.valueField] = 0;
18088 o[this.displayField] = default_text;
18090 this.ios_options.push({
18097 this.store.data.each(function(d, rowIndex){
18101 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18102 html = d.data[this.displayField];
18107 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18108 value = d.data[this.valueField];
18117 if(this.value == d.data[this.valueField]){
18118 option['selected'] = true;
18121 var opt = this.inputEl().createChild(option);
18123 this.ios_options.push({
18130 this.inputEl().on('change', function(){
18131 this.fireEvent('select', this);
18136 clearIOSView: function()
18138 this.inputEl().dom.innerHTML = '';
18140 this.ios_options = [];
18143 setIOSValue: function(v)
18147 if(!this.ios_options){
18151 Roo.each(this.ios_options, function(opts){
18153 opts.el.dom.removeAttribute('selected');
18155 if(opts.data[this.valueField] != v){
18159 opts.el.dom.setAttribute('selected', true);
18165 * @cfg {Boolean} grow
18169 * @cfg {Number} growMin
18173 * @cfg {Number} growMax
18182 Roo.apply(Roo.bootstrap.ComboBox, {
18186 cls: 'modal-header',
18208 cls: 'list-group-item',
18212 cls: 'roo-combobox-list-group-item-value'
18216 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18230 listItemCheckbox : {
18232 cls: 'list-group-item',
18236 cls: 'roo-combobox-list-group-item-value'
18240 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18256 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18261 cls: 'modal-footer',
18269 cls: 'col-xs-6 text-left',
18272 cls: 'btn btn-danger roo-touch-view-cancel',
18278 cls: 'col-xs-6 text-right',
18281 cls: 'btn btn-success roo-touch-view-ok',
18292 Roo.apply(Roo.bootstrap.ComboBox, {
18294 touchViewTemplate : {
18296 cls: 'modal fade roo-combobox-touch-view',
18300 cls: 'modal-dialog',
18301 style : 'position:fixed', // we have to fix position....
18305 cls: 'modal-content',
18307 Roo.bootstrap.ComboBox.header,
18308 Roo.bootstrap.ComboBox.body,
18309 Roo.bootstrap.ComboBox.footer
18318 * Ext JS Library 1.1.1
18319 * Copyright(c) 2006-2007, Ext JS, LLC.
18321 * Originally Released Under LGPL - original licence link has changed is not relivant.
18324 * <script type="text/javascript">
18329 * @extends Roo.util.Observable
18330 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18331 * This class also supports single and multi selection modes. <br>
18332 * Create a data model bound view:
18334 var store = new Roo.data.Store(...);
18336 var view = new Roo.View({
18338 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18340 singleSelect: true,
18341 selectedClass: "ydataview-selected",
18345 // listen for node click?
18346 view.on("click", function(vw, index, node, e){
18347 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18351 dataModel.load("foobar.xml");
18353 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18355 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18356 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18358 * Note: old style constructor is still suported (container, template, config)
18361 * Create a new View
18362 * @param {Object} config The config object
18365 Roo.View = function(config, depreciated_tpl, depreciated_config){
18367 this.parent = false;
18369 if (typeof(depreciated_tpl) == 'undefined') {
18370 // new way.. - universal constructor.
18371 Roo.apply(this, config);
18372 this.el = Roo.get(this.el);
18375 this.el = Roo.get(config);
18376 this.tpl = depreciated_tpl;
18377 Roo.apply(this, depreciated_config);
18379 this.wrapEl = this.el.wrap().wrap();
18380 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18383 if(typeof(this.tpl) == "string"){
18384 this.tpl = new Roo.Template(this.tpl);
18386 // support xtype ctors..
18387 this.tpl = new Roo.factory(this.tpl, Roo);
18391 this.tpl.compile();
18396 * @event beforeclick
18397 * Fires before a click is processed. Returns false to cancel the default action.
18398 * @param {Roo.View} this
18399 * @param {Number} index The index of the target node
18400 * @param {HTMLElement} node The target node
18401 * @param {Roo.EventObject} e The raw event object
18403 "beforeclick" : true,
18406 * Fires when a template node is clicked.
18407 * @param {Roo.View} this
18408 * @param {Number} index The index of the target node
18409 * @param {HTMLElement} node The target node
18410 * @param {Roo.EventObject} e The raw event object
18415 * Fires when a template node is double clicked.
18416 * @param {Roo.View} this
18417 * @param {Number} index The index of the target node
18418 * @param {HTMLElement} node The target node
18419 * @param {Roo.EventObject} e The raw event object
18423 * @event contextmenu
18424 * Fires when a template node is right clicked.
18425 * @param {Roo.View} this
18426 * @param {Number} index The index of the target node
18427 * @param {HTMLElement} node The target node
18428 * @param {Roo.EventObject} e The raw event object
18430 "contextmenu" : true,
18432 * @event selectionchange
18433 * Fires when the selected nodes change.
18434 * @param {Roo.View} this
18435 * @param {Array} selections Array of the selected nodes
18437 "selectionchange" : true,
18440 * @event beforeselect
18441 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18442 * @param {Roo.View} this
18443 * @param {HTMLElement} node The node to be selected
18444 * @param {Array} selections Array of currently selected nodes
18446 "beforeselect" : true,
18448 * @event preparedata
18449 * Fires on every row to render, to allow you to change the data.
18450 * @param {Roo.View} this
18451 * @param {Object} data to be rendered (change this)
18453 "preparedata" : true
18461 "click": this.onClick,
18462 "dblclick": this.onDblClick,
18463 "contextmenu": this.onContextMenu,
18467 this.selections = [];
18469 this.cmp = new Roo.CompositeElementLite([]);
18471 this.store = Roo.factory(this.store, Roo.data);
18472 this.setStore(this.store, true);
18475 if ( this.footer && this.footer.xtype) {
18477 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18479 this.footer.dataSource = this.store;
18480 this.footer.container = fctr;
18481 this.footer = Roo.factory(this.footer, Roo);
18482 fctr.insertFirst(this.el);
18484 // this is a bit insane - as the paging toolbar seems to detach the el..
18485 // dom.parentNode.parentNode.parentNode
18486 // they get detached?
18490 Roo.View.superclass.constructor.call(this);
18495 Roo.extend(Roo.View, Roo.util.Observable, {
18498 * @cfg {Roo.data.Store} store Data store to load data from.
18503 * @cfg {String|Roo.Element} el The container element.
18508 * @cfg {String|Roo.Template} tpl The template used by this View
18512 * @cfg {String} dataName the named area of the template to use as the data area
18513 * Works with domtemplates roo-name="name"
18517 * @cfg {String} selectedClass The css class to add to selected nodes
18519 selectedClass : "x-view-selected",
18521 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18526 * @cfg {String} text to display on mask (default Loading)
18530 * @cfg {Boolean} multiSelect Allow multiple selection
18532 multiSelect : false,
18534 * @cfg {Boolean} singleSelect Allow single selection
18536 singleSelect: false,
18539 * @cfg {Boolean} toggleSelect - selecting
18541 toggleSelect : false,
18544 * @cfg {Boolean} tickable - selecting
18549 * Returns the element this view is bound to.
18550 * @return {Roo.Element}
18552 getEl : function(){
18553 return this.wrapEl;
18559 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18561 refresh : function(){
18562 //Roo.log('refresh');
18565 // if we are using something like 'domtemplate', then
18566 // the what gets used is:
18567 // t.applySubtemplate(NAME, data, wrapping data..)
18568 // the outer template then get' applied with
18569 // the store 'extra data'
18570 // and the body get's added to the
18571 // roo-name="data" node?
18572 // <span class='roo-tpl-{name}'></span> ?????
18576 this.clearSelections();
18577 this.el.update("");
18579 var records = this.store.getRange();
18580 if(records.length < 1) {
18582 // is this valid?? = should it render a template??
18584 this.el.update(this.emptyText);
18588 if (this.dataName) {
18589 this.el.update(t.apply(this.store.meta)); //????
18590 el = this.el.child('.roo-tpl-' + this.dataName);
18593 for(var i = 0, len = records.length; i < len; i++){
18594 var data = this.prepareData(records[i].data, i, records[i]);
18595 this.fireEvent("preparedata", this, data, i, records[i]);
18597 var d = Roo.apply({}, data);
18600 Roo.apply(d, {'roo-id' : Roo.id()});
18604 Roo.each(this.parent.item, function(item){
18605 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18608 Roo.apply(d, {'roo-data-checked' : 'checked'});
18612 html[html.length] = Roo.util.Format.trim(
18614 t.applySubtemplate(this.dataName, d, this.store.meta) :
18621 el.update(html.join(""));
18622 this.nodes = el.dom.childNodes;
18623 this.updateIndexes(0);
18628 * Function to override to reformat the data that is sent to
18629 * the template for each node.
18630 * DEPRICATED - use the preparedata event handler.
18631 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18632 * a JSON object for an UpdateManager bound view).
18634 prepareData : function(data, index, record)
18636 this.fireEvent("preparedata", this, data, index, record);
18640 onUpdate : function(ds, record){
18641 // Roo.log('on update');
18642 this.clearSelections();
18643 var index = this.store.indexOf(record);
18644 var n = this.nodes[index];
18645 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18646 n.parentNode.removeChild(n);
18647 this.updateIndexes(index, index);
18653 onAdd : function(ds, records, index)
18655 //Roo.log(['on Add', ds, records, index] );
18656 this.clearSelections();
18657 if(this.nodes.length == 0){
18661 var n = this.nodes[index];
18662 for(var i = 0, len = records.length; i < len; i++){
18663 var d = this.prepareData(records[i].data, i, records[i]);
18665 this.tpl.insertBefore(n, d);
18668 this.tpl.append(this.el, d);
18671 this.updateIndexes(index);
18674 onRemove : function(ds, record, index){
18675 // Roo.log('onRemove');
18676 this.clearSelections();
18677 var el = this.dataName ?
18678 this.el.child('.roo-tpl-' + this.dataName) :
18681 el.dom.removeChild(this.nodes[index]);
18682 this.updateIndexes(index);
18686 * Refresh an individual node.
18687 * @param {Number} index
18689 refreshNode : function(index){
18690 this.onUpdate(this.store, this.store.getAt(index));
18693 updateIndexes : function(startIndex, endIndex){
18694 var ns = this.nodes;
18695 startIndex = startIndex || 0;
18696 endIndex = endIndex || ns.length - 1;
18697 for(var i = startIndex; i <= endIndex; i++){
18698 ns[i].nodeIndex = i;
18703 * Changes the data store this view uses and refresh the view.
18704 * @param {Store} store
18706 setStore : function(store, initial){
18707 if(!initial && this.store){
18708 this.store.un("datachanged", this.refresh);
18709 this.store.un("add", this.onAdd);
18710 this.store.un("remove", this.onRemove);
18711 this.store.un("update", this.onUpdate);
18712 this.store.un("clear", this.refresh);
18713 this.store.un("beforeload", this.onBeforeLoad);
18714 this.store.un("load", this.onLoad);
18715 this.store.un("loadexception", this.onLoad);
18719 store.on("datachanged", this.refresh, this);
18720 store.on("add", this.onAdd, this);
18721 store.on("remove", this.onRemove, this);
18722 store.on("update", this.onUpdate, this);
18723 store.on("clear", this.refresh, this);
18724 store.on("beforeload", this.onBeforeLoad, this);
18725 store.on("load", this.onLoad, this);
18726 store.on("loadexception", this.onLoad, this);
18734 * onbeforeLoad - masks the loading area.
18737 onBeforeLoad : function(store,opts)
18739 //Roo.log('onBeforeLoad');
18741 this.el.update("");
18743 this.el.mask(this.mask ? this.mask : "Loading" );
18745 onLoad : function ()
18752 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18753 * @param {HTMLElement} node
18754 * @return {HTMLElement} The template node
18756 findItemFromChild : function(node){
18757 var el = this.dataName ?
18758 this.el.child('.roo-tpl-' + this.dataName,true) :
18761 if(!node || node.parentNode == el){
18764 var p = node.parentNode;
18765 while(p && p != el){
18766 if(p.parentNode == el){
18775 onClick : function(e){
18776 var item = this.findItemFromChild(e.getTarget());
18778 var index = this.indexOf(item);
18779 if(this.onItemClick(item, index, e) !== false){
18780 this.fireEvent("click", this, index, item, e);
18783 this.clearSelections();
18788 onContextMenu : function(e){
18789 var item = this.findItemFromChild(e.getTarget());
18791 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18796 onDblClick : function(e){
18797 var item = this.findItemFromChild(e.getTarget());
18799 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18803 onItemClick : function(item, index, e)
18805 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18808 if (this.toggleSelect) {
18809 var m = this.isSelected(item) ? 'unselect' : 'select';
18812 _t[m](item, true, false);
18815 if(this.multiSelect || this.singleSelect){
18816 if(this.multiSelect && e.shiftKey && this.lastSelection){
18817 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18819 this.select(item, this.multiSelect && e.ctrlKey);
18820 this.lastSelection = item;
18823 if(!this.tickable){
18824 e.preventDefault();
18832 * Get the number of selected nodes.
18835 getSelectionCount : function(){
18836 return this.selections.length;
18840 * Get the currently selected nodes.
18841 * @return {Array} An array of HTMLElements
18843 getSelectedNodes : function(){
18844 return this.selections;
18848 * Get the indexes of the selected nodes.
18851 getSelectedIndexes : function(){
18852 var indexes = [], s = this.selections;
18853 for(var i = 0, len = s.length; i < len; i++){
18854 indexes.push(s[i].nodeIndex);
18860 * Clear all selections
18861 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18863 clearSelections : function(suppressEvent){
18864 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18865 this.cmp.elements = this.selections;
18866 this.cmp.removeClass(this.selectedClass);
18867 this.selections = [];
18868 if(!suppressEvent){
18869 this.fireEvent("selectionchange", this, this.selections);
18875 * Returns true if the passed node is selected
18876 * @param {HTMLElement/Number} node The node or node index
18877 * @return {Boolean}
18879 isSelected : function(node){
18880 var s = this.selections;
18884 node = this.getNode(node);
18885 return s.indexOf(node) !== -1;
18890 * @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
18891 * @param {Boolean} keepExisting (optional) true to keep existing selections
18892 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18894 select : function(nodeInfo, keepExisting, suppressEvent){
18895 if(nodeInfo instanceof Array){
18897 this.clearSelections(true);
18899 for(var i = 0, len = nodeInfo.length; i < len; i++){
18900 this.select(nodeInfo[i], true, true);
18904 var node = this.getNode(nodeInfo);
18905 if(!node || this.isSelected(node)){
18906 return; // already selected.
18909 this.clearSelections(true);
18912 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18913 Roo.fly(node).addClass(this.selectedClass);
18914 this.selections.push(node);
18915 if(!suppressEvent){
18916 this.fireEvent("selectionchange", this, this.selections);
18924 * @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
18925 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18926 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18928 unselect : function(nodeInfo, keepExisting, suppressEvent)
18930 if(nodeInfo instanceof Array){
18931 Roo.each(this.selections, function(s) {
18932 this.unselect(s, nodeInfo);
18936 var node = this.getNode(nodeInfo);
18937 if(!node || !this.isSelected(node)){
18938 //Roo.log("not selected");
18939 return; // not selected.
18943 Roo.each(this.selections, function(s) {
18945 Roo.fly(node).removeClass(this.selectedClass);
18952 this.selections= ns;
18953 this.fireEvent("selectionchange", this, this.selections);
18957 * Gets a template node.
18958 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18959 * @return {HTMLElement} The node or null if it wasn't found
18961 getNode : function(nodeInfo){
18962 if(typeof nodeInfo == "string"){
18963 return document.getElementById(nodeInfo);
18964 }else if(typeof nodeInfo == "number"){
18965 return this.nodes[nodeInfo];
18971 * Gets a range template nodes.
18972 * @param {Number} startIndex
18973 * @param {Number} endIndex
18974 * @return {Array} An array of nodes
18976 getNodes : function(start, end){
18977 var ns = this.nodes;
18978 start = start || 0;
18979 end = typeof end == "undefined" ? ns.length - 1 : end;
18982 for(var i = start; i <= end; i++){
18986 for(var i = start; i >= end; i--){
18994 * Finds the index of the passed node
18995 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18996 * @return {Number} The index of the node or -1
18998 indexOf : function(node){
18999 node = this.getNode(node);
19000 if(typeof node.nodeIndex == "number"){
19001 return node.nodeIndex;
19003 var ns = this.nodes;
19004 for(var i = 0, len = ns.length; i < len; i++){
19015 * based on jquery fullcalendar
19019 Roo.bootstrap = Roo.bootstrap || {};
19021 * @class Roo.bootstrap.Calendar
19022 * @extends Roo.bootstrap.Component
19023 * Bootstrap Calendar class
19024 * @cfg {Boolean} loadMask (true|false) default false
19025 * @cfg {Object} header generate the user specific header of the calendar, default false
19028 * Create a new Container
19029 * @param {Object} config The config object
19034 Roo.bootstrap.Calendar = function(config){
19035 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19039 * Fires when a date is selected
19040 * @param {DatePicker} this
19041 * @param {Date} date The selected date
19045 * @event monthchange
19046 * Fires when the displayed month changes
19047 * @param {DatePicker} this
19048 * @param {Date} date The selected month
19050 'monthchange': true,
19052 * @event evententer
19053 * Fires when mouse over an event
19054 * @param {Calendar} this
19055 * @param {event} Event
19057 'evententer': true,
19059 * @event eventleave
19060 * Fires when the mouse leaves an
19061 * @param {Calendar} this
19064 'eventleave': true,
19066 * @event eventclick
19067 * Fires when the mouse click an
19068 * @param {Calendar} this
19077 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19080 * @cfg {Number} startDay
19081 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19089 getAutoCreate : function(){
19092 var fc_button = function(name, corner, style, content ) {
19093 return Roo.apply({},{
19095 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19097 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19100 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19111 style : 'width:100%',
19118 cls : 'fc-header-left',
19120 fc_button('prev', 'left', 'arrow', '‹' ),
19121 fc_button('next', 'right', 'arrow', '›' ),
19122 { tag: 'span', cls: 'fc-header-space' },
19123 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19131 cls : 'fc-header-center',
19135 cls: 'fc-header-title',
19138 html : 'month / year'
19146 cls : 'fc-header-right',
19148 /* fc_button('month', 'left', '', 'month' ),
19149 fc_button('week', '', '', 'week' ),
19150 fc_button('day', 'right', '', 'day' )
19162 header = this.header;
19165 var cal_heads = function() {
19167 // fixme - handle this.
19169 for (var i =0; i < Date.dayNames.length; i++) {
19170 var d = Date.dayNames[i];
19173 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19174 html : d.substring(0,3)
19178 ret[0].cls += ' fc-first';
19179 ret[6].cls += ' fc-last';
19182 var cal_cell = function(n) {
19185 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19190 cls: 'fc-day-number',
19194 cls: 'fc-day-content',
19198 style: 'position: relative;' // height: 17px;
19210 var cal_rows = function() {
19213 for (var r = 0; r < 6; r++) {
19220 for (var i =0; i < Date.dayNames.length; i++) {
19221 var d = Date.dayNames[i];
19222 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19225 row.cn[0].cls+=' fc-first';
19226 row.cn[0].cn[0].style = 'min-height:90px';
19227 row.cn[6].cls+=' fc-last';
19231 ret[0].cls += ' fc-first';
19232 ret[4].cls += ' fc-prev-last';
19233 ret[5].cls += ' fc-last';
19240 cls: 'fc-border-separate',
19241 style : 'width:100%',
19249 cls : 'fc-first fc-last',
19267 cls : 'fc-content',
19268 style : "position: relative;",
19271 cls : 'fc-view fc-view-month fc-grid',
19272 style : 'position: relative',
19273 unselectable : 'on',
19276 cls : 'fc-event-container',
19277 style : 'position:absolute;z-index:8;top:0;left:0;'
19295 initEvents : function()
19298 throw "can not find store for calendar";
19304 style: "text-align:center",
19308 style: "background-color:white;width:50%;margin:250 auto",
19312 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19323 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19325 var size = this.el.select('.fc-content', true).first().getSize();
19326 this.maskEl.setSize(size.width, size.height);
19327 this.maskEl.enableDisplayMode("block");
19328 if(!this.loadMask){
19329 this.maskEl.hide();
19332 this.store = Roo.factory(this.store, Roo.data);
19333 this.store.on('load', this.onLoad, this);
19334 this.store.on('beforeload', this.onBeforeLoad, this);
19338 this.cells = this.el.select('.fc-day',true);
19339 //Roo.log(this.cells);
19340 this.textNodes = this.el.query('.fc-day-number');
19341 this.cells.addClassOnOver('fc-state-hover');
19343 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19344 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19345 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19346 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19348 this.on('monthchange', this.onMonthChange, this);
19350 this.update(new Date().clearTime());
19353 resize : function() {
19354 var sz = this.el.getSize();
19356 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19357 this.el.select('.fc-day-content div',true).setHeight(34);
19362 showPrevMonth : function(e){
19363 this.update(this.activeDate.add("mo", -1));
19365 showToday : function(e){
19366 this.update(new Date().clearTime());
19369 showNextMonth : function(e){
19370 this.update(this.activeDate.add("mo", 1));
19374 showPrevYear : function(){
19375 this.update(this.activeDate.add("y", -1));
19379 showNextYear : function(){
19380 this.update(this.activeDate.add("y", 1));
19385 update : function(date)
19387 var vd = this.activeDate;
19388 this.activeDate = date;
19389 // if(vd && this.el){
19390 // var t = date.getTime();
19391 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19392 // Roo.log('using add remove');
19394 // this.fireEvent('monthchange', this, date);
19396 // this.cells.removeClass("fc-state-highlight");
19397 // this.cells.each(function(c){
19398 // if(c.dateValue == t){
19399 // c.addClass("fc-state-highlight");
19400 // setTimeout(function(){
19401 // try{c.dom.firstChild.focus();}catch(e){}
19411 var days = date.getDaysInMonth();
19413 var firstOfMonth = date.getFirstDateOfMonth();
19414 var startingPos = firstOfMonth.getDay()-this.startDay;
19416 if(startingPos < this.startDay){
19420 var pm = date.add(Date.MONTH, -1);
19421 var prevStart = pm.getDaysInMonth()-startingPos;
19423 this.cells = this.el.select('.fc-day',true);
19424 this.textNodes = this.el.query('.fc-day-number');
19425 this.cells.addClassOnOver('fc-state-hover');
19427 var cells = this.cells.elements;
19428 var textEls = this.textNodes;
19430 Roo.each(cells, function(cell){
19431 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19434 days += startingPos;
19436 // convert everything to numbers so it's fast
19437 var day = 86400000;
19438 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19441 //Roo.log(prevStart);
19443 var today = new Date().clearTime().getTime();
19444 var sel = date.clearTime().getTime();
19445 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19446 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19447 var ddMatch = this.disabledDatesRE;
19448 var ddText = this.disabledDatesText;
19449 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19450 var ddaysText = this.disabledDaysText;
19451 var format = this.format;
19453 var setCellClass = function(cal, cell){
19457 //Roo.log('set Cell Class');
19459 var t = d.getTime();
19463 cell.dateValue = t;
19465 cell.className += " fc-today";
19466 cell.className += " fc-state-highlight";
19467 cell.title = cal.todayText;
19470 // disable highlight in other month..
19471 //cell.className += " fc-state-highlight";
19476 cell.className = " fc-state-disabled";
19477 cell.title = cal.minText;
19481 cell.className = " fc-state-disabled";
19482 cell.title = cal.maxText;
19486 if(ddays.indexOf(d.getDay()) != -1){
19487 cell.title = ddaysText;
19488 cell.className = " fc-state-disabled";
19491 if(ddMatch && format){
19492 var fvalue = d.dateFormat(format);
19493 if(ddMatch.test(fvalue)){
19494 cell.title = ddText.replace("%0", fvalue);
19495 cell.className = " fc-state-disabled";
19499 if (!cell.initialClassName) {
19500 cell.initialClassName = cell.dom.className;
19503 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19508 for(; i < startingPos; i++) {
19509 textEls[i].innerHTML = (++prevStart);
19510 d.setDate(d.getDate()+1);
19512 cells[i].className = "fc-past fc-other-month";
19513 setCellClass(this, cells[i]);
19518 for(; i < days; i++){
19519 intDay = i - startingPos + 1;
19520 textEls[i].innerHTML = (intDay);
19521 d.setDate(d.getDate()+1);
19523 cells[i].className = ''; // "x-date-active";
19524 setCellClass(this, cells[i]);
19528 for(; i < 42; i++) {
19529 textEls[i].innerHTML = (++extraDays);
19530 d.setDate(d.getDate()+1);
19532 cells[i].className = "fc-future fc-other-month";
19533 setCellClass(this, cells[i]);
19536 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19538 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19540 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19541 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19543 if(totalRows != 6){
19544 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19545 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19548 this.fireEvent('monthchange', this, date);
19552 if(!this.internalRender){
19553 var main = this.el.dom.firstChild;
19554 var w = main.offsetWidth;
19555 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19556 Roo.fly(main).setWidth(w);
19557 this.internalRender = true;
19558 // opera does not respect the auto grow header center column
19559 // then, after it gets a width opera refuses to recalculate
19560 // without a second pass
19561 if(Roo.isOpera && !this.secondPass){
19562 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19563 this.secondPass = true;
19564 this.update.defer(10, this, [date]);
19571 findCell : function(dt) {
19572 dt = dt.clearTime().getTime();
19574 this.cells.each(function(c){
19575 //Roo.log("check " +c.dateValue + '?=' + dt);
19576 if(c.dateValue == dt){
19586 findCells : function(ev) {
19587 var s = ev.start.clone().clearTime().getTime();
19589 var e= ev.end.clone().clearTime().getTime();
19592 this.cells.each(function(c){
19593 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19595 if(c.dateValue > e){
19598 if(c.dateValue < s){
19607 // findBestRow: function(cells)
19611 // for (var i =0 ; i < cells.length;i++) {
19612 // ret = Math.max(cells[i].rows || 0,ret);
19619 addItem : function(ev)
19621 // look for vertical location slot in
19622 var cells = this.findCells(ev);
19624 // ev.row = this.findBestRow(cells);
19626 // work out the location.
19630 for(var i =0; i < cells.length; i++) {
19632 cells[i].row = cells[0].row;
19635 cells[i].row = cells[i].row + 1;
19645 if (crow.start.getY() == cells[i].getY()) {
19647 crow.end = cells[i];
19664 cells[0].events.push(ev);
19666 this.calevents.push(ev);
19669 clearEvents: function() {
19671 if(!this.calevents){
19675 Roo.each(this.cells.elements, function(c){
19681 Roo.each(this.calevents, function(e) {
19682 Roo.each(e.els, function(el) {
19683 el.un('mouseenter' ,this.onEventEnter, this);
19684 el.un('mouseleave' ,this.onEventLeave, this);
19689 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19695 renderEvents: function()
19699 this.cells.each(function(c) {
19708 if(c.row != c.events.length){
19709 r = 4 - (4 - (c.row - c.events.length));
19712 c.events = ev.slice(0, r);
19713 c.more = ev.slice(r);
19715 if(c.more.length && c.more.length == 1){
19716 c.events.push(c.more.pop());
19719 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19723 this.cells.each(function(c) {
19725 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19728 for (var e = 0; e < c.events.length; e++){
19729 var ev = c.events[e];
19730 var rows = ev.rows;
19732 for(var i = 0; i < rows.length; i++) {
19734 // how many rows should it span..
19737 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19738 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19740 unselectable : "on",
19743 cls: 'fc-event-inner',
19747 // cls: 'fc-event-time',
19748 // html : cells.length > 1 ? '' : ev.time
19752 cls: 'fc-event-title',
19753 html : String.format('{0}', ev.title)
19760 cls: 'ui-resizable-handle ui-resizable-e',
19761 html : '  '
19768 cfg.cls += ' fc-event-start';
19770 if ((i+1) == rows.length) {
19771 cfg.cls += ' fc-event-end';
19774 var ctr = _this.el.select('.fc-event-container',true).first();
19775 var cg = ctr.createChild(cfg);
19777 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19778 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19780 var r = (c.more.length) ? 1 : 0;
19781 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19782 cg.setWidth(ebox.right - sbox.x -2);
19784 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19785 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19786 cg.on('click', _this.onEventClick, _this, ev);
19797 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19798 style : 'position: absolute',
19799 unselectable : "on",
19802 cls: 'fc-event-inner',
19806 cls: 'fc-event-title',
19814 cls: 'ui-resizable-handle ui-resizable-e',
19815 html : '  '
19821 var ctr = _this.el.select('.fc-event-container',true).first();
19822 var cg = ctr.createChild(cfg);
19824 var sbox = c.select('.fc-day-content',true).first().getBox();
19825 var ebox = c.select('.fc-day-content',true).first().getBox();
19827 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19828 cg.setWidth(ebox.right - sbox.x -2);
19830 cg.on('click', _this.onMoreEventClick, _this, c.more);
19840 onEventEnter: function (e, el,event,d) {
19841 this.fireEvent('evententer', this, el, event);
19844 onEventLeave: function (e, el,event,d) {
19845 this.fireEvent('eventleave', this, el, event);
19848 onEventClick: function (e, el,event,d) {
19849 this.fireEvent('eventclick', this, el, event);
19852 onMonthChange: function () {
19856 onMoreEventClick: function(e, el, more)
19860 this.calpopover.placement = 'right';
19861 this.calpopover.setTitle('More');
19863 this.calpopover.setContent('');
19865 var ctr = this.calpopover.el.select('.popover-content', true).first();
19867 Roo.each(more, function(m){
19869 cls : 'fc-event-hori fc-event-draggable',
19872 var cg = ctr.createChild(cfg);
19874 cg.on('click', _this.onEventClick, _this, m);
19877 this.calpopover.show(el);
19882 onLoad: function ()
19884 this.calevents = [];
19887 if(this.store.getCount() > 0){
19888 this.store.data.each(function(d){
19891 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19892 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19893 time : d.data.start_time,
19894 title : d.data.title,
19895 description : d.data.description,
19896 venue : d.data.venue
19901 this.renderEvents();
19903 if(this.calevents.length && this.loadMask){
19904 this.maskEl.hide();
19908 onBeforeLoad: function()
19910 this.clearEvents();
19912 this.maskEl.show();
19926 * @class Roo.bootstrap.Popover
19927 * @extends Roo.bootstrap.Component
19928 * Bootstrap Popover class
19929 * @cfg {String} html contents of the popover (or false to use children..)
19930 * @cfg {String} title of popover (or false to hide)
19931 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19932 * @cfg {String} trigger click || hover (or false to trigger manually)
19933 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19934 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19935 * - if false and it has a 'parent' then it will be automatically added to that element
19936 * - if string - Roo.get will be called
19937 * @cfg {Number} delay - delay before showing
19940 * Create a new Popover
19941 * @param {Object} config The config object
19944 Roo.bootstrap.Popover = function(config){
19945 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19951 * After the popover show
19953 * @param {Roo.bootstrap.Popover} this
19958 * After the popover hide
19960 * @param {Roo.bootstrap.Popover} this
19966 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19971 placement : 'right',
19972 trigger : 'hover', // hover
19978 can_build_overlaid : false,
19980 maskEl : false, // the mask element
19983 alignEl : false, // when show is called with an element - this get's stored.
19985 getChildContainer : function()
19987 return this.contentEl;
19990 getPopoverHeader : function()
19992 this.title = true; // flag not to hide it..
19993 this.headerEl.addClass('p-0');
19994 return this.headerEl
19998 getAutoCreate : function(){
20001 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20002 style: 'display:block',
20008 cls : 'popover-inner ',
20012 cls: 'popover-title popover-header',
20013 html : this.title === false ? '' : this.title
20016 cls : 'popover-content popover-body ' + (this.cls || ''),
20017 html : this.html || ''
20028 * @param {string} the title
20030 setTitle: function(str)
20034 this.headerEl.dom.innerHTML = str;
20039 * @param {string} the body content
20041 setContent: function(str)
20044 if (this.contentEl) {
20045 this.contentEl.dom.innerHTML = str;
20049 // as it get's added to the bottom of the page.
20050 onRender : function(ct, position)
20052 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20057 var cfg = Roo.apply({}, this.getAutoCreate());
20061 cfg.cls += ' ' + this.cls;
20064 cfg.style = this.style;
20066 //Roo.log("adding to ");
20067 this.el = Roo.get(document.body).createChild(cfg, position);
20068 // Roo.log(this.el);
20071 this.contentEl = this.el.select('.popover-content',true).first();
20072 this.headerEl = this.el.select('.popover-title',true).first();
20075 if(typeof(this.items) != 'undefined'){
20076 var items = this.items;
20079 for(var i =0;i < items.length;i++) {
20080 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20084 this.items = nitems;
20086 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20087 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20094 resizeMask : function()
20096 this.maskEl.setSize(
20097 Roo.lib.Dom.getViewWidth(true),
20098 Roo.lib.Dom.getViewHeight(true)
20102 initEvents : function()
20106 Roo.bootstrap.Popover.register(this);
20109 this.arrowEl = this.el.select('.arrow',true).first();
20110 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20111 this.el.enableDisplayMode('block');
20115 if (this.over === false && !this.parent()) {
20118 if (this.triggers === false) {
20123 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20124 var triggers = this.trigger ? this.trigger.split(' ') : [];
20125 Roo.each(triggers, function(trigger) {
20127 if (trigger == 'click') {
20128 on_el.on('click', this.toggle, this);
20129 } else if (trigger != 'manual') {
20130 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20131 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20133 on_el.on(eventIn ,this.enter, this);
20134 on_el.on(eventOut, this.leave, this);
20144 toggle : function () {
20145 this.hoverState == 'in' ? this.leave() : this.enter();
20148 enter : function () {
20150 clearTimeout(this.timeout);
20152 this.hoverState = 'in';
20154 if (!this.delay || !this.delay.show) {
20159 this.timeout = setTimeout(function () {
20160 if (_t.hoverState == 'in') {
20163 }, this.delay.show)
20166 leave : function() {
20167 clearTimeout(this.timeout);
20169 this.hoverState = 'out';
20171 if (!this.delay || !this.delay.hide) {
20176 this.timeout = setTimeout(function () {
20177 if (_t.hoverState == 'out') {
20180 }, this.delay.hide)
20184 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20185 * @param {string} (left|right|top|bottom) position
20187 show : function (on_el, placement)
20189 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20190 on_el = on_el || false; // default to false
20193 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20194 on_el = this.parent().el;
20195 } else if (this.over) {
20196 Roo.get(this.over);
20201 this.alignEl = Roo.get( on_el );
20204 this.render(document.body);
20210 if (this.title === false) {
20211 this.headerEl.hide();
20216 this.el.dom.style.display = 'block';
20219 if (this.alignEl) {
20220 this.updatePosition(this.placement, true);
20223 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20224 var es = this.el.getSize();
20225 var x = Roo.lib.Dom.getViewWidth()/2;
20226 var y = Roo.lib.Dom.getViewHeight()/2;
20227 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20232 //var arrow = this.el.select('.arrow',true).first();
20233 //arrow.set(align[2],
20235 this.el.addClass('in');
20239 this.hoverState = 'in';
20242 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20243 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20244 this.maskEl.dom.style.display = 'block';
20245 this.maskEl.addClass('show');
20247 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20249 this.fireEvent('show', this);
20253 * fire this manually after loading a grid in the table for example
20254 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20255 * @param {Boolean} try and move it if we cant get right position.
20257 updatePosition : function(placement, try_move)
20259 // allow for calling with no parameters
20260 placement = placement ? placement : this.placement;
20261 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20263 this.el.removeClass([
20264 'fade','top','bottom', 'left', 'right','in',
20265 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20267 this.el.addClass(placement + ' bs-popover-' + placement);
20269 if (!this.alignEl ) {
20273 switch (placement) {
20275 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20276 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20277 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20278 //normal display... or moved up/down.
20279 this.el.setXY(offset);
20280 var xy = this.alignEl.getAnchorXY('tr', false);
20282 this.arrowEl.setXY(xy);
20285 // continue through...
20286 return this.updatePosition('left', false);
20290 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20291 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20292 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20293 //normal display... or moved up/down.
20294 this.el.setXY(offset);
20295 var xy = this.alignEl.getAnchorXY('tl', false);
20296 xy[0]-=10;xy[1]+=5; // << fix me
20297 this.arrowEl.setXY(xy);
20301 return this.updatePosition('right', false);
20304 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20305 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20306 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20307 //normal display... or moved up/down.
20308 this.el.setXY(offset);
20309 var xy = this.alignEl.getAnchorXY('t', false);
20310 xy[1]-=10; // << fix me
20311 this.arrowEl.setXY(xy);
20315 return this.updatePosition('bottom', false);
20318 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20319 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20320 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20321 //normal display... or moved up/down.
20322 this.el.setXY(offset);
20323 var xy = this.alignEl.getAnchorXY('b', false);
20324 xy[1]+=2; // << fix me
20325 this.arrowEl.setXY(xy);
20329 return this.updatePosition('top', false);
20340 this.el.setXY([0,0]);
20341 this.el.removeClass('in');
20343 this.hoverState = null;
20344 this.maskEl.hide(); // always..
20345 this.fireEvent('hide', this);
20351 Roo.apply(Roo.bootstrap.Popover, {
20354 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20355 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20356 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20357 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20362 clickHander : false,
20365 onMouseDown : function(e)
20367 if (!e.getTarget(".roo-popover")) {
20375 register : function(popup)
20377 if (!Roo.bootstrap.Popover.clickHandler) {
20378 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20380 // hide other popups.
20382 this.popups.push(popup);
20384 hideAll : function()
20386 this.popups.forEach(function(p) {
20394 * Card header - holder for the card header elements.
20399 * @class Roo.bootstrap.PopoverNav
20400 * @extends Roo.bootstrap.NavGroup
20401 * Bootstrap Popover header navigation class
20403 * Create a new Popover Header Navigation
20404 * @param {Object} config The config object
20407 Roo.bootstrap.PopoverNav = function(config){
20408 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20411 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20414 container_method : 'getPopoverHeader'
20432 * @class Roo.bootstrap.Progress
20433 * @extends Roo.bootstrap.Component
20434 * Bootstrap Progress class
20435 * @cfg {Boolean} striped striped of the progress bar
20436 * @cfg {Boolean} active animated of the progress bar
20440 * Create a new Progress
20441 * @param {Object} config The config object
20444 Roo.bootstrap.Progress = function(config){
20445 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20448 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20453 getAutoCreate : function(){
20461 cfg.cls += ' progress-striped';
20465 cfg.cls += ' active';
20484 * @class Roo.bootstrap.ProgressBar
20485 * @extends Roo.bootstrap.Component
20486 * Bootstrap ProgressBar class
20487 * @cfg {Number} aria_valuenow aria-value now
20488 * @cfg {Number} aria_valuemin aria-value min
20489 * @cfg {Number} aria_valuemax aria-value max
20490 * @cfg {String} label label for the progress bar
20491 * @cfg {String} panel (success | info | warning | danger )
20492 * @cfg {String} role role of the progress bar
20493 * @cfg {String} sr_only text
20497 * Create a new ProgressBar
20498 * @param {Object} config The config object
20501 Roo.bootstrap.ProgressBar = function(config){
20502 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20505 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20509 aria_valuemax : 100,
20515 getAutoCreate : function()
20520 cls: 'progress-bar',
20521 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20533 cfg.role = this.role;
20536 if(this.aria_valuenow){
20537 cfg['aria-valuenow'] = this.aria_valuenow;
20540 if(this.aria_valuemin){
20541 cfg['aria-valuemin'] = this.aria_valuemin;
20544 if(this.aria_valuemax){
20545 cfg['aria-valuemax'] = this.aria_valuemax;
20548 if(this.label && !this.sr_only){
20549 cfg.html = this.label;
20553 cfg.cls += ' progress-bar-' + this.panel;
20559 update : function(aria_valuenow)
20561 this.aria_valuenow = aria_valuenow;
20563 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20578 * @class Roo.bootstrap.TabGroup
20579 * @extends Roo.bootstrap.Column
20580 * Bootstrap Column class
20581 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20582 * @cfg {Boolean} carousel true to make the group behave like a carousel
20583 * @cfg {Boolean} bullets show bullets for the panels
20584 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20585 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20586 * @cfg {Boolean} showarrow (true|false) show arrow default true
20589 * Create a new TabGroup
20590 * @param {Object} config The config object
20593 Roo.bootstrap.TabGroup = function(config){
20594 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20596 this.navId = Roo.id();
20599 Roo.bootstrap.TabGroup.register(this);
20603 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20606 transition : false,
20611 slideOnTouch : false,
20614 getAutoCreate : function()
20616 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20618 cfg.cls += ' tab-content';
20620 if (this.carousel) {
20621 cfg.cls += ' carousel slide';
20624 cls : 'carousel-inner',
20628 if(this.bullets && !Roo.isTouch){
20631 cls : 'carousel-bullets',
20635 if(this.bullets_cls){
20636 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20643 cfg.cn[0].cn.push(bullets);
20646 if(this.showarrow){
20647 cfg.cn[0].cn.push({
20649 class : 'carousel-arrow',
20653 class : 'carousel-prev',
20657 class : 'fa fa-chevron-left'
20663 class : 'carousel-next',
20667 class : 'fa fa-chevron-right'
20680 initEvents: function()
20682 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20683 // this.el.on("touchstart", this.onTouchStart, this);
20686 if(this.autoslide){
20689 this.slideFn = window.setInterval(function() {
20690 _this.showPanelNext();
20694 if(this.showarrow){
20695 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20696 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20702 // onTouchStart : function(e, el, o)
20704 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20708 // this.showPanelNext();
20712 getChildContainer : function()
20714 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20718 * register a Navigation item
20719 * @param {Roo.bootstrap.NavItem} the navitem to add
20721 register : function(item)
20723 this.tabs.push( item);
20724 item.navId = this.navId; // not really needed..
20729 getActivePanel : function()
20732 Roo.each(this.tabs, function(t) {
20742 getPanelByName : function(n)
20745 Roo.each(this.tabs, function(t) {
20746 if (t.tabId == n) {
20754 indexOfPanel : function(p)
20757 Roo.each(this.tabs, function(t,i) {
20758 if (t.tabId == p.tabId) {
20767 * show a specific panel
20768 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20769 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20771 showPanel : function (pan)
20773 if(this.transition || typeof(pan) == 'undefined'){
20774 Roo.log("waiting for the transitionend");
20778 if (typeof(pan) == 'number') {
20779 pan = this.tabs[pan];
20782 if (typeof(pan) == 'string') {
20783 pan = this.getPanelByName(pan);
20786 var cur = this.getActivePanel();
20789 Roo.log('pan or acitve pan is undefined');
20793 if (pan.tabId == this.getActivePanel().tabId) {
20797 if (false === cur.fireEvent('beforedeactivate')) {
20801 if(this.bullets > 0 && !Roo.isTouch){
20802 this.setActiveBullet(this.indexOfPanel(pan));
20805 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20807 //class="carousel-item carousel-item-next carousel-item-left"
20809 this.transition = true;
20810 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20811 var lr = dir == 'next' ? 'left' : 'right';
20812 pan.el.addClass(dir); // or prev
20813 pan.el.addClass('carousel-item-' + dir); // or prev
20814 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20815 cur.el.addClass(lr); // or right
20816 pan.el.addClass(lr);
20817 cur.el.addClass('carousel-item-' +lr); // or right
20818 pan.el.addClass('carousel-item-' +lr);
20822 cur.el.on('transitionend', function() {
20823 Roo.log("trans end?");
20825 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20826 pan.setActive(true);
20828 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20829 cur.setActive(false);
20831 _this.transition = false;
20833 }, this, { single: true } );
20838 cur.setActive(false);
20839 pan.setActive(true);
20844 showPanelNext : function()
20846 var i = this.indexOfPanel(this.getActivePanel());
20848 if (i >= this.tabs.length - 1 && !this.autoslide) {
20852 if (i >= this.tabs.length - 1 && this.autoslide) {
20856 this.showPanel(this.tabs[i+1]);
20859 showPanelPrev : function()
20861 var i = this.indexOfPanel(this.getActivePanel());
20863 if (i < 1 && !this.autoslide) {
20867 if (i < 1 && this.autoslide) {
20868 i = this.tabs.length;
20871 this.showPanel(this.tabs[i-1]);
20875 addBullet: function()
20877 if(!this.bullets || Roo.isTouch){
20880 var ctr = this.el.select('.carousel-bullets',true).first();
20881 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20882 var bullet = ctr.createChild({
20883 cls : 'bullet bullet-' + i
20884 },ctr.dom.lastChild);
20889 bullet.on('click', (function(e, el, o, ii, t){
20891 e.preventDefault();
20893 this.showPanel(ii);
20895 if(this.autoslide && this.slideFn){
20896 clearInterval(this.slideFn);
20897 this.slideFn = window.setInterval(function() {
20898 _this.showPanelNext();
20902 }).createDelegate(this, [i, bullet], true));
20907 setActiveBullet : function(i)
20913 Roo.each(this.el.select('.bullet', true).elements, function(el){
20914 el.removeClass('selected');
20917 var bullet = this.el.select('.bullet-' + i, true).first();
20923 bullet.addClass('selected');
20934 Roo.apply(Roo.bootstrap.TabGroup, {
20938 * register a Navigation Group
20939 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20941 register : function(navgrp)
20943 this.groups[navgrp.navId] = navgrp;
20947 * fetch a Navigation Group based on the navigation ID
20948 * if one does not exist , it will get created.
20949 * @param {string} the navgroup to add
20950 * @returns {Roo.bootstrap.NavGroup} the navgroup
20952 get: function(navId) {
20953 if (typeof(this.groups[navId]) == 'undefined') {
20954 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20956 return this.groups[navId] ;
20971 * @class Roo.bootstrap.TabPanel
20972 * @extends Roo.bootstrap.Component
20973 * Bootstrap TabPanel class
20974 * @cfg {Boolean} active panel active
20975 * @cfg {String} html panel content
20976 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20977 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20978 * @cfg {String} href click to link..
20979 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20983 * Create a new TabPanel
20984 * @param {Object} config The config object
20987 Roo.bootstrap.TabPanel = function(config){
20988 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20992 * Fires when the active status changes
20993 * @param {Roo.bootstrap.TabPanel} this
20994 * @param {Boolean} state the new state
20999 * @event beforedeactivate
21000 * Fires before a tab is de-activated - can be used to do validation on a form.
21001 * @param {Roo.bootstrap.TabPanel} this
21002 * @return {Boolean} false if there is an error
21005 'beforedeactivate': true
21008 this.tabId = this.tabId || Roo.id();
21012 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21019 touchSlide : false,
21020 getAutoCreate : function(){
21025 // item is needed for carousel - not sure if it has any effect otherwise
21026 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21027 html: this.html || ''
21031 cfg.cls += ' active';
21035 cfg.tabId = this.tabId;
21043 initEvents: function()
21045 var p = this.parent();
21047 this.navId = this.navId || p.navId;
21049 if (typeof(this.navId) != 'undefined') {
21050 // not really needed.. but just in case.. parent should be a NavGroup.
21051 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21055 var i = tg.tabs.length - 1;
21057 if(this.active && tg.bullets > 0 && i < tg.bullets){
21058 tg.setActiveBullet(i);
21062 this.el.on('click', this.onClick, this);
21064 if(Roo.isTouch && this.touchSlide){
21065 this.el.on("touchstart", this.onTouchStart, this);
21066 this.el.on("touchmove", this.onTouchMove, this);
21067 this.el.on("touchend", this.onTouchEnd, this);
21072 onRender : function(ct, position)
21074 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21077 setActive : function(state)
21079 Roo.log("panel - set active " + this.tabId + "=" + state);
21081 this.active = state;
21083 this.el.removeClass('active');
21085 } else if (!this.el.hasClass('active')) {
21086 this.el.addClass('active');
21089 this.fireEvent('changed', this, state);
21092 onClick : function(e)
21094 e.preventDefault();
21096 if(!this.href.length){
21100 window.location.href = this.href;
21109 onTouchStart : function(e)
21111 this.swiping = false;
21113 this.startX = e.browserEvent.touches[0].clientX;
21114 this.startY = e.browserEvent.touches[0].clientY;
21117 onTouchMove : function(e)
21119 this.swiping = true;
21121 this.endX = e.browserEvent.touches[0].clientX;
21122 this.endY = e.browserEvent.touches[0].clientY;
21125 onTouchEnd : function(e)
21132 var tabGroup = this.parent();
21134 if(this.endX > this.startX){ // swiping right
21135 tabGroup.showPanelPrev();
21139 if(this.startX > this.endX){ // swiping left
21140 tabGroup.showPanelNext();
21159 * @class Roo.bootstrap.DateField
21160 * @extends Roo.bootstrap.Input
21161 * Bootstrap DateField class
21162 * @cfg {Number} weekStart default 0
21163 * @cfg {String} viewMode default empty, (months|years)
21164 * @cfg {String} minViewMode default empty, (months|years)
21165 * @cfg {Number} startDate default -Infinity
21166 * @cfg {Number} endDate default Infinity
21167 * @cfg {Boolean} todayHighlight default false
21168 * @cfg {Boolean} todayBtn default false
21169 * @cfg {Boolean} calendarWeeks default false
21170 * @cfg {Object} daysOfWeekDisabled default empty
21171 * @cfg {Boolean} singleMode default false (true | false)
21173 * @cfg {Boolean} keyboardNavigation default true
21174 * @cfg {String} language default en
21177 * Create a new DateField
21178 * @param {Object} config The config object
21181 Roo.bootstrap.DateField = function(config){
21182 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21186 * Fires when this field show.
21187 * @param {Roo.bootstrap.DateField} this
21188 * @param {Mixed} date The date value
21193 * Fires when this field hide.
21194 * @param {Roo.bootstrap.DateField} this
21195 * @param {Mixed} date The date value
21200 * Fires when select a date.
21201 * @param {Roo.bootstrap.DateField} this
21202 * @param {Mixed} date The date value
21206 * @event beforeselect
21207 * Fires when before select a date.
21208 * @param {Roo.bootstrap.DateField} this
21209 * @param {Mixed} date The date value
21211 beforeselect : true
21215 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21218 * @cfg {String} format
21219 * The default date format string which can be overriden for localization support. The format must be
21220 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21224 * @cfg {String} altFormats
21225 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21226 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21228 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21236 todayHighlight : false,
21242 keyboardNavigation: true,
21244 calendarWeeks: false,
21246 startDate: -Infinity,
21250 daysOfWeekDisabled: [],
21254 singleMode : false,
21256 UTCDate: function()
21258 return new Date(Date.UTC.apply(Date, arguments));
21261 UTCToday: function()
21263 var today = new Date();
21264 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21267 getDate: function() {
21268 var d = this.getUTCDate();
21269 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21272 getUTCDate: function() {
21276 setDate: function(d) {
21277 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21280 setUTCDate: function(d) {
21282 this.setValue(this.formatDate(this.date));
21285 onRender: function(ct, position)
21288 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21290 this.language = this.language || 'en';
21291 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21292 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21294 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21295 this.format = this.format || 'm/d/y';
21296 this.isInline = false;
21297 this.isInput = true;
21298 this.component = this.el.select('.add-on', true).first() || false;
21299 this.component = (this.component && this.component.length === 0) ? false : this.component;
21300 this.hasInput = this.component && this.inputEl().length;
21302 if (typeof(this.minViewMode === 'string')) {
21303 switch (this.minViewMode) {
21305 this.minViewMode = 1;
21308 this.minViewMode = 2;
21311 this.minViewMode = 0;
21316 if (typeof(this.viewMode === 'string')) {
21317 switch (this.viewMode) {
21330 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21332 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21334 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21336 this.picker().on('mousedown', this.onMousedown, this);
21337 this.picker().on('click', this.onClick, this);
21339 this.picker().addClass('datepicker-dropdown');
21341 this.startViewMode = this.viewMode;
21343 if(this.singleMode){
21344 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21345 v.setVisibilityMode(Roo.Element.DISPLAY);
21349 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21350 v.setStyle('width', '189px');
21354 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21355 if(!this.calendarWeeks){
21360 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21361 v.attr('colspan', function(i, val){
21362 return parseInt(val) + 1;
21367 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21369 this.setStartDate(this.startDate);
21370 this.setEndDate(this.endDate);
21372 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21379 if(this.isInline) {
21384 picker : function()
21386 return this.pickerEl;
21387 // return this.el.select('.datepicker', true).first();
21390 fillDow: function()
21392 var dowCnt = this.weekStart;
21401 if(this.calendarWeeks){
21409 while (dowCnt < this.weekStart + 7) {
21413 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21417 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21420 fillMonths: function()
21423 var months = this.picker().select('>.datepicker-months td', true).first();
21425 months.dom.innerHTML = '';
21431 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21434 months.createChild(month);
21441 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;
21443 if (this.date < this.startDate) {
21444 this.viewDate = new Date(this.startDate);
21445 } else if (this.date > this.endDate) {
21446 this.viewDate = new Date(this.endDate);
21448 this.viewDate = new Date(this.date);
21456 var d = new Date(this.viewDate),
21457 year = d.getUTCFullYear(),
21458 month = d.getUTCMonth(),
21459 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21460 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21461 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21462 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21463 currentDate = this.date && this.date.valueOf(),
21464 today = this.UTCToday();
21466 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21468 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21470 // this.picker.select('>tfoot th.today').
21471 // .text(dates[this.language].today)
21472 // .toggle(this.todayBtn !== false);
21474 this.updateNavArrows();
21477 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21479 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21481 prevMonth.setUTCDate(day);
21483 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21485 var nextMonth = new Date(prevMonth);
21487 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21489 nextMonth = nextMonth.valueOf();
21491 var fillMonths = false;
21493 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21495 while(prevMonth.valueOf() <= nextMonth) {
21498 if (prevMonth.getUTCDay() === this.weekStart) {
21500 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21508 if(this.calendarWeeks){
21509 // ISO 8601: First week contains first thursday.
21510 // ISO also states week starts on Monday, but we can be more abstract here.
21512 // Start of current week: based on weekstart/current date
21513 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21514 // Thursday of this week
21515 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21516 // First Thursday of year, year from thursday
21517 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21518 // Calendar week: ms between thursdays, div ms per day, div 7 days
21519 calWeek = (th - yth) / 864e5 / 7 + 1;
21521 fillMonths.cn.push({
21529 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21531 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21534 if (this.todayHighlight &&
21535 prevMonth.getUTCFullYear() == today.getFullYear() &&
21536 prevMonth.getUTCMonth() == today.getMonth() &&
21537 prevMonth.getUTCDate() == today.getDate()) {
21538 clsName += ' today';
21541 if (currentDate && prevMonth.valueOf() === currentDate) {
21542 clsName += ' active';
21545 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21546 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21547 clsName += ' disabled';
21550 fillMonths.cn.push({
21552 cls: 'day ' + clsName,
21553 html: prevMonth.getDate()
21556 prevMonth.setDate(prevMonth.getDate()+1);
21559 var currentYear = this.date && this.date.getUTCFullYear();
21560 var currentMonth = this.date && this.date.getUTCMonth();
21562 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21564 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21565 v.removeClass('active');
21567 if(currentYear === year && k === currentMonth){
21568 v.addClass('active');
21571 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21572 v.addClass('disabled');
21578 year = parseInt(year/10, 10) * 10;
21580 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21582 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21585 for (var i = -1; i < 11; i++) {
21586 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21588 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21596 showMode: function(dir)
21599 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21602 Roo.each(this.picker().select('>div',true).elements, function(v){
21603 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21606 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21611 if(this.isInline) {
21615 this.picker().removeClass(['bottom', 'top']);
21617 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21619 * place to the top of element!
21623 this.picker().addClass('top');
21624 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21629 this.picker().addClass('bottom');
21631 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21634 parseDate : function(value)
21636 if(!value || value instanceof Date){
21639 var v = Date.parseDate(value, this.format);
21640 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21641 v = Date.parseDate(value, 'Y-m-d');
21643 if(!v && this.altFormats){
21644 if(!this.altFormatsArray){
21645 this.altFormatsArray = this.altFormats.split("|");
21647 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21648 v = Date.parseDate(value, this.altFormatsArray[i]);
21654 formatDate : function(date, fmt)
21656 return (!date || !(date instanceof Date)) ?
21657 date : date.dateFormat(fmt || this.format);
21660 onFocus : function()
21662 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21666 onBlur : function()
21668 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21670 var d = this.inputEl().getValue();
21677 showPopup : function()
21679 this.picker().show();
21683 this.fireEvent('showpopup', this, this.date);
21686 hidePopup : function()
21688 if(this.isInline) {
21691 this.picker().hide();
21692 this.viewMode = this.startViewMode;
21695 this.fireEvent('hidepopup', this, this.date);
21699 onMousedown: function(e)
21701 e.stopPropagation();
21702 e.preventDefault();
21707 Roo.bootstrap.DateField.superclass.keyup.call(this);
21711 setValue: function(v)
21713 if(this.fireEvent('beforeselect', this, v) !== false){
21714 var d = new Date(this.parseDate(v) ).clearTime();
21716 if(isNaN(d.getTime())){
21717 this.date = this.viewDate = '';
21718 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21722 v = this.formatDate(d);
21724 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21726 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21730 this.fireEvent('select', this, this.date);
21734 getValue: function()
21736 return this.formatDate(this.date);
21739 fireKey: function(e)
21741 if (!this.picker().isVisible()){
21742 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21748 var dateChanged = false,
21750 newDate, newViewDate;
21755 e.preventDefault();
21759 if (!this.keyboardNavigation) {
21762 dir = e.keyCode == 37 ? -1 : 1;
21765 newDate = this.moveYear(this.date, dir);
21766 newViewDate = this.moveYear(this.viewDate, dir);
21767 } else if (e.shiftKey){
21768 newDate = this.moveMonth(this.date, dir);
21769 newViewDate = this.moveMonth(this.viewDate, dir);
21771 newDate = new Date(this.date);
21772 newDate.setUTCDate(this.date.getUTCDate() + dir);
21773 newViewDate = new Date(this.viewDate);
21774 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21776 if (this.dateWithinRange(newDate)){
21777 this.date = newDate;
21778 this.viewDate = newViewDate;
21779 this.setValue(this.formatDate(this.date));
21781 e.preventDefault();
21782 dateChanged = true;
21787 if (!this.keyboardNavigation) {
21790 dir = e.keyCode == 38 ? -1 : 1;
21792 newDate = this.moveYear(this.date, dir);
21793 newViewDate = this.moveYear(this.viewDate, dir);
21794 } else if (e.shiftKey){
21795 newDate = this.moveMonth(this.date, dir);
21796 newViewDate = this.moveMonth(this.viewDate, dir);
21798 newDate = new Date(this.date);
21799 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21800 newViewDate = new Date(this.viewDate);
21801 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21803 if (this.dateWithinRange(newDate)){
21804 this.date = newDate;
21805 this.viewDate = newViewDate;
21806 this.setValue(this.formatDate(this.date));
21808 e.preventDefault();
21809 dateChanged = true;
21813 this.setValue(this.formatDate(this.date));
21815 e.preventDefault();
21818 this.setValue(this.formatDate(this.date));
21832 onClick: function(e)
21834 e.stopPropagation();
21835 e.preventDefault();
21837 var target = e.getTarget();
21839 if(target.nodeName.toLowerCase() === 'i'){
21840 target = Roo.get(target).dom.parentNode;
21843 var nodeName = target.nodeName;
21844 var className = target.className;
21845 var html = target.innerHTML;
21846 //Roo.log(nodeName);
21848 switch(nodeName.toLowerCase()) {
21850 switch(className) {
21856 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21857 switch(this.viewMode){
21859 this.viewDate = this.moveMonth(this.viewDate, dir);
21863 this.viewDate = this.moveYear(this.viewDate, dir);
21869 var date = new Date();
21870 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21872 this.setValue(this.formatDate(this.date));
21879 if (className.indexOf('disabled') < 0) {
21880 this.viewDate.setUTCDate(1);
21881 if (className.indexOf('month') > -1) {
21882 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21884 var year = parseInt(html, 10) || 0;
21885 this.viewDate.setUTCFullYear(year);
21889 if(this.singleMode){
21890 this.setValue(this.formatDate(this.viewDate));
21901 //Roo.log(className);
21902 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21903 var day = parseInt(html, 10) || 1;
21904 var year = (this.viewDate || new Date()).getUTCFullYear(),
21905 month = (this.viewDate || new Date()).getUTCMonth();
21907 if (className.indexOf('old') > -1) {
21914 } else if (className.indexOf('new') > -1) {
21922 //Roo.log([year,month,day]);
21923 this.date = this.UTCDate(year, month, day,0,0,0,0);
21924 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21926 //Roo.log(this.formatDate(this.date));
21927 this.setValue(this.formatDate(this.date));
21934 setStartDate: function(startDate)
21936 this.startDate = startDate || -Infinity;
21937 if (this.startDate !== -Infinity) {
21938 this.startDate = this.parseDate(this.startDate);
21941 this.updateNavArrows();
21944 setEndDate: function(endDate)
21946 this.endDate = endDate || Infinity;
21947 if (this.endDate !== Infinity) {
21948 this.endDate = this.parseDate(this.endDate);
21951 this.updateNavArrows();
21954 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21956 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21957 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21958 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21960 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21961 return parseInt(d, 10);
21964 this.updateNavArrows();
21967 updateNavArrows: function()
21969 if(this.singleMode){
21973 var d = new Date(this.viewDate),
21974 year = d.getUTCFullYear(),
21975 month = d.getUTCMonth();
21977 Roo.each(this.picker().select('.prev', true).elements, function(v){
21979 switch (this.viewMode) {
21982 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21988 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21995 Roo.each(this.picker().select('.next', true).elements, function(v){
21997 switch (this.viewMode) {
22000 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22006 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22014 moveMonth: function(date, dir)
22019 var new_date = new Date(date.valueOf()),
22020 day = new_date.getUTCDate(),
22021 month = new_date.getUTCMonth(),
22022 mag = Math.abs(dir),
22024 dir = dir > 0 ? 1 : -1;
22027 // If going back one month, make sure month is not current month
22028 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22030 return new_date.getUTCMonth() == month;
22032 // If going forward one month, make sure month is as expected
22033 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22035 return new_date.getUTCMonth() != new_month;
22037 new_month = month + dir;
22038 new_date.setUTCMonth(new_month);
22039 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22040 if (new_month < 0 || new_month > 11) {
22041 new_month = (new_month + 12) % 12;
22044 // For magnitudes >1, move one month at a time...
22045 for (var i=0; i<mag; i++) {
22046 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22047 new_date = this.moveMonth(new_date, dir);
22049 // ...then reset the day, keeping it in the new month
22050 new_month = new_date.getUTCMonth();
22051 new_date.setUTCDate(day);
22053 return new_month != new_date.getUTCMonth();
22056 // Common date-resetting loop -- if date is beyond end of month, make it
22059 new_date.setUTCDate(--day);
22060 new_date.setUTCMonth(new_month);
22065 moveYear: function(date, dir)
22067 return this.moveMonth(date, dir*12);
22070 dateWithinRange: function(date)
22072 return date >= this.startDate && date <= this.endDate;
22078 this.picker().remove();
22081 validateValue : function(value)
22083 if(this.getVisibilityEl().hasClass('hidden')){
22087 if(value.length < 1) {
22088 if(this.allowBlank){
22094 if(value.length < this.minLength){
22097 if(value.length > this.maxLength){
22101 var vt = Roo.form.VTypes;
22102 if(!vt[this.vtype](value, this)){
22106 if(typeof this.validator == "function"){
22107 var msg = this.validator(value);
22113 if(this.regex && !this.regex.test(value)){
22117 if(typeof(this.parseDate(value)) == 'undefined'){
22121 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22125 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22135 this.date = this.viewDate = '';
22137 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22142 Roo.apply(Roo.bootstrap.DateField, {
22153 html: '<i class="fa fa-arrow-left"/>'
22163 html: '<i class="fa fa-arrow-right"/>'
22205 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22206 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22207 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22208 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22209 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22222 navFnc: 'FullYear',
22227 navFnc: 'FullYear',
22232 Roo.apply(Roo.bootstrap.DateField, {
22236 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22240 cls: 'datepicker-days',
22244 cls: 'table-condensed',
22246 Roo.bootstrap.DateField.head,
22250 Roo.bootstrap.DateField.footer
22257 cls: 'datepicker-months',
22261 cls: 'table-condensed',
22263 Roo.bootstrap.DateField.head,
22264 Roo.bootstrap.DateField.content,
22265 Roo.bootstrap.DateField.footer
22272 cls: 'datepicker-years',
22276 cls: 'table-condensed',
22278 Roo.bootstrap.DateField.head,
22279 Roo.bootstrap.DateField.content,
22280 Roo.bootstrap.DateField.footer
22299 * @class Roo.bootstrap.TimeField
22300 * @extends Roo.bootstrap.Input
22301 * Bootstrap DateField class
22305 * Create a new TimeField
22306 * @param {Object} config The config object
22309 Roo.bootstrap.TimeField = function(config){
22310 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22314 * Fires when this field show.
22315 * @param {Roo.bootstrap.DateField} thisthis
22316 * @param {Mixed} date The date value
22321 * Fires when this field hide.
22322 * @param {Roo.bootstrap.DateField} this
22323 * @param {Mixed} date The date value
22328 * Fires when select a date.
22329 * @param {Roo.bootstrap.DateField} this
22330 * @param {Mixed} date The date value
22336 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22339 * @cfg {String} format
22340 * The default time format string which can be overriden for localization support. The format must be
22341 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22345 getAutoCreate : function()
22347 this.after = '<i class="fa far fa-clock"></i>';
22348 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22352 onRender: function(ct, position)
22355 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22357 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22359 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22361 this.pop = this.picker().select('>.datepicker-time',true).first();
22362 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22364 this.picker().on('mousedown', this.onMousedown, this);
22365 this.picker().on('click', this.onClick, this);
22367 this.picker().addClass('datepicker-dropdown');
22372 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22373 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22374 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22375 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22376 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22377 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22381 fireKey: function(e){
22382 if (!this.picker().isVisible()){
22383 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22389 e.preventDefault();
22397 this.onTogglePeriod();
22400 this.onIncrementMinutes();
22403 this.onDecrementMinutes();
22412 onClick: function(e) {
22413 e.stopPropagation();
22414 e.preventDefault();
22417 picker : function()
22419 return this.pickerEl;
22422 fillTime: function()
22424 var time = this.pop.select('tbody', true).first();
22426 time.dom.innerHTML = '';
22441 cls: 'hours-up fa fas fa-chevron-up'
22461 cls: 'minutes-up fa fas fa-chevron-up'
22482 cls: 'timepicker-hour',
22497 cls: 'timepicker-minute',
22512 cls: 'btn btn-primary period',
22534 cls: 'hours-down fa fas fa-chevron-down'
22554 cls: 'minutes-down fa fas fa-chevron-down'
22572 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22579 var hours = this.time.getHours();
22580 var minutes = this.time.getMinutes();
22593 hours = hours - 12;
22597 hours = '0' + hours;
22601 minutes = '0' + minutes;
22604 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22605 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22606 this.pop.select('button', true).first().dom.innerHTML = period;
22612 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22614 var cls = ['bottom'];
22616 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22623 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22627 //this.picker().setXY(20000,20000);
22628 this.picker().addClass(cls.join('-'));
22632 Roo.each(cls, function(c){
22637 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22638 //_this.picker().setTop(_this.inputEl().getHeight());
22642 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22644 //_this.picker().setTop(0 - _this.picker().getHeight());
22649 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22653 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22661 onFocus : function()
22663 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22667 onBlur : function()
22669 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22675 this.picker().show();
22680 this.fireEvent('show', this, this.date);
22685 this.picker().hide();
22688 this.fireEvent('hide', this, this.date);
22691 setTime : function()
22694 this.setValue(this.time.format(this.format));
22696 this.fireEvent('select', this, this.date);
22701 onMousedown: function(e){
22702 e.stopPropagation();
22703 e.preventDefault();
22706 onIncrementHours: function()
22708 Roo.log('onIncrementHours');
22709 this.time = this.time.add(Date.HOUR, 1);
22714 onDecrementHours: function()
22716 Roo.log('onDecrementHours');
22717 this.time = this.time.add(Date.HOUR, -1);
22721 onIncrementMinutes: function()
22723 Roo.log('onIncrementMinutes');
22724 this.time = this.time.add(Date.MINUTE, 1);
22728 onDecrementMinutes: function()
22730 Roo.log('onDecrementMinutes');
22731 this.time = this.time.add(Date.MINUTE, -1);
22735 onTogglePeriod: function()
22737 Roo.log('onTogglePeriod');
22738 this.time = this.time.add(Date.HOUR, 12);
22746 Roo.apply(Roo.bootstrap.TimeField, {
22750 cls: 'datepicker dropdown-menu',
22754 cls: 'datepicker-time',
22758 cls: 'table-condensed',
22787 cls: 'btn btn-info ok',
22815 * @class Roo.bootstrap.MonthField
22816 * @extends Roo.bootstrap.Input
22817 * Bootstrap MonthField class
22819 * @cfg {String} language default en
22822 * Create a new MonthField
22823 * @param {Object} config The config object
22826 Roo.bootstrap.MonthField = function(config){
22827 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22832 * Fires when this field show.
22833 * @param {Roo.bootstrap.MonthField} this
22834 * @param {Mixed} date The date value
22839 * Fires when this field hide.
22840 * @param {Roo.bootstrap.MonthField} this
22841 * @param {Mixed} date The date value
22846 * Fires when select a date.
22847 * @param {Roo.bootstrap.MonthField} this
22848 * @param {String} oldvalue The old value
22849 * @param {String} newvalue The new value
22855 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22857 onRender: function(ct, position)
22860 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22862 this.language = this.language || 'en';
22863 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22864 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22866 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22867 this.isInline = false;
22868 this.isInput = true;
22869 this.component = this.el.select('.add-on', true).first() || false;
22870 this.component = (this.component && this.component.length === 0) ? false : this.component;
22871 this.hasInput = this.component && this.inputEL().length;
22873 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22875 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22877 this.picker().on('mousedown', this.onMousedown, this);
22878 this.picker().on('click', this.onClick, this);
22880 this.picker().addClass('datepicker-dropdown');
22882 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22883 v.setStyle('width', '189px');
22890 if(this.isInline) {
22896 setValue: function(v, suppressEvent)
22898 var o = this.getValue();
22900 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22904 if(suppressEvent !== true){
22905 this.fireEvent('select', this, o, v);
22910 getValue: function()
22915 onClick: function(e)
22917 e.stopPropagation();
22918 e.preventDefault();
22920 var target = e.getTarget();
22922 if(target.nodeName.toLowerCase() === 'i'){
22923 target = Roo.get(target).dom.parentNode;
22926 var nodeName = target.nodeName;
22927 var className = target.className;
22928 var html = target.innerHTML;
22930 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22934 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22936 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22942 picker : function()
22944 return this.pickerEl;
22947 fillMonths: function()
22950 var months = this.picker().select('>.datepicker-months td', true).first();
22952 months.dom.innerHTML = '';
22958 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22961 months.createChild(month);
22970 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22971 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22974 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22975 e.removeClass('active');
22977 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22978 e.addClass('active');
22985 if(this.isInline) {
22989 this.picker().removeClass(['bottom', 'top']);
22991 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22993 * place to the top of element!
22997 this.picker().addClass('top');
22998 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23003 this.picker().addClass('bottom');
23005 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23008 onFocus : function()
23010 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23014 onBlur : function()
23016 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23018 var d = this.inputEl().getValue();
23027 this.picker().show();
23028 this.picker().select('>.datepicker-months', true).first().show();
23032 this.fireEvent('show', this, this.date);
23037 if(this.isInline) {
23040 this.picker().hide();
23041 this.fireEvent('hide', this, this.date);
23045 onMousedown: function(e)
23047 e.stopPropagation();
23048 e.preventDefault();
23053 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23057 fireKey: function(e)
23059 if (!this.picker().isVisible()){
23060 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23071 e.preventDefault();
23075 dir = e.keyCode == 37 ? -1 : 1;
23077 this.vIndex = this.vIndex + dir;
23079 if(this.vIndex < 0){
23083 if(this.vIndex > 11){
23087 if(isNaN(this.vIndex)){
23091 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23097 dir = e.keyCode == 38 ? -1 : 1;
23099 this.vIndex = this.vIndex + dir * 4;
23101 if(this.vIndex < 0){
23105 if(this.vIndex > 11){
23109 if(isNaN(this.vIndex)){
23113 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23118 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23119 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23123 e.preventDefault();
23126 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23127 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23143 this.picker().remove();
23148 Roo.apply(Roo.bootstrap.MonthField, {
23167 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23168 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23173 Roo.apply(Roo.bootstrap.MonthField, {
23177 cls: 'datepicker dropdown-menu roo-dynamic',
23181 cls: 'datepicker-months',
23185 cls: 'table-condensed',
23187 Roo.bootstrap.DateField.content
23207 * @class Roo.bootstrap.CheckBox
23208 * @extends Roo.bootstrap.Input
23209 * Bootstrap CheckBox class
23211 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23212 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23213 * @cfg {String} boxLabel The text that appears beside the checkbox
23214 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23215 * @cfg {Boolean} checked initnal the element
23216 * @cfg {Boolean} inline inline the element (default false)
23217 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23218 * @cfg {String} tooltip label tooltip
23221 * Create a new CheckBox
23222 * @param {Object} config The config object
23225 Roo.bootstrap.CheckBox = function(config){
23226 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23231 * Fires when the element is checked or unchecked.
23232 * @param {Roo.bootstrap.CheckBox} this This input
23233 * @param {Boolean} checked The new checked value
23238 * Fires when the element is click.
23239 * @param {Roo.bootstrap.CheckBox} this This input
23246 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23248 inputType: 'checkbox',
23257 // checkbox success does not make any sense really..
23262 getAutoCreate : function()
23264 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23270 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23273 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23279 type : this.inputType,
23280 value : this.inputValue,
23281 cls : 'roo-' + this.inputType, //'form-box',
23282 placeholder : this.placeholder || ''
23286 if(this.inputType != 'radio'){
23290 cls : 'roo-hidden-value',
23291 value : this.checked ? this.inputValue : this.valueOff
23296 if (this.weight) { // Validity check?
23297 cfg.cls += " " + this.inputType + "-" + this.weight;
23300 if (this.disabled) {
23301 input.disabled=true;
23305 input.checked = this.checked;
23310 input.name = this.name;
23312 if(this.inputType != 'radio'){
23313 hidden.name = this.name;
23314 input.name = '_hidden_' + this.name;
23319 input.cls += ' input-' + this.size;
23324 ['xs','sm','md','lg'].map(function(size){
23325 if (settings[size]) {
23326 cfg.cls += ' col-' + size + '-' + settings[size];
23330 var inputblock = input;
23332 if (this.before || this.after) {
23335 cls : 'input-group',
23340 inputblock.cn.push({
23342 cls : 'input-group-addon',
23347 inputblock.cn.push(input);
23349 if(this.inputType != 'radio'){
23350 inputblock.cn.push(hidden);
23354 inputblock.cn.push({
23356 cls : 'input-group-addon',
23362 var boxLabelCfg = false;
23368 //'for': id, // box label is handled by onclick - so no for...
23370 html: this.boxLabel
23373 boxLabelCfg.tooltip = this.tooltip;
23379 if (align ==='left' && this.fieldLabel.length) {
23380 // Roo.log("left and has label");
23385 cls : 'control-label',
23386 html : this.fieldLabel
23397 cfg.cn[1].cn.push(boxLabelCfg);
23400 if(this.labelWidth > 12){
23401 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23404 if(this.labelWidth < 13 && this.labelmd == 0){
23405 this.labelmd = this.labelWidth;
23408 if(this.labellg > 0){
23409 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23410 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23413 if(this.labelmd > 0){
23414 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23415 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23418 if(this.labelsm > 0){
23419 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23420 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23423 if(this.labelxs > 0){
23424 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23425 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23428 } else if ( this.fieldLabel.length) {
23429 // Roo.log(" label");
23433 tag: this.boxLabel ? 'span' : 'label',
23435 cls: 'control-label box-input-label',
23436 //cls : 'input-group-addon',
23437 html : this.fieldLabel
23444 cfg.cn.push(boxLabelCfg);
23449 // Roo.log(" no label && no align");
23450 cfg.cn = [ inputblock ] ;
23452 cfg.cn.push(boxLabelCfg);
23460 if(this.inputType != 'radio'){
23461 cfg.cn.push(hidden);
23469 * return the real input element.
23471 inputEl: function ()
23473 return this.el.select('input.roo-' + this.inputType,true).first();
23475 hiddenEl: function ()
23477 return this.el.select('input.roo-hidden-value',true).first();
23480 labelEl: function()
23482 return this.el.select('label.control-label',true).first();
23484 /* depricated... */
23488 return this.labelEl();
23491 boxLabelEl: function()
23493 return this.el.select('label.box-label',true).first();
23496 initEvents : function()
23498 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23500 this.inputEl().on('click', this.onClick, this);
23502 if (this.boxLabel) {
23503 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23506 this.startValue = this.getValue();
23509 Roo.bootstrap.CheckBox.register(this);
23513 onClick : function(e)
23515 if(this.fireEvent('click', this, e) !== false){
23516 this.setChecked(!this.checked);
23521 setChecked : function(state,suppressEvent)
23523 this.startValue = this.getValue();
23525 if(this.inputType == 'radio'){
23527 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23528 e.dom.checked = false;
23531 this.inputEl().dom.checked = true;
23533 this.inputEl().dom.value = this.inputValue;
23535 if(suppressEvent !== true){
23536 this.fireEvent('check', this, true);
23544 this.checked = state;
23546 this.inputEl().dom.checked = state;
23549 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23551 if(suppressEvent !== true){
23552 this.fireEvent('check', this, state);
23558 getValue : function()
23560 if(this.inputType == 'radio'){
23561 return this.getGroupValue();
23564 return this.hiddenEl().dom.value;
23568 getGroupValue : function()
23570 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23574 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23577 setValue : function(v,suppressEvent)
23579 if(this.inputType == 'radio'){
23580 this.setGroupValue(v, suppressEvent);
23584 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23589 setGroupValue : function(v, suppressEvent)
23591 this.startValue = this.getValue();
23593 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23594 e.dom.checked = false;
23596 if(e.dom.value == v){
23597 e.dom.checked = true;
23601 if(suppressEvent !== true){
23602 this.fireEvent('check', this, true);
23610 validate : function()
23612 if(this.getVisibilityEl().hasClass('hidden')){
23618 (this.inputType == 'radio' && this.validateRadio()) ||
23619 (this.inputType == 'checkbox' && this.validateCheckbox())
23625 this.markInvalid();
23629 validateRadio : function()
23631 if(this.getVisibilityEl().hasClass('hidden')){
23635 if(this.allowBlank){
23641 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23642 if(!e.dom.checked){
23654 validateCheckbox : function()
23657 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23658 //return (this.getValue() == this.inputValue) ? true : false;
23661 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23669 for(var i in group){
23670 if(group[i].el.isVisible(true)){
23678 for(var i in group){
23683 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23690 * Mark this field as valid
23692 markValid : function()
23696 this.fireEvent('valid', this);
23698 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23701 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23708 if(this.inputType == 'radio'){
23709 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23710 var fg = e.findParent('.form-group', false, true);
23711 if (Roo.bootstrap.version == 3) {
23712 fg.removeClass([_this.invalidClass, _this.validClass]);
23713 fg.addClass(_this.validClass);
23715 fg.removeClass(['is-valid', 'is-invalid']);
23716 fg.addClass('is-valid');
23724 var fg = this.el.findParent('.form-group', false, true);
23725 if (Roo.bootstrap.version == 3) {
23726 fg.removeClass([this.invalidClass, this.validClass]);
23727 fg.addClass(this.validClass);
23729 fg.removeClass(['is-valid', 'is-invalid']);
23730 fg.addClass('is-valid');
23735 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23741 for(var i in group){
23742 var fg = group[i].el.findParent('.form-group', false, true);
23743 if (Roo.bootstrap.version == 3) {
23744 fg.removeClass([this.invalidClass, this.validClass]);
23745 fg.addClass(this.validClass);
23747 fg.removeClass(['is-valid', 'is-invalid']);
23748 fg.addClass('is-valid');
23754 * Mark this field as invalid
23755 * @param {String} msg The validation message
23757 markInvalid : function(msg)
23759 if(this.allowBlank){
23765 this.fireEvent('invalid', this, msg);
23767 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23770 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23774 label.markInvalid();
23777 if(this.inputType == 'radio'){
23779 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23780 var fg = e.findParent('.form-group', false, true);
23781 if (Roo.bootstrap.version == 3) {
23782 fg.removeClass([_this.invalidClass, _this.validClass]);
23783 fg.addClass(_this.invalidClass);
23785 fg.removeClass(['is-invalid', 'is-valid']);
23786 fg.addClass('is-invalid');
23794 var fg = this.el.findParent('.form-group', false, true);
23795 if (Roo.bootstrap.version == 3) {
23796 fg.removeClass([_this.invalidClass, _this.validClass]);
23797 fg.addClass(_this.invalidClass);
23799 fg.removeClass(['is-invalid', 'is-valid']);
23800 fg.addClass('is-invalid');
23805 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23811 for(var i in group){
23812 var fg = group[i].el.findParent('.form-group', false, true);
23813 if (Roo.bootstrap.version == 3) {
23814 fg.removeClass([_this.invalidClass, _this.validClass]);
23815 fg.addClass(_this.invalidClass);
23817 fg.removeClass(['is-invalid', 'is-valid']);
23818 fg.addClass('is-invalid');
23824 clearInvalid : function()
23826 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23828 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23830 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23832 if (label && label.iconEl) {
23833 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23834 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23838 disable : function()
23840 if(this.inputType != 'radio'){
23841 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23848 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23849 _this.getActionEl().addClass(this.disabledClass);
23850 e.dom.disabled = true;
23854 this.disabled = true;
23855 this.fireEvent("disable", this);
23859 enable : function()
23861 if(this.inputType != 'radio'){
23862 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23869 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23870 _this.getActionEl().removeClass(this.disabledClass);
23871 e.dom.disabled = false;
23875 this.disabled = false;
23876 this.fireEvent("enable", this);
23880 setBoxLabel : function(v)
23885 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23891 Roo.apply(Roo.bootstrap.CheckBox, {
23896 * register a CheckBox Group
23897 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23899 register : function(checkbox)
23901 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23902 this.groups[checkbox.groupId] = {};
23905 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23909 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23913 * fetch a CheckBox Group based on the group ID
23914 * @param {string} the group ID
23915 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23917 get: function(groupId) {
23918 if (typeof(this.groups[groupId]) == 'undefined') {
23922 return this.groups[groupId] ;
23935 * @class Roo.bootstrap.Radio
23936 * @extends Roo.bootstrap.Component
23937 * Bootstrap Radio class
23938 * @cfg {String} boxLabel - the label associated
23939 * @cfg {String} value - the value of radio
23942 * Create a new Radio
23943 * @param {Object} config The config object
23945 Roo.bootstrap.Radio = function(config){
23946 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23950 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23956 getAutoCreate : function()
23960 cls : 'form-group radio',
23965 html : this.boxLabel
23973 initEvents : function()
23975 this.parent().register(this);
23977 this.el.on('click', this.onClick, this);
23981 onClick : function(e)
23983 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23984 this.setChecked(true);
23988 setChecked : function(state, suppressEvent)
23990 this.parent().setValue(this.value, suppressEvent);
23994 setBoxLabel : function(v)
23999 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24014 * @class Roo.bootstrap.SecurePass
24015 * @extends Roo.bootstrap.Input
24016 * Bootstrap SecurePass class
24020 * Create a new SecurePass
24021 * @param {Object} config The config object
24024 Roo.bootstrap.SecurePass = function (config) {
24025 // these go here, so the translation tool can replace them..
24027 PwdEmpty: "Please type a password, and then retype it to confirm.",
24028 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24029 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24030 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24031 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24032 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24033 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24034 TooWeak: "Your password is Too Weak."
24036 this.meterLabel = "Password strength:";
24037 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24038 this.meterClass = [
24039 "roo-password-meter-tooweak",
24040 "roo-password-meter-weak",
24041 "roo-password-meter-medium",
24042 "roo-password-meter-strong",
24043 "roo-password-meter-grey"
24048 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24051 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24053 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24055 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24056 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24057 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24058 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24059 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24060 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24061 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24071 * @cfg {String/Object} Label for the strength meter (defaults to
24072 * 'Password strength:')
24077 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24078 * ['Weak', 'Medium', 'Strong'])
24081 pwdStrengths: false,
24094 initEvents: function ()
24096 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24098 if (this.el.is('input[type=password]') && Roo.isSafari) {
24099 this.el.on('keydown', this.SafariOnKeyDown, this);
24102 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24105 onRender: function (ct, position)
24107 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24108 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24109 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24111 this.trigger.createChild({
24116 cls: 'roo-password-meter-grey col-xs-12',
24119 //width: this.meterWidth + 'px'
24123 cls: 'roo-password-meter-text'
24129 if (this.hideTrigger) {
24130 this.trigger.setDisplayed(false);
24132 this.setSize(this.width || '', this.height || '');
24135 onDestroy: function ()
24137 if (this.trigger) {
24138 this.trigger.removeAllListeners();
24139 this.trigger.remove();
24142 this.wrap.remove();
24144 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24147 checkStrength: function ()
24149 var pwd = this.inputEl().getValue();
24150 if (pwd == this._lastPwd) {
24155 if (this.ClientSideStrongPassword(pwd)) {
24157 } else if (this.ClientSideMediumPassword(pwd)) {
24159 } else if (this.ClientSideWeakPassword(pwd)) {
24165 Roo.log('strength1: ' + strength);
24167 //var pm = this.trigger.child('div/div/div').dom;
24168 var pm = this.trigger.child('div/div');
24169 pm.removeClass(this.meterClass);
24170 pm.addClass(this.meterClass[strength]);
24173 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24175 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24177 this._lastPwd = pwd;
24181 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24183 this._lastPwd = '';
24185 var pm = this.trigger.child('div/div');
24186 pm.removeClass(this.meterClass);
24187 pm.addClass('roo-password-meter-grey');
24190 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24193 this.inputEl().dom.type='password';
24196 validateValue: function (value)
24198 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24201 if (value.length == 0) {
24202 if (this.allowBlank) {
24203 this.clearInvalid();
24207 this.markInvalid(this.errors.PwdEmpty);
24208 this.errorMsg = this.errors.PwdEmpty;
24216 if (!value.match(/[\x21-\x7e]+/)) {
24217 this.markInvalid(this.errors.PwdBadChar);
24218 this.errorMsg = this.errors.PwdBadChar;
24221 if (value.length < 6) {
24222 this.markInvalid(this.errors.PwdShort);
24223 this.errorMsg = this.errors.PwdShort;
24226 if (value.length > 16) {
24227 this.markInvalid(this.errors.PwdLong);
24228 this.errorMsg = this.errors.PwdLong;
24232 if (this.ClientSideStrongPassword(value)) {
24234 } else if (this.ClientSideMediumPassword(value)) {
24236 } else if (this.ClientSideWeakPassword(value)) {
24243 if (strength < 2) {
24244 //this.markInvalid(this.errors.TooWeak);
24245 this.errorMsg = this.errors.TooWeak;
24250 console.log('strength2: ' + strength);
24252 //var pm = this.trigger.child('div/div/div').dom;
24254 var pm = this.trigger.child('div/div');
24255 pm.removeClass(this.meterClass);
24256 pm.addClass(this.meterClass[strength]);
24258 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24260 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24262 this.errorMsg = '';
24266 CharacterSetChecks: function (type)
24269 this.fResult = false;
24272 isctype: function (character, type)
24275 case this.kCapitalLetter:
24276 if (character >= 'A' && character <= 'Z') {
24281 case this.kSmallLetter:
24282 if (character >= 'a' && character <= 'z') {
24288 if (character >= '0' && character <= '9') {
24293 case this.kPunctuation:
24294 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24305 IsLongEnough: function (pwd, size)
24307 return !(pwd == null || isNaN(size) || pwd.length < size);
24310 SpansEnoughCharacterSets: function (word, nb)
24312 if (!this.IsLongEnough(word, nb))
24317 var characterSetChecks = new Array(
24318 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24319 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24322 for (var index = 0; index < word.length; ++index) {
24323 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24324 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24325 characterSetChecks[nCharSet].fResult = true;
24332 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24333 if (characterSetChecks[nCharSet].fResult) {
24338 if (nCharSets < nb) {
24344 ClientSideStrongPassword: function (pwd)
24346 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24349 ClientSideMediumPassword: function (pwd)
24351 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24354 ClientSideWeakPassword: function (pwd)
24356 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24359 })//<script type="text/javascript">
24362 * Based Ext JS Library 1.1.1
24363 * Copyright(c) 2006-2007, Ext JS, LLC.
24369 * @class Roo.HtmlEditorCore
24370 * @extends Roo.Component
24371 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24373 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24376 Roo.HtmlEditorCore = function(config){
24379 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24384 * @event initialize
24385 * Fires when the editor is fully initialized (including the iframe)
24386 * @param {Roo.HtmlEditorCore} this
24391 * Fires when the editor is first receives the focus. Any insertion must wait
24392 * until after this event.
24393 * @param {Roo.HtmlEditorCore} this
24397 * @event beforesync
24398 * Fires before the textarea is updated with content from the editor iframe. Return false
24399 * to cancel the sync.
24400 * @param {Roo.HtmlEditorCore} this
24401 * @param {String} html
24405 * @event beforepush
24406 * Fires before the iframe editor is updated with content from the textarea. Return false
24407 * to cancel the push.
24408 * @param {Roo.HtmlEditorCore} this
24409 * @param {String} html
24414 * Fires when the textarea is updated with content from the editor iframe.
24415 * @param {Roo.HtmlEditorCore} this
24416 * @param {String} html
24421 * Fires when the iframe editor is updated with content from the textarea.
24422 * @param {Roo.HtmlEditorCore} this
24423 * @param {String} html
24428 * @event editorevent
24429 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24430 * @param {Roo.HtmlEditorCore} this
24436 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24438 // defaults : white / black...
24439 this.applyBlacklists();
24446 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24450 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24456 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24461 * @cfg {Number} height (in pixels)
24465 * @cfg {Number} width (in pixels)
24470 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24473 stylesheets: false,
24478 // private properties
24479 validationEvent : false,
24481 initialized : false,
24483 sourceEditMode : false,
24484 onFocus : Roo.emptyFn,
24486 hideMode:'offsets',
24490 // blacklist + whitelisted elements..
24497 * Protected method that will not generally be called directly. It
24498 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24499 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24501 getDocMarkup : function(){
24505 // inherit styels from page...??
24506 if (this.stylesheets === false) {
24508 Roo.get(document.head).select('style').each(function(node) {
24509 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24512 Roo.get(document.head).select('link').each(function(node) {
24513 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24516 } else if (!this.stylesheets.length) {
24518 st = '<style type="text/css">' +
24519 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24522 for (var i in this.stylesheets) {
24523 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24528 st += '<style type="text/css">' +
24529 'IMG { cursor: pointer } ' +
24532 var cls = 'roo-htmleditor-body';
24534 if(this.bodyCls.length){
24535 cls += ' ' + this.bodyCls;
24538 return '<html><head>' + st +
24539 //<style type="text/css">' +
24540 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24542 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24546 onRender : function(ct, position)
24549 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24550 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24553 this.el.dom.style.border = '0 none';
24554 this.el.dom.setAttribute('tabIndex', -1);
24555 this.el.addClass('x-hidden hide');
24559 if(Roo.isIE){ // fix IE 1px bogus margin
24560 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24564 this.frameId = Roo.id();
24568 var iframe = this.owner.wrap.createChild({
24570 cls: 'form-control', // bootstrap..
24572 name: this.frameId,
24573 frameBorder : 'no',
24574 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24579 this.iframe = iframe.dom;
24581 this.assignDocWin();
24583 this.doc.designMode = 'on';
24586 this.doc.write(this.getDocMarkup());
24590 var task = { // must defer to wait for browser to be ready
24592 //console.log("run task?" + this.doc.readyState);
24593 this.assignDocWin();
24594 if(this.doc.body || this.doc.readyState == 'complete'){
24596 this.doc.designMode="on";
24600 Roo.TaskMgr.stop(task);
24601 this.initEditor.defer(10, this);
24608 Roo.TaskMgr.start(task);
24613 onResize : function(w, h)
24615 Roo.log('resize: ' +w + ',' + h );
24616 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24620 if(typeof w == 'number'){
24622 this.iframe.style.width = w + 'px';
24624 if(typeof h == 'number'){
24626 this.iframe.style.height = h + 'px';
24628 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24635 * Toggles the editor between standard and source edit mode.
24636 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24638 toggleSourceEdit : function(sourceEditMode){
24640 this.sourceEditMode = sourceEditMode === true;
24642 if(this.sourceEditMode){
24644 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24647 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24648 //this.iframe.className = '';
24651 //this.setSize(this.owner.wrap.getSize());
24652 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24659 * Protected method that will not generally be called directly. If you need/want
24660 * custom HTML cleanup, this is the method you should override.
24661 * @param {String} html The HTML to be cleaned
24662 * return {String} The cleaned HTML
24664 cleanHtml : function(html){
24665 html = String(html);
24666 if(html.length > 5){
24667 if(Roo.isSafari){ // strip safari nonsense
24668 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24671 if(html == ' '){
24678 * HTML Editor -> Textarea
24679 * Protected method that will not generally be called directly. Syncs the contents
24680 * of the editor iframe with the textarea.
24682 syncValue : function(){
24683 if(this.initialized){
24684 var bd = (this.doc.body || this.doc.documentElement);
24685 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24686 var html = bd.innerHTML;
24688 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24689 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24691 html = '<div style="'+m[0]+'">' + html + '</div>';
24694 html = this.cleanHtml(html);
24695 // fix up the special chars.. normaly like back quotes in word...
24696 // however we do not want to do this with chinese..
24697 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24699 var cc = match.charCodeAt();
24701 // Get the character value, handling surrogate pairs
24702 if (match.length == 2) {
24703 // It's a surrogate pair, calculate the Unicode code point
24704 var high = match.charCodeAt(0) - 0xD800;
24705 var low = match.charCodeAt(1) - 0xDC00;
24706 cc = (high * 0x400) + low + 0x10000;
24708 (cc >= 0x4E00 && cc < 0xA000 ) ||
24709 (cc >= 0x3400 && cc < 0x4E00 ) ||
24710 (cc >= 0xf900 && cc < 0xfb00 )
24715 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24716 return "&#" + cc + ";";
24723 if(this.owner.fireEvent('beforesync', this, html) !== false){
24724 this.el.dom.value = html;
24725 this.owner.fireEvent('sync', this, html);
24731 * Protected method that will not generally be called directly. Pushes the value of the textarea
24732 * into the iframe editor.
24734 pushValue : function(){
24735 if(this.initialized){
24736 var v = this.el.dom.value.trim();
24738 // if(v.length < 1){
24742 if(this.owner.fireEvent('beforepush', this, v) !== false){
24743 var d = (this.doc.body || this.doc.documentElement);
24745 this.cleanUpPaste();
24746 this.el.dom.value = d.innerHTML;
24747 this.owner.fireEvent('push', this, v);
24753 deferFocus : function(){
24754 this.focus.defer(10, this);
24758 focus : function(){
24759 if(this.win && !this.sourceEditMode){
24766 assignDocWin: function()
24768 var iframe = this.iframe;
24771 this.doc = iframe.contentWindow.document;
24772 this.win = iframe.contentWindow;
24774 // if (!Roo.get(this.frameId)) {
24777 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24778 // this.win = Roo.get(this.frameId).dom.contentWindow;
24780 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24784 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24785 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24790 initEditor : function(){
24791 //console.log("INIT EDITOR");
24792 this.assignDocWin();
24796 this.doc.designMode="on";
24798 this.doc.write(this.getDocMarkup());
24801 var dbody = (this.doc.body || this.doc.documentElement);
24802 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24803 // this copies styles from the containing element into thsi one..
24804 // not sure why we need all of this..
24805 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24807 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24808 //ss['background-attachment'] = 'fixed'; // w3c
24809 dbody.bgProperties = 'fixed'; // ie
24810 //Roo.DomHelper.applyStyles(dbody, ss);
24811 Roo.EventManager.on(this.doc, {
24812 //'mousedown': this.onEditorEvent,
24813 'mouseup': this.onEditorEvent,
24814 'dblclick': this.onEditorEvent,
24815 'click': this.onEditorEvent,
24816 'keyup': this.onEditorEvent,
24821 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24823 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24824 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24826 this.initialized = true;
24828 this.owner.fireEvent('initialize', this);
24833 onDestroy : function(){
24839 //for (var i =0; i < this.toolbars.length;i++) {
24840 // // fixme - ask toolbars for heights?
24841 // this.toolbars[i].onDestroy();
24844 //this.wrap.dom.innerHTML = '';
24845 //this.wrap.remove();
24850 onFirstFocus : function(){
24852 this.assignDocWin();
24855 this.activated = true;
24858 if(Roo.isGecko){ // prevent silly gecko errors
24860 var s = this.win.getSelection();
24861 if(!s.focusNode || s.focusNode.nodeType != 3){
24862 var r = s.getRangeAt(0);
24863 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24868 this.execCmd('useCSS', true);
24869 this.execCmd('styleWithCSS', false);
24872 this.owner.fireEvent('activate', this);
24876 adjustFont: function(btn){
24877 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24878 //if(Roo.isSafari){ // safari
24881 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24882 if(Roo.isSafari){ // safari
24883 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24884 v = (v < 10) ? 10 : v;
24885 v = (v > 48) ? 48 : v;
24886 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24891 v = Math.max(1, v+adjust);
24893 this.execCmd('FontSize', v );
24896 onEditorEvent : function(e)
24898 this.owner.fireEvent('editorevent', this, e);
24899 // this.updateToolbar();
24900 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24903 insertTag : function(tg)
24905 // could be a bit smarter... -> wrap the current selected tRoo..
24906 if (tg.toLowerCase() == 'span' ||
24907 tg.toLowerCase() == 'code' ||
24908 tg.toLowerCase() == 'sup' ||
24909 tg.toLowerCase() == 'sub'
24912 range = this.createRange(this.getSelection());
24913 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24914 wrappingNode.appendChild(range.extractContents());
24915 range.insertNode(wrappingNode);
24922 this.execCmd("formatblock", tg);
24926 insertText : function(txt)
24930 var range = this.createRange();
24931 range.deleteContents();
24932 //alert(Sender.getAttribute('label'));
24934 range.insertNode(this.doc.createTextNode(txt));
24940 * Executes a Midas editor command on the editor document and performs necessary focus and
24941 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24942 * @param {String} cmd The Midas command
24943 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24945 relayCmd : function(cmd, value){
24947 this.execCmd(cmd, value);
24948 this.owner.fireEvent('editorevent', this);
24949 //this.updateToolbar();
24950 this.owner.deferFocus();
24954 * Executes a Midas editor command directly on the editor document.
24955 * For visual commands, you should use {@link #relayCmd} instead.
24956 * <b>This should only be called after the editor is initialized.</b>
24957 * @param {String} cmd The Midas command
24958 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24960 execCmd : function(cmd, value){
24961 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24968 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24970 * @param {String} text | dom node..
24972 insertAtCursor : function(text)
24975 if(!this.activated){
24981 var r = this.doc.selection.createRange();
24992 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24996 // from jquery ui (MIT licenced)
24998 var win = this.win;
25000 if (win.getSelection && win.getSelection().getRangeAt) {
25001 range = win.getSelection().getRangeAt(0);
25002 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25003 range.insertNode(node);
25004 } else if (win.document.selection && win.document.selection.createRange) {
25005 // no firefox support
25006 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25007 win.document.selection.createRange().pasteHTML(txt);
25009 // no firefox support
25010 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25011 this.execCmd('InsertHTML', txt);
25020 mozKeyPress : function(e){
25022 var c = e.getCharCode(), cmd;
25025 c = String.fromCharCode(c).toLowerCase();
25039 this.cleanUpPaste.defer(100, this);
25047 e.preventDefault();
25055 fixKeys : function(){ // load time branching for fastest keydown performance
25057 return function(e){
25058 var k = e.getKey(), r;
25061 r = this.doc.selection.createRange();
25064 r.pasteHTML('    ');
25071 r = this.doc.selection.createRange();
25073 var target = r.parentElement();
25074 if(!target || target.tagName.toLowerCase() != 'li'){
25076 r.pasteHTML('<br />');
25082 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25083 this.cleanUpPaste.defer(100, this);
25089 }else if(Roo.isOpera){
25090 return function(e){
25091 var k = e.getKey();
25095 this.execCmd('InsertHTML','    ');
25098 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25099 this.cleanUpPaste.defer(100, this);
25104 }else if(Roo.isSafari){
25105 return function(e){
25106 var k = e.getKey();
25110 this.execCmd('InsertText','\t');
25114 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25115 this.cleanUpPaste.defer(100, this);
25123 getAllAncestors: function()
25125 var p = this.getSelectedNode();
25128 a.push(p); // push blank onto stack..
25129 p = this.getParentElement();
25133 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25137 a.push(this.doc.body);
25141 lastSelNode : false,
25144 getSelection : function()
25146 this.assignDocWin();
25147 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25150 getSelectedNode: function()
25152 // this may only work on Gecko!!!
25154 // should we cache this!!!!
25159 var range = this.createRange(this.getSelection()).cloneRange();
25162 var parent = range.parentElement();
25164 var testRange = range.duplicate();
25165 testRange.moveToElementText(parent);
25166 if (testRange.inRange(range)) {
25169 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25172 parent = parent.parentElement;
25177 // is ancestor a text element.
25178 var ac = range.commonAncestorContainer;
25179 if (ac.nodeType == 3) {
25180 ac = ac.parentNode;
25183 var ar = ac.childNodes;
25186 var other_nodes = [];
25187 var has_other_nodes = false;
25188 for (var i=0;i<ar.length;i++) {
25189 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25192 // fullly contained node.
25194 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25199 // probably selected..
25200 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25201 other_nodes.push(ar[i]);
25205 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25210 has_other_nodes = true;
25212 if (!nodes.length && other_nodes.length) {
25213 nodes= other_nodes;
25215 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25221 createRange: function(sel)
25223 // this has strange effects when using with
25224 // top toolbar - not sure if it's a great idea.
25225 //this.editor.contentWindow.focus();
25226 if (typeof sel != "undefined") {
25228 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25230 return this.doc.createRange();
25233 return this.doc.createRange();
25236 getParentElement: function()
25239 this.assignDocWin();
25240 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25242 var range = this.createRange(sel);
25245 var p = range.commonAncestorContainer;
25246 while (p.nodeType == 3) { // text node
25257 * Range intersection.. the hard stuff...
25261 * [ -- selected range --- ]
25265 * if end is before start or hits it. fail.
25266 * if start is after end or hits it fail.
25268 * if either hits (but other is outside. - then it's not
25274 // @see http://www.thismuchiknow.co.uk/?p=64.
25275 rangeIntersectsNode : function(range, node)
25277 var nodeRange = node.ownerDocument.createRange();
25279 nodeRange.selectNode(node);
25281 nodeRange.selectNodeContents(node);
25284 var rangeStartRange = range.cloneRange();
25285 rangeStartRange.collapse(true);
25287 var rangeEndRange = range.cloneRange();
25288 rangeEndRange.collapse(false);
25290 var nodeStartRange = nodeRange.cloneRange();
25291 nodeStartRange.collapse(true);
25293 var nodeEndRange = nodeRange.cloneRange();
25294 nodeEndRange.collapse(false);
25296 return rangeStartRange.compareBoundaryPoints(
25297 Range.START_TO_START, nodeEndRange) == -1 &&
25298 rangeEndRange.compareBoundaryPoints(
25299 Range.START_TO_START, nodeStartRange) == 1;
25303 rangeCompareNode : function(range, node)
25305 var nodeRange = node.ownerDocument.createRange();
25307 nodeRange.selectNode(node);
25309 nodeRange.selectNodeContents(node);
25313 range.collapse(true);
25315 nodeRange.collapse(true);
25317 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25318 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25320 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25322 var nodeIsBefore = ss == 1;
25323 var nodeIsAfter = ee == -1;
25325 if (nodeIsBefore && nodeIsAfter) {
25328 if (!nodeIsBefore && nodeIsAfter) {
25329 return 1; //right trailed.
25332 if (nodeIsBefore && !nodeIsAfter) {
25333 return 2; // left trailed.
25339 // private? - in a new class?
25340 cleanUpPaste : function()
25342 // cleans up the whole document..
25343 Roo.log('cleanuppaste');
25345 this.cleanUpChildren(this.doc.body);
25346 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25347 if (clean != this.doc.body.innerHTML) {
25348 this.doc.body.innerHTML = clean;
25353 cleanWordChars : function(input) {// change the chars to hex code
25354 var he = Roo.HtmlEditorCore;
25356 var output = input;
25357 Roo.each(he.swapCodes, function(sw) {
25358 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25360 output = output.replace(swapper, sw[1]);
25367 cleanUpChildren : function (n)
25369 if (!n.childNodes.length) {
25372 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25373 this.cleanUpChild(n.childNodes[i]);
25380 cleanUpChild : function (node)
25383 //console.log(node);
25384 if (node.nodeName == "#text") {
25385 // clean up silly Windows -- stuff?
25388 if (node.nodeName == "#comment") {
25389 node.parentNode.removeChild(node);
25390 // clean up silly Windows -- stuff?
25393 var lcname = node.tagName.toLowerCase();
25394 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25395 // whitelist of tags..
25397 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25399 node.parentNode.removeChild(node);
25404 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25406 // spans with no attributes - just remove them..
25407 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25408 remove_keep_children = true;
25411 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25412 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25414 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25415 // remove_keep_children = true;
25418 if (remove_keep_children) {
25419 this.cleanUpChildren(node);
25420 // inserts everything just before this node...
25421 while (node.childNodes.length) {
25422 var cn = node.childNodes[0];
25423 node.removeChild(cn);
25424 node.parentNode.insertBefore(cn, node);
25426 node.parentNode.removeChild(node);
25430 if (!node.attributes || !node.attributes.length) {
25435 this.cleanUpChildren(node);
25439 function cleanAttr(n,v)
25442 if (v.match(/^\./) || v.match(/^\//)) {
25445 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25448 if (v.match(/^#/)) {
25451 if (v.match(/^\{/)) { // allow template editing.
25454 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25455 node.removeAttribute(n);
25459 var cwhite = this.cwhite;
25460 var cblack = this.cblack;
25462 function cleanStyle(n,v)
25464 if (v.match(/expression/)) { //XSS?? should we even bother..
25465 node.removeAttribute(n);
25469 var parts = v.split(/;/);
25472 Roo.each(parts, function(p) {
25473 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25477 var l = p.split(':').shift().replace(/\s+/g,'');
25478 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25480 if ( cwhite.length && cblack.indexOf(l) > -1) {
25481 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25482 //node.removeAttribute(n);
25486 // only allow 'c whitelisted system attributes'
25487 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25488 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25489 //node.removeAttribute(n);
25499 if (clean.length) {
25500 node.setAttribute(n, clean.join(';'));
25502 node.removeAttribute(n);
25508 for (var i = node.attributes.length-1; i > -1 ; i--) {
25509 var a = node.attributes[i];
25512 if (a.name.toLowerCase().substr(0,2)=='on') {
25513 node.removeAttribute(a.name);
25516 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25517 node.removeAttribute(a.name);
25520 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25521 cleanAttr(a.name,a.value); // fixme..
25524 if (a.name == 'style') {
25525 cleanStyle(a.name,a.value);
25528 /// clean up MS crap..
25529 // tecnically this should be a list of valid class'es..
25532 if (a.name == 'class') {
25533 if (a.value.match(/^Mso/)) {
25534 node.removeAttribute('class');
25537 if (a.value.match(/^body$/)) {
25538 node.removeAttribute('class');
25549 this.cleanUpChildren(node);
25555 * Clean up MS wordisms...
25557 cleanWord : function(node)
25560 this.cleanWord(this.doc.body);
25565 node.nodeName == 'SPAN' &&
25566 !node.hasAttributes() &&
25567 node.childNodes.length == 1 &&
25568 node.firstChild.nodeName == "#text"
25570 var textNode = node.firstChild;
25571 node.removeChild(textNode);
25572 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25573 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25575 node.parentNode.insertBefore(textNode, node);
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.removeChild(node);
25582 if (node.nodeName == "#text") {
25583 // clean up silly Windows -- stuff?
25586 if (node.nodeName == "#comment") {
25587 node.parentNode.removeChild(node);
25588 // clean up silly Windows -- stuff?
25592 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25593 node.parentNode.removeChild(node);
25596 //Roo.log(node.tagName);
25597 // remove - but keep children..
25598 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25599 //Roo.log('-- removed');
25600 while (node.childNodes.length) {
25601 var cn = node.childNodes[0];
25602 node.removeChild(cn);
25603 node.parentNode.insertBefore(cn, node);
25604 // move node to parent - and clean it..
25605 this.cleanWord(cn);
25607 node.parentNode.removeChild(node);
25608 /// no need to iterate chidlren = it's got none..
25609 //this.iterateChildren(node, this.cleanWord);
25613 if (node.className.length) {
25615 var cn = node.className.split(/\W+/);
25617 Roo.each(cn, function(cls) {
25618 if (cls.match(/Mso[a-zA-Z]+/)) {
25623 node.className = cna.length ? cna.join(' ') : '';
25625 node.removeAttribute("class");
25629 if (node.hasAttribute("lang")) {
25630 node.removeAttribute("lang");
25633 if (node.hasAttribute("style")) {
25635 var styles = node.getAttribute("style").split(";");
25637 Roo.each(styles, function(s) {
25638 if (!s.match(/:/)) {
25641 var kv = s.split(":");
25642 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25645 // what ever is left... we allow.
25648 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25649 if (!nstyle.length) {
25650 node.removeAttribute('style');
25653 this.iterateChildren(node, this.cleanWord);
25659 * iterateChildren of a Node, calling fn each time, using this as the scole..
25660 * @param {DomNode} node node to iterate children of.
25661 * @param {Function} fn method of this class to call on each item.
25663 iterateChildren : function(node, fn)
25665 if (!node.childNodes.length) {
25668 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25669 fn.call(this, node.childNodes[i])
25675 * cleanTableWidths.
25677 * Quite often pasting from word etc.. results in tables with column and widths.
25678 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25681 cleanTableWidths : function(node)
25686 this.cleanTableWidths(this.doc.body);
25691 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25694 Roo.log(node.tagName);
25695 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25696 this.iterateChildren(node, this.cleanTableWidths);
25699 if (node.hasAttribute('width')) {
25700 node.removeAttribute('width');
25704 if (node.hasAttribute("style")) {
25707 var styles = node.getAttribute("style").split(";");
25709 Roo.each(styles, function(s) {
25710 if (!s.match(/:/)) {
25713 var kv = s.split(":");
25714 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25717 // what ever is left... we allow.
25720 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25721 if (!nstyle.length) {
25722 node.removeAttribute('style');
25726 this.iterateChildren(node, this.cleanTableWidths);
25734 domToHTML : function(currentElement, depth, nopadtext) {
25736 depth = depth || 0;
25737 nopadtext = nopadtext || false;
25739 if (!currentElement) {
25740 return this.domToHTML(this.doc.body);
25743 //Roo.log(currentElement);
25745 var allText = false;
25746 var nodeName = currentElement.nodeName;
25747 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25749 if (nodeName == '#text') {
25751 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25756 if (nodeName != 'BODY') {
25759 // Prints the node tagName, such as <A>, <IMG>, etc
25762 for(i = 0; i < currentElement.attributes.length;i++) {
25764 var aname = currentElement.attributes.item(i).name;
25765 if (!currentElement.attributes.item(i).value.length) {
25768 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25771 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25780 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25783 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25788 // Traverse the tree
25790 var currentElementChild = currentElement.childNodes.item(i);
25791 var allText = true;
25792 var innerHTML = '';
25794 while (currentElementChild) {
25795 // Formatting code (indent the tree so it looks nice on the screen)
25796 var nopad = nopadtext;
25797 if (lastnode == 'SPAN') {
25801 if (currentElementChild.nodeName == '#text') {
25802 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25803 toadd = nopadtext ? toadd : toadd.trim();
25804 if (!nopad && toadd.length > 80) {
25805 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25807 innerHTML += toadd;
25810 currentElementChild = currentElement.childNodes.item(i);
25816 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25818 // Recursively traverse the tree structure of the child node
25819 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25820 lastnode = currentElementChild.nodeName;
25822 currentElementChild=currentElement.childNodes.item(i);
25828 // The remaining code is mostly for formatting the tree
25829 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25834 ret+= "</"+tagName+">";
25840 applyBlacklists : function()
25842 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25843 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25847 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25848 if (b.indexOf(tag) > -1) {
25851 this.white.push(tag);
25855 Roo.each(w, function(tag) {
25856 if (b.indexOf(tag) > -1) {
25859 if (this.white.indexOf(tag) > -1) {
25862 this.white.push(tag);
25867 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25868 if (w.indexOf(tag) > -1) {
25871 this.black.push(tag);
25875 Roo.each(b, function(tag) {
25876 if (w.indexOf(tag) > -1) {
25879 if (this.black.indexOf(tag) > -1) {
25882 this.black.push(tag);
25887 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25888 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25892 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25893 if (b.indexOf(tag) > -1) {
25896 this.cwhite.push(tag);
25900 Roo.each(w, function(tag) {
25901 if (b.indexOf(tag) > -1) {
25904 if (this.cwhite.indexOf(tag) > -1) {
25907 this.cwhite.push(tag);
25912 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25913 if (w.indexOf(tag) > -1) {
25916 this.cblack.push(tag);
25920 Roo.each(b, function(tag) {
25921 if (w.indexOf(tag) > -1) {
25924 if (this.cblack.indexOf(tag) > -1) {
25927 this.cblack.push(tag);
25932 setStylesheets : function(stylesheets)
25934 if(typeof(stylesheets) == 'string'){
25935 Roo.get(this.iframe.contentDocument.head).createChild({
25937 rel : 'stylesheet',
25946 Roo.each(stylesheets, function(s) {
25951 Roo.get(_this.iframe.contentDocument.head).createChild({
25953 rel : 'stylesheet',
25962 removeStylesheets : function()
25966 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25971 setStyle : function(style)
25973 Roo.get(this.iframe.contentDocument.head).createChild({
25982 // hide stuff that is not compatible
25996 * @event specialkey
26000 * @cfg {String} fieldClass @hide
26003 * @cfg {String} focusClass @hide
26006 * @cfg {String} autoCreate @hide
26009 * @cfg {String} inputType @hide
26012 * @cfg {String} invalidClass @hide
26015 * @cfg {String} invalidText @hide
26018 * @cfg {String} msgFx @hide
26021 * @cfg {String} validateOnBlur @hide
26025 Roo.HtmlEditorCore.white = [
26026 'area', 'br', 'img', 'input', 'hr', 'wbr',
26028 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26029 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26030 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26031 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26032 'table', 'ul', 'xmp',
26034 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26037 'dir', 'menu', 'ol', 'ul', 'dl',
26043 Roo.HtmlEditorCore.black = [
26044 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26046 'base', 'basefont', 'bgsound', 'blink', 'body',
26047 'frame', 'frameset', 'head', 'html', 'ilayer',
26048 'iframe', 'layer', 'link', 'meta', 'object',
26049 'script', 'style' ,'title', 'xml' // clean later..
26051 Roo.HtmlEditorCore.clean = [
26052 'script', 'style', 'title', 'xml'
26054 Roo.HtmlEditorCore.remove = [
26059 Roo.HtmlEditorCore.ablack = [
26063 Roo.HtmlEditorCore.aclean = [
26064 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26068 Roo.HtmlEditorCore.pwhite= [
26069 'http', 'https', 'mailto'
26072 // white listed style attributes.
26073 Roo.HtmlEditorCore.cwhite= [
26074 // 'text-align', /// default is to allow most things..
26080 // black listed style attributes.
26081 Roo.HtmlEditorCore.cblack= [
26082 // 'font-size' -- this can be set by the project
26086 Roo.HtmlEditorCore.swapCodes =[
26087 [ 8211, "–" ],
26088 [ 8212, "—" ],
26105 * @class Roo.bootstrap.HtmlEditor
26106 * @extends Roo.bootstrap.TextArea
26107 * Bootstrap HtmlEditor class
26110 * Create a new HtmlEditor
26111 * @param {Object} config The config object
26114 Roo.bootstrap.HtmlEditor = function(config){
26115 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26116 if (!this.toolbars) {
26117 this.toolbars = [];
26120 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26123 * @event initialize
26124 * Fires when the editor is fully initialized (including the iframe)
26125 * @param {HtmlEditor} this
26130 * Fires when the editor is first receives the focus. Any insertion must wait
26131 * until after this event.
26132 * @param {HtmlEditor} this
26136 * @event beforesync
26137 * Fires before the textarea is updated with content from the editor iframe. Return false
26138 * to cancel the sync.
26139 * @param {HtmlEditor} this
26140 * @param {String} html
26144 * @event beforepush
26145 * Fires before the iframe editor is updated with content from the textarea. Return false
26146 * to cancel the push.
26147 * @param {HtmlEditor} this
26148 * @param {String} html
26153 * Fires when the textarea is updated with content from the editor iframe.
26154 * @param {HtmlEditor} this
26155 * @param {String} html
26160 * Fires when the iframe editor is updated with content from the textarea.
26161 * @param {HtmlEditor} this
26162 * @param {String} html
26166 * @event editmodechange
26167 * Fires when the editor switches edit modes
26168 * @param {HtmlEditor} this
26169 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26171 editmodechange: true,
26173 * @event editorevent
26174 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26175 * @param {HtmlEditor} this
26179 * @event firstfocus
26180 * Fires when on first focus - needed by toolbars..
26181 * @param {HtmlEditor} this
26186 * Auto save the htmlEditor value as a file into Events
26187 * @param {HtmlEditor} this
26191 * @event savedpreview
26192 * preview the saved version of htmlEditor
26193 * @param {HtmlEditor} this
26200 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26204 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26209 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26214 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26219 * @cfg {Number} height (in pixels)
26223 * @cfg {Number} width (in pixels)
26228 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26231 stylesheets: false,
26236 // private properties
26237 validationEvent : false,
26239 initialized : false,
26242 onFocus : Roo.emptyFn,
26244 hideMode:'offsets',
26246 tbContainer : false,
26250 toolbarContainer :function() {
26251 return this.wrap.select('.x-html-editor-tb',true).first();
26255 * Protected method that will not generally be called directly. It
26256 * is called when the editor creates its toolbar. Override this method if you need to
26257 * add custom toolbar buttons.
26258 * @param {HtmlEditor} editor
26260 createToolbar : function(){
26261 Roo.log('renewing');
26262 Roo.log("create toolbars");
26264 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26265 this.toolbars[0].render(this.toolbarContainer());
26269 // if (!editor.toolbars || !editor.toolbars.length) {
26270 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26273 // for (var i =0 ; i < editor.toolbars.length;i++) {
26274 // editor.toolbars[i] = Roo.factory(
26275 // typeof(editor.toolbars[i]) == 'string' ?
26276 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26277 // Roo.bootstrap.HtmlEditor);
26278 // editor.toolbars[i].init(editor);
26284 onRender : function(ct, position)
26286 // Roo.log("Call onRender: " + this.xtype);
26288 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26290 this.wrap = this.inputEl().wrap({
26291 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26294 this.editorcore.onRender(ct, position);
26296 if (this.resizable) {
26297 this.resizeEl = new Roo.Resizable(this.wrap, {
26301 minHeight : this.height,
26302 height: this.height,
26303 handles : this.resizable,
26306 resize : function(r, w, h) {
26307 _t.onResize(w,h); // -something
26313 this.createToolbar(this);
26316 if(!this.width && this.resizable){
26317 this.setSize(this.wrap.getSize());
26319 if (this.resizeEl) {
26320 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26321 // should trigger onReize..
26327 onResize : function(w, h)
26329 Roo.log('resize: ' +w + ',' + h );
26330 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26334 if(this.inputEl() ){
26335 if(typeof w == 'number'){
26336 var aw = w - this.wrap.getFrameWidth('lr');
26337 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26340 if(typeof h == 'number'){
26341 var tbh = -11; // fixme it needs to tool bar size!
26342 for (var i =0; i < this.toolbars.length;i++) {
26343 // fixme - ask toolbars for heights?
26344 tbh += this.toolbars[i].el.getHeight();
26345 //if (this.toolbars[i].footer) {
26346 // tbh += this.toolbars[i].footer.el.getHeight();
26354 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26355 ah -= 5; // knock a few pixes off for look..
26356 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26360 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26361 this.editorcore.onResize(ew,eh);
26366 * Toggles the editor between standard and source edit mode.
26367 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26369 toggleSourceEdit : function(sourceEditMode)
26371 this.editorcore.toggleSourceEdit(sourceEditMode);
26373 if(this.editorcore.sourceEditMode){
26374 Roo.log('editor - showing textarea');
26377 // Roo.log(this.syncValue());
26379 this.inputEl().removeClass(['hide', 'x-hidden']);
26380 this.inputEl().dom.removeAttribute('tabIndex');
26381 this.inputEl().focus();
26383 Roo.log('editor - hiding textarea');
26385 // Roo.log(this.pushValue());
26388 this.inputEl().addClass(['hide', 'x-hidden']);
26389 this.inputEl().dom.setAttribute('tabIndex', -1);
26390 //this.deferFocus();
26393 if(this.resizable){
26394 this.setSize(this.wrap.getSize());
26397 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26400 // private (for BoxComponent)
26401 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26403 // private (for BoxComponent)
26404 getResizeEl : function(){
26408 // private (for BoxComponent)
26409 getPositionEl : function(){
26414 initEvents : function(){
26415 this.originalValue = this.getValue();
26419 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26422 // markInvalid : Roo.emptyFn,
26424 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26427 // clearInvalid : Roo.emptyFn,
26429 setValue : function(v){
26430 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26431 this.editorcore.pushValue();
26436 deferFocus : function(){
26437 this.focus.defer(10, this);
26441 focus : function(){
26442 this.editorcore.focus();
26448 onDestroy : function(){
26454 for (var i =0; i < this.toolbars.length;i++) {
26455 // fixme - ask toolbars for heights?
26456 this.toolbars[i].onDestroy();
26459 this.wrap.dom.innerHTML = '';
26460 this.wrap.remove();
26465 onFirstFocus : function(){
26466 //Roo.log("onFirstFocus");
26467 this.editorcore.onFirstFocus();
26468 for (var i =0; i < this.toolbars.length;i++) {
26469 this.toolbars[i].onFirstFocus();
26475 syncValue : function()
26477 this.editorcore.syncValue();
26480 pushValue : function()
26482 this.editorcore.pushValue();
26486 // hide stuff that is not compatible
26500 * @event specialkey
26504 * @cfg {String} fieldClass @hide
26507 * @cfg {String} focusClass @hide
26510 * @cfg {String} autoCreate @hide
26513 * @cfg {String} inputType @hide
26517 * @cfg {String} invalidText @hide
26520 * @cfg {String} msgFx @hide
26523 * @cfg {String} validateOnBlur @hide
26532 Roo.namespace('Roo.bootstrap.htmleditor');
26534 * @class Roo.bootstrap.HtmlEditorToolbar1
26540 new Roo.bootstrap.HtmlEditor({
26543 new Roo.bootstrap.HtmlEditorToolbar1({
26544 disable : { fonts: 1 , format: 1, ..., ... , ...],
26550 * @cfg {Object} disable List of elements to disable..
26551 * @cfg {Array} btns List of additional buttons.
26555 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26558 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26561 Roo.apply(this, config);
26563 // default disabled, based on 'good practice'..
26564 this.disable = this.disable || {};
26565 Roo.applyIf(this.disable, {
26568 specialElements : true
26570 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26572 this.editor = config.editor;
26573 this.editorcore = config.editor.editorcore;
26575 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26577 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26578 // dont call parent... till later.
26580 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26585 editorcore : false,
26590 "h1","h2","h3","h4","h5","h6",
26592 "abbr", "acronym", "address", "cite", "samp", "var",
26596 onRender : function(ct, position)
26598 // Roo.log("Call onRender: " + this.xtype);
26600 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26602 this.el.dom.style.marginBottom = '0';
26604 var editorcore = this.editorcore;
26605 var editor= this.editor;
26608 var btn = function(id,cmd , toggle, handler, html){
26610 var event = toggle ? 'toggle' : 'click';
26615 xns: Roo.bootstrap,
26619 enableToggle:toggle !== false,
26621 pressed : toggle ? false : null,
26624 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26625 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26631 // var cb_box = function...
26636 xns: Roo.bootstrap,
26641 xns: Roo.bootstrap,
26645 Roo.each(this.formats, function(f) {
26646 style.menu.items.push({
26648 xns: Roo.bootstrap,
26649 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26654 editorcore.insertTag(this.tagname);
26661 children.push(style);
26663 btn('bold',false,true);
26664 btn('italic',false,true);
26665 btn('align-left', 'justifyleft',true);
26666 btn('align-center', 'justifycenter',true);
26667 btn('align-right' , 'justifyright',true);
26668 btn('link', false, false, function(btn) {
26669 //Roo.log("create link?");
26670 var url = prompt(this.createLinkText, this.defaultLinkValue);
26671 if(url && url != 'http:/'+'/'){
26672 this.editorcore.relayCmd('createlink', url);
26675 btn('list','insertunorderedlist',true);
26676 btn('pencil', false,true, function(btn){
26678 this.toggleSourceEdit(btn.pressed);
26681 if (this.editor.btns.length > 0) {
26682 for (var i = 0; i<this.editor.btns.length; i++) {
26683 children.push(this.editor.btns[i]);
26691 xns: Roo.bootstrap,
26696 xns: Roo.bootstrap,
26701 cog.menu.items.push({
26703 xns: Roo.bootstrap,
26704 html : Clean styles,
26709 editorcore.insertTag(this.tagname);
26718 this.xtype = 'NavSimplebar';
26720 for(var i=0;i< children.length;i++) {
26722 this.buttons.add(this.addxtypeChild(children[i]));
26726 editor.on('editorevent', this.updateToolbar, this);
26728 onBtnClick : function(id)
26730 this.editorcore.relayCmd(id);
26731 this.editorcore.focus();
26735 * Protected method that will not generally be called directly. It triggers
26736 * a toolbar update by reading the markup state of the current selection in the editor.
26738 updateToolbar: function(){
26740 if(!this.editorcore.activated){
26741 this.editor.onFirstFocus(); // is this neeed?
26745 var btns = this.buttons;
26746 var doc = this.editorcore.doc;
26747 btns.get('bold').setActive(doc.queryCommandState('bold'));
26748 btns.get('italic').setActive(doc.queryCommandState('italic'));
26749 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26751 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26752 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26753 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26755 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26756 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26759 var ans = this.editorcore.getAllAncestors();
26760 if (this.formatCombo) {
26763 var store = this.formatCombo.store;
26764 this.formatCombo.setValue("");
26765 for (var i =0; i < ans.length;i++) {
26766 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26768 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26776 // hides menus... - so this cant be on a menu...
26777 Roo.bootstrap.MenuMgr.hideAll();
26779 Roo.bootstrap.MenuMgr.hideAll();
26780 //this.editorsyncValue();
26782 onFirstFocus: function() {
26783 this.buttons.each(function(item){
26787 toggleSourceEdit : function(sourceEditMode){
26790 if(sourceEditMode){
26791 Roo.log("disabling buttons");
26792 this.buttons.each( function(item){
26793 if(item.cmd != 'pencil'){
26799 Roo.log("enabling buttons");
26800 if(this.editorcore.initialized){
26801 this.buttons.each( function(item){
26807 Roo.log("calling toggole on editor");
26808 // tell the editor that it's been pressed..
26809 this.editor.toggleSourceEdit(sourceEditMode);
26823 * @class Roo.bootstrap.Markdown
26824 * @extends Roo.bootstrap.TextArea
26825 * Bootstrap Showdown editable area
26826 * @cfg {string} content
26829 * Create a new Showdown
26832 Roo.bootstrap.Markdown = function(config){
26833 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26837 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26841 initEvents : function()
26844 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26845 this.markdownEl = this.el.createChild({
26846 cls : 'roo-markdown-area'
26848 this.inputEl().addClass('d-none');
26849 if (this.getValue() == '') {
26850 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26853 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26855 this.markdownEl.on('click', this.toggleTextEdit, this);
26856 this.on('blur', this.toggleTextEdit, this);
26857 this.on('specialkey', this.resizeTextArea, this);
26860 toggleTextEdit : function()
26862 var sh = this.markdownEl.getHeight();
26863 this.inputEl().addClass('d-none');
26864 this.markdownEl.addClass('d-none');
26865 if (!this.editing) {
26867 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26868 this.inputEl().removeClass('d-none');
26869 this.inputEl().focus();
26870 this.editing = true;
26873 // show showdown...
26874 this.updateMarkdown();
26875 this.markdownEl.removeClass('d-none');
26876 this.editing = false;
26879 updateMarkdown : function()
26881 if (this.getValue() == '') {
26882 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26886 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26889 resizeTextArea: function () {
26892 Roo.log([sh, this.getValue().split("\n").length * 30]);
26893 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26895 setValue : function(val)
26897 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26898 if (!this.editing) {
26899 this.updateMarkdown();
26905 if (!this.editing) {
26906 this.toggleTextEdit();
26914 * @class Roo.bootstrap.Table.AbstractSelectionModel
26915 * @extends Roo.util.Observable
26916 * Abstract base class for grid SelectionModels. It provides the interface that should be
26917 * implemented by descendant classes. This class should not be directly instantiated.
26920 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26921 this.locked = false;
26922 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26926 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26927 /** @ignore Called by the grid automatically. Do not call directly. */
26928 init : function(grid){
26934 * Locks the selections.
26937 this.locked = true;
26941 * Unlocks the selections.
26943 unlock : function(){
26944 this.locked = false;
26948 * Returns true if the selections are locked.
26949 * @return {Boolean}
26951 isLocked : function(){
26952 return this.locked;
26956 initEvents : function ()
26962 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26963 * @class Roo.bootstrap.Table.RowSelectionModel
26964 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26965 * It supports multiple selections and keyboard selection/navigation.
26967 * @param {Object} config
26970 Roo.bootstrap.Table.RowSelectionModel = function(config){
26971 Roo.apply(this, config);
26972 this.selections = new Roo.util.MixedCollection(false, function(o){
26977 this.lastActive = false;
26981 * @event selectionchange
26982 * Fires when the selection changes
26983 * @param {SelectionModel} this
26985 "selectionchange" : true,
26987 * @event afterselectionchange
26988 * Fires after the selection changes (eg. by key press or clicking)
26989 * @param {SelectionModel} this
26991 "afterselectionchange" : true,
26993 * @event beforerowselect
26994 * Fires when a row is selected being selected, return false to cancel.
26995 * @param {SelectionModel} this
26996 * @param {Number} rowIndex The selected index
26997 * @param {Boolean} keepExisting False if other selections will be cleared
26999 "beforerowselect" : true,
27002 * Fires when a row is selected.
27003 * @param {SelectionModel} this
27004 * @param {Number} rowIndex The selected index
27005 * @param {Roo.data.Record} r The record
27007 "rowselect" : true,
27009 * @event rowdeselect
27010 * Fires when a row is deselected.
27011 * @param {SelectionModel} this
27012 * @param {Number} rowIndex The selected index
27014 "rowdeselect" : true
27016 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27017 this.locked = false;
27020 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27022 * @cfg {Boolean} singleSelect
27023 * True to allow selection of only one row at a time (defaults to false)
27025 singleSelect : false,
27028 initEvents : function()
27031 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27032 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27033 //}else{ // allow click to work like normal
27034 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27036 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27037 this.grid.on("rowclick", this.handleMouseDown, this);
27039 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27040 "up" : function(e){
27042 this.selectPrevious(e.shiftKey);
27043 }else if(this.last !== false && this.lastActive !== false){
27044 var last = this.last;
27045 this.selectRange(this.last, this.lastActive-1);
27046 this.grid.getView().focusRow(this.lastActive);
27047 if(last !== false){
27051 this.selectFirstRow();
27053 this.fireEvent("afterselectionchange", this);
27055 "down" : function(e){
27057 this.selectNext(e.shiftKey);
27058 }else if(this.last !== false && this.lastActive !== false){
27059 var last = this.last;
27060 this.selectRange(this.last, this.lastActive+1);
27061 this.grid.getView().focusRow(this.lastActive);
27062 if(last !== false){
27066 this.selectFirstRow();
27068 this.fireEvent("afterselectionchange", this);
27072 this.grid.store.on('load', function(){
27073 this.selections.clear();
27076 var view = this.grid.view;
27077 view.on("refresh", this.onRefresh, this);
27078 view.on("rowupdated", this.onRowUpdated, this);
27079 view.on("rowremoved", this.onRemove, this);
27084 onRefresh : function()
27086 var ds = this.grid.store, i, v = this.grid.view;
27087 var s = this.selections;
27088 s.each(function(r){
27089 if((i = ds.indexOfId(r.id)) != -1){
27098 onRemove : function(v, index, r){
27099 this.selections.remove(r);
27103 onRowUpdated : function(v, index, r){
27104 if(this.isSelected(r)){
27105 v.onRowSelect(index);
27111 * @param {Array} records The records to select
27112 * @param {Boolean} keepExisting (optional) True to keep existing selections
27114 selectRecords : function(records, keepExisting)
27117 this.clearSelections();
27119 var ds = this.grid.store;
27120 for(var i = 0, len = records.length; i < len; i++){
27121 this.selectRow(ds.indexOf(records[i]), true);
27126 * Gets the number of selected rows.
27129 getCount : function(){
27130 return this.selections.length;
27134 * Selects the first row in the grid.
27136 selectFirstRow : function(){
27141 * Select the last row.
27142 * @param {Boolean} keepExisting (optional) True to keep existing selections
27144 selectLastRow : function(keepExisting){
27145 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27146 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27150 * Selects the row immediately following the last selected row.
27151 * @param {Boolean} keepExisting (optional) True to keep existing selections
27153 selectNext : function(keepExisting)
27155 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27156 this.selectRow(this.last+1, keepExisting);
27157 this.grid.getView().focusRow(this.last);
27162 * Selects the row that precedes the last selected row.
27163 * @param {Boolean} keepExisting (optional) True to keep existing selections
27165 selectPrevious : function(keepExisting){
27167 this.selectRow(this.last-1, keepExisting);
27168 this.grid.getView().focusRow(this.last);
27173 * Returns the selected records
27174 * @return {Array} Array of selected records
27176 getSelections : function(){
27177 return [].concat(this.selections.items);
27181 * Returns the first selected record.
27184 getSelected : function(){
27185 return this.selections.itemAt(0);
27190 * Clears all selections.
27192 clearSelections : function(fast)
27198 var ds = this.grid.store;
27199 var s = this.selections;
27200 s.each(function(r){
27201 this.deselectRow(ds.indexOfId(r.id));
27205 this.selections.clear();
27212 * Selects all rows.
27214 selectAll : function(){
27218 this.selections.clear();
27219 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27220 this.selectRow(i, true);
27225 * Returns True if there is a selection.
27226 * @return {Boolean}
27228 hasSelection : function(){
27229 return this.selections.length > 0;
27233 * Returns True if the specified row is selected.
27234 * @param {Number/Record} record The record or index of the record to check
27235 * @return {Boolean}
27237 isSelected : function(index){
27238 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27239 return (r && this.selections.key(r.id) ? true : false);
27243 * Returns True if the specified record id is selected.
27244 * @param {String} id The id of record to check
27245 * @return {Boolean}
27247 isIdSelected : function(id){
27248 return (this.selections.key(id) ? true : false);
27253 handleMouseDBClick : function(e, t){
27257 handleMouseDown : function(e, t)
27259 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27260 if(this.isLocked() || rowIndex < 0 ){
27263 if(e.shiftKey && this.last !== false){
27264 var last = this.last;
27265 this.selectRange(last, rowIndex, e.ctrlKey);
27266 this.last = last; // reset the last
27270 var isSelected = this.isSelected(rowIndex);
27271 //Roo.log("select row:" + rowIndex);
27273 this.deselectRow(rowIndex);
27275 this.selectRow(rowIndex, true);
27279 if(e.button !== 0 && isSelected){
27280 alert('rowIndex 2: ' + rowIndex);
27281 view.focusRow(rowIndex);
27282 }else if(e.ctrlKey && isSelected){
27283 this.deselectRow(rowIndex);
27284 }else if(!isSelected){
27285 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27286 view.focusRow(rowIndex);
27290 this.fireEvent("afterselectionchange", this);
27293 handleDragableRowClick : function(grid, rowIndex, e)
27295 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27296 this.selectRow(rowIndex, false);
27297 grid.view.focusRow(rowIndex);
27298 this.fireEvent("afterselectionchange", this);
27303 * Selects multiple rows.
27304 * @param {Array} rows Array of the indexes of the row to select
27305 * @param {Boolean} keepExisting (optional) True to keep existing selections
27307 selectRows : function(rows, keepExisting){
27309 this.clearSelections();
27311 for(var i = 0, len = rows.length; i < len; i++){
27312 this.selectRow(rows[i], true);
27317 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27318 * @param {Number} startRow The index of the first row in the range
27319 * @param {Number} endRow The index of the last row in the range
27320 * @param {Boolean} keepExisting (optional) True to retain existing selections
27322 selectRange : function(startRow, endRow, keepExisting){
27327 this.clearSelections();
27329 if(startRow <= endRow){
27330 for(var i = startRow; i <= endRow; i++){
27331 this.selectRow(i, true);
27334 for(var i = startRow; i >= endRow; i--){
27335 this.selectRow(i, true);
27341 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27342 * @param {Number} startRow The index of the first row in the range
27343 * @param {Number} endRow The index of the last row in the range
27345 deselectRange : function(startRow, endRow, preventViewNotify){
27349 for(var i = startRow; i <= endRow; i++){
27350 this.deselectRow(i, preventViewNotify);
27356 * @param {Number} row The index of the row to select
27357 * @param {Boolean} keepExisting (optional) True to keep existing selections
27359 selectRow : function(index, keepExisting, preventViewNotify)
27361 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27364 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27365 if(!keepExisting || this.singleSelect){
27366 this.clearSelections();
27369 var r = this.grid.store.getAt(index);
27370 //console.log('selectRow - record id :' + r.id);
27372 this.selections.add(r);
27373 this.last = this.lastActive = index;
27374 if(!preventViewNotify){
27375 var proxy = new Roo.Element(
27376 this.grid.getRowDom(index)
27378 proxy.addClass('bg-info info');
27380 this.fireEvent("rowselect", this, index, r);
27381 this.fireEvent("selectionchange", this);
27387 * @param {Number} row The index of the row to deselect
27389 deselectRow : function(index, preventViewNotify)
27394 if(this.last == index){
27397 if(this.lastActive == index){
27398 this.lastActive = false;
27401 var r = this.grid.store.getAt(index);
27406 this.selections.remove(r);
27407 //.console.log('deselectRow - record id :' + r.id);
27408 if(!preventViewNotify){
27410 var proxy = new Roo.Element(
27411 this.grid.getRowDom(index)
27413 proxy.removeClass('bg-info info');
27415 this.fireEvent("rowdeselect", this, index);
27416 this.fireEvent("selectionchange", this);
27420 restoreLast : function(){
27422 this.last = this._last;
27427 acceptsNav : function(row, col, cm){
27428 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27432 onEditorKey : function(field, e){
27433 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27438 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27440 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27442 }else if(k == e.ENTER && !e.ctrlKey){
27446 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27448 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27450 }else if(k == e.ESC){
27454 g.startEditing(newCell[0], newCell[1]);
27460 * Ext JS Library 1.1.1
27461 * Copyright(c) 2006-2007, Ext JS, LLC.
27463 * Originally Released Under LGPL - original licence link has changed is not relivant.
27466 * <script type="text/javascript">
27470 * @class Roo.bootstrap.PagingToolbar
27471 * @extends Roo.bootstrap.NavSimplebar
27472 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27474 * Create a new PagingToolbar
27475 * @param {Object} config The config object
27476 * @param {Roo.data.Store} store
27478 Roo.bootstrap.PagingToolbar = function(config)
27480 // old args format still supported... - xtype is prefered..
27481 // created from xtype...
27483 this.ds = config.dataSource;
27485 if (config.store && !this.ds) {
27486 this.store= Roo.factory(config.store, Roo.data);
27487 this.ds = this.store;
27488 this.ds.xmodule = this.xmodule || false;
27491 this.toolbarItems = [];
27492 if (config.items) {
27493 this.toolbarItems = config.items;
27496 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27501 this.bind(this.ds);
27504 if (Roo.bootstrap.version == 4) {
27505 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27507 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27512 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27514 * @cfg {Roo.data.Store} dataSource
27515 * The underlying data store providing the paged data
27518 * @cfg {String/HTMLElement/Element} container
27519 * container The id or element that will contain the toolbar
27522 * @cfg {Boolean} displayInfo
27523 * True to display the displayMsg (defaults to false)
27526 * @cfg {Number} pageSize
27527 * The number of records to display per page (defaults to 20)
27531 * @cfg {String} displayMsg
27532 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27534 displayMsg : 'Displaying {0} - {1} of {2}',
27536 * @cfg {String} emptyMsg
27537 * The message to display when no records are found (defaults to "No data to display")
27539 emptyMsg : 'No data to display',
27541 * Customizable piece of the default paging text (defaults to "Page")
27544 beforePageText : "Page",
27546 * Customizable piece of the default paging text (defaults to "of %0")
27549 afterPageText : "of {0}",
27551 * Customizable piece of the default paging text (defaults to "First Page")
27554 firstText : "First Page",
27556 * Customizable piece of the default paging text (defaults to "Previous Page")
27559 prevText : "Previous Page",
27561 * Customizable piece of the default paging text (defaults to "Next Page")
27564 nextText : "Next Page",
27566 * Customizable piece of the default paging text (defaults to "Last Page")
27569 lastText : "Last Page",
27571 * Customizable piece of the default paging text (defaults to "Refresh")
27574 refreshText : "Refresh",
27578 onRender : function(ct, position)
27580 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27581 this.navgroup.parentId = this.id;
27582 this.navgroup.onRender(this.el, null);
27583 // add the buttons to the navgroup
27585 if(this.displayInfo){
27586 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27587 this.displayEl = this.el.select('.x-paging-info', true).first();
27588 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27589 // this.displayEl = navel.el.select('span',true).first();
27595 Roo.each(_this.buttons, function(e){ // this might need to use render????
27596 Roo.factory(e).render(_this.el);
27600 Roo.each(_this.toolbarItems, function(e) {
27601 _this.navgroup.addItem(e);
27605 this.first = this.navgroup.addItem({
27606 tooltip: this.firstText,
27607 cls: "prev btn-outline-secondary",
27608 html : ' <i class="fa fa-step-backward"></i>',
27610 preventDefault: true,
27611 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27614 this.prev = this.navgroup.addItem({
27615 tooltip: this.prevText,
27616 cls: "prev btn-outline-secondary",
27617 html : ' <i class="fa fa-backward"></i>',
27619 preventDefault: true,
27620 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27622 //this.addSeparator();
27625 var field = this.navgroup.addItem( {
27627 cls : 'x-paging-position btn-outline-secondary',
27629 html : this.beforePageText +
27630 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27631 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27634 this.field = field.el.select('input', true).first();
27635 this.field.on("keydown", this.onPagingKeydown, this);
27636 this.field.on("focus", function(){this.dom.select();});
27639 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27640 //this.field.setHeight(18);
27641 //this.addSeparator();
27642 this.next = this.navgroup.addItem({
27643 tooltip: this.nextText,
27644 cls: "next btn-outline-secondary",
27645 html : ' <i class="fa fa-forward"></i>',
27647 preventDefault: true,
27648 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27650 this.last = this.navgroup.addItem({
27651 tooltip: this.lastText,
27652 html : ' <i class="fa fa-step-forward"></i>',
27653 cls: "next btn-outline-secondary",
27655 preventDefault: true,
27656 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27658 //this.addSeparator();
27659 this.loading = this.navgroup.addItem({
27660 tooltip: this.refreshText,
27661 cls: "btn-outline-secondary",
27662 html : ' <i class="fa fa-refresh"></i>',
27663 preventDefault: true,
27664 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27670 updateInfo : function(){
27671 if(this.displayEl){
27672 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27673 var msg = count == 0 ?
27677 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27679 this.displayEl.update(msg);
27684 onLoad : function(ds, r, o)
27686 this.cursor = o.params && o.params.start ? o.params.start : 0;
27688 var d = this.getPageData(),
27693 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27694 this.field.dom.value = ap;
27695 this.first.setDisabled(ap == 1);
27696 this.prev.setDisabled(ap == 1);
27697 this.next.setDisabled(ap == ps);
27698 this.last.setDisabled(ap == ps);
27699 this.loading.enable();
27704 getPageData : function(){
27705 var total = this.ds.getTotalCount();
27708 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27709 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27714 onLoadError : function(){
27715 this.loading.enable();
27719 onPagingKeydown : function(e){
27720 var k = e.getKey();
27721 var d = this.getPageData();
27723 var v = this.field.dom.value, pageNum;
27724 if(!v || isNaN(pageNum = parseInt(v, 10))){
27725 this.field.dom.value = d.activePage;
27728 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27729 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27732 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))
27734 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27735 this.field.dom.value = pageNum;
27736 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27739 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27741 var v = this.field.dom.value, pageNum;
27742 var increment = (e.shiftKey) ? 10 : 1;
27743 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27746 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27747 this.field.dom.value = d.activePage;
27750 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27752 this.field.dom.value = parseInt(v, 10) + increment;
27753 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27754 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27761 beforeLoad : function(){
27763 this.loading.disable();
27768 onClick : function(which){
27777 ds.load({params:{start: 0, limit: this.pageSize}});
27780 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27783 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27786 var total = ds.getTotalCount();
27787 var extra = total % this.pageSize;
27788 var lastStart = extra ? (total - extra) : total-this.pageSize;
27789 ds.load({params:{start: lastStart, limit: this.pageSize}});
27792 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27798 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27799 * @param {Roo.data.Store} store The data store to unbind
27801 unbind : function(ds){
27802 ds.un("beforeload", this.beforeLoad, this);
27803 ds.un("load", this.onLoad, this);
27804 ds.un("loadexception", this.onLoadError, this);
27805 ds.un("remove", this.updateInfo, this);
27806 ds.un("add", this.updateInfo, this);
27807 this.ds = undefined;
27811 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27812 * @param {Roo.data.Store} store The data store to bind
27814 bind : function(ds){
27815 ds.on("beforeload", this.beforeLoad, this);
27816 ds.on("load", this.onLoad, this);
27817 ds.on("loadexception", this.onLoadError, this);
27818 ds.on("remove", this.updateInfo, this);
27819 ds.on("add", this.updateInfo, this);
27830 * @class Roo.bootstrap.MessageBar
27831 * @extends Roo.bootstrap.Component
27832 * Bootstrap MessageBar class
27833 * @cfg {String} html contents of the MessageBar
27834 * @cfg {String} weight (info | success | warning | danger) default info
27835 * @cfg {String} beforeClass insert the bar before the given class
27836 * @cfg {Boolean} closable (true | false) default false
27837 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27840 * Create a new Element
27841 * @param {Object} config The config object
27844 Roo.bootstrap.MessageBar = function(config){
27845 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27848 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27854 beforeClass: 'bootstrap-sticky-wrap',
27856 getAutoCreate : function(){
27860 cls: 'alert alert-dismissable alert-' + this.weight,
27865 html: this.html || ''
27871 cfg.cls += ' alert-messages-fixed';
27885 onRender : function(ct, position)
27887 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27890 var cfg = Roo.apply({}, this.getAutoCreate());
27894 cfg.cls += ' ' + this.cls;
27897 cfg.style = this.style;
27899 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27901 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27904 this.el.select('>button.close').on('click', this.hide, this);
27910 if (!this.rendered) {
27916 this.fireEvent('show', this);
27922 if (!this.rendered) {
27928 this.fireEvent('hide', this);
27931 update : function()
27933 // var e = this.el.dom.firstChild;
27935 // if(this.closable){
27936 // e = e.nextSibling;
27939 // e.data = this.html || '';
27941 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27957 * @class Roo.bootstrap.Graph
27958 * @extends Roo.bootstrap.Component
27959 * Bootstrap Graph class
27963 @cfg {String} graphtype bar | vbar | pie
27964 @cfg {number} g_x coodinator | centre x (pie)
27965 @cfg {number} g_y coodinator | centre y (pie)
27966 @cfg {number} g_r radius (pie)
27967 @cfg {number} g_height height of the chart (respected by all elements in the set)
27968 @cfg {number} g_width width of the chart (respected by all elements in the set)
27969 @cfg {Object} title The title of the chart
27972 -opts (object) options for the chart
27974 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27975 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27977 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.
27978 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27980 o stretch (boolean)
27982 -opts (object) options for the pie
27985 o startAngle (number)
27986 o endAngle (number)
27990 * Create a new Input
27991 * @param {Object} config The config object
27994 Roo.bootstrap.Graph = function(config){
27995 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28001 * The img click event for the img.
28002 * @param {Roo.EventObject} e
28008 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28019 //g_colors: this.colors,
28026 getAutoCreate : function(){
28037 onRender : function(ct,position){
28040 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28042 if (typeof(Raphael) == 'undefined') {
28043 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28047 this.raphael = Raphael(this.el.dom);
28049 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28050 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28051 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28052 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28054 r.text(160, 10, "Single Series Chart").attr(txtattr);
28055 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28056 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28057 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28059 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28060 r.barchart(330, 10, 300, 220, data1);
28061 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28062 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28065 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28066 // r.barchart(30, 30, 560, 250, xdata, {
28067 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28068 // axis : "0 0 1 1",
28069 // axisxlabels : xdata
28070 // //yvalues : cols,
28073 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28075 // this.load(null,xdata,{
28076 // axis : "0 0 1 1",
28077 // axisxlabels : xdata
28082 load : function(graphtype,xdata,opts)
28084 this.raphael.clear();
28086 graphtype = this.graphtype;
28091 var r = this.raphael,
28092 fin = function () {
28093 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28095 fout = function () {
28096 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28098 pfin = function() {
28099 this.sector.stop();
28100 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28103 this.label[0].stop();
28104 this.label[0].attr({ r: 7.5 });
28105 this.label[1].attr({ "font-weight": 800 });
28108 pfout = function() {
28109 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28112 this.label[0].animate({ r: 5 }, 500, "bounce");
28113 this.label[1].attr({ "font-weight": 400 });
28119 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28122 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28125 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28126 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28128 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28135 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28140 setTitle: function(o)
28145 initEvents: function() {
28148 this.el.on('click', this.onClick, this);
28152 onClick : function(e)
28154 Roo.log('img onclick');
28155 this.fireEvent('click', this, e);
28167 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28170 * @class Roo.bootstrap.dash.NumberBox
28171 * @extends Roo.bootstrap.Component
28172 * Bootstrap NumberBox class
28173 * @cfg {String} headline Box headline
28174 * @cfg {String} content Box content
28175 * @cfg {String} icon Box icon
28176 * @cfg {String} footer Footer text
28177 * @cfg {String} fhref Footer href
28180 * Create a new NumberBox
28181 * @param {Object} config The config object
28185 Roo.bootstrap.dash.NumberBox = function(config){
28186 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28190 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28199 getAutoCreate : function(){
28203 cls : 'small-box ',
28211 cls : 'roo-headline',
28212 html : this.headline
28216 cls : 'roo-content',
28217 html : this.content
28231 cls : 'ion ' + this.icon
28240 cls : 'small-box-footer',
28241 href : this.fhref || '#',
28245 cfg.cn.push(footer);
28252 onRender : function(ct,position){
28253 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28260 setHeadline: function (value)
28262 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28265 setFooter: function (value, href)
28267 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28270 this.el.select('a.small-box-footer',true).first().attr('href', href);
28275 setContent: function (value)
28277 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28280 initEvents: function()
28294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28297 * @class Roo.bootstrap.dash.TabBox
28298 * @extends Roo.bootstrap.Component
28299 * Bootstrap TabBox class
28300 * @cfg {String} title Title of the TabBox
28301 * @cfg {String} icon Icon of the TabBox
28302 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28303 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28306 * Create a new TabBox
28307 * @param {Object} config The config object
28311 Roo.bootstrap.dash.TabBox = function(config){
28312 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28317 * When a pane is added
28318 * @param {Roo.bootstrap.dash.TabPane} pane
28322 * @event activatepane
28323 * When a pane is activated
28324 * @param {Roo.bootstrap.dash.TabPane} pane
28326 "activatepane" : true
28334 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28339 tabScrollable : false,
28341 getChildContainer : function()
28343 return this.el.select('.tab-content', true).first();
28346 getAutoCreate : function(){
28350 cls: 'pull-left header',
28358 cls: 'fa ' + this.icon
28364 cls: 'nav nav-tabs pull-right',
28370 if(this.tabScrollable){
28377 cls: 'nav nav-tabs pull-right',
28388 cls: 'nav-tabs-custom',
28393 cls: 'tab-content no-padding',
28401 initEvents : function()
28403 //Roo.log('add add pane handler');
28404 this.on('addpane', this.onAddPane, this);
28407 * Updates the box title
28408 * @param {String} html to set the title to.
28410 setTitle : function(value)
28412 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28414 onAddPane : function(pane)
28416 this.panes.push(pane);
28417 //Roo.log('addpane');
28419 // tabs are rendere left to right..
28420 if(!this.showtabs){
28424 var ctr = this.el.select('.nav-tabs', true).first();
28427 var existing = ctr.select('.nav-tab',true);
28428 var qty = existing.getCount();;
28431 var tab = ctr.createChild({
28433 cls : 'nav-tab' + (qty ? '' : ' active'),
28441 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28444 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28446 pane.el.addClass('active');
28451 onTabClick : function(ev,un,ob,pane)
28453 //Roo.log('tab - prev default');
28454 ev.preventDefault();
28457 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28458 pane.tab.addClass('active');
28459 //Roo.log(pane.title);
28460 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28461 // technically we should have a deactivate event.. but maybe add later.
28462 // and it should not de-activate the selected tab...
28463 this.fireEvent('activatepane', pane);
28464 pane.el.addClass('active');
28465 pane.fireEvent('activate');
28470 getActivePane : function()
28473 Roo.each(this.panes, function(p) {
28474 if(p.el.hasClass('active')){
28495 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28497 * @class Roo.bootstrap.TabPane
28498 * @extends Roo.bootstrap.Component
28499 * Bootstrap TabPane class
28500 * @cfg {Boolean} active (false | true) Default false
28501 * @cfg {String} title title of panel
28505 * Create a new TabPane
28506 * @param {Object} config The config object
28509 Roo.bootstrap.dash.TabPane = function(config){
28510 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28516 * When a pane is activated
28517 * @param {Roo.bootstrap.dash.TabPane} pane
28524 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28529 // the tabBox that this is attached to.
28532 getAutoCreate : function()
28540 cfg.cls += ' active';
28545 initEvents : function()
28547 //Roo.log('trigger add pane handler');
28548 this.parent().fireEvent('addpane', this)
28552 * Updates the tab title
28553 * @param {String} html to set the title to.
28555 setTitle: function(str)
28561 this.tab.select('a', true).first().dom.innerHTML = str;
28578 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28581 * @class Roo.bootstrap.menu.Menu
28582 * @extends Roo.bootstrap.Component
28583 * Bootstrap Menu class - container for Menu
28584 * @cfg {String} html Text of the menu
28585 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28586 * @cfg {String} icon Font awesome icon
28587 * @cfg {String} pos Menu align to (top | bottom) default bottom
28591 * Create a new Menu
28592 * @param {Object} config The config object
28596 Roo.bootstrap.menu.Menu = function(config){
28597 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28601 * @event beforeshow
28602 * Fires before this menu is displayed
28603 * @param {Roo.bootstrap.menu.Menu} this
28607 * @event beforehide
28608 * Fires before this menu is hidden
28609 * @param {Roo.bootstrap.menu.Menu} this
28614 * Fires after this menu is displayed
28615 * @param {Roo.bootstrap.menu.Menu} this
28620 * Fires after this menu is hidden
28621 * @param {Roo.bootstrap.menu.Menu} this
28626 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28627 * @param {Roo.bootstrap.menu.Menu} this
28628 * @param {Roo.EventObject} e
28635 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28639 weight : 'default',
28644 getChildContainer : function() {
28645 if(this.isSubMenu){
28649 return this.el.select('ul.dropdown-menu', true).first();
28652 getAutoCreate : function()
28657 cls : 'roo-menu-text',
28665 cls : 'fa ' + this.icon
28676 cls : 'dropdown-button btn btn-' + this.weight,
28681 cls : 'dropdown-toggle btn btn-' + this.weight,
28691 cls : 'dropdown-menu'
28697 if(this.pos == 'top'){
28698 cfg.cls += ' dropup';
28701 if(this.isSubMenu){
28704 cls : 'dropdown-menu'
28711 onRender : function(ct, position)
28713 this.isSubMenu = ct.hasClass('dropdown-submenu');
28715 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28718 initEvents : function()
28720 if(this.isSubMenu){
28724 this.hidden = true;
28726 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28727 this.triggerEl.on('click', this.onTriggerPress, this);
28729 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28730 this.buttonEl.on('click', this.onClick, this);
28736 if(this.isSubMenu){
28740 return this.el.select('ul.dropdown-menu', true).first();
28743 onClick : function(e)
28745 this.fireEvent("click", this, e);
28748 onTriggerPress : function(e)
28750 if (this.isVisible()) {
28757 isVisible : function(){
28758 return !this.hidden;
28763 this.fireEvent("beforeshow", this);
28765 this.hidden = false;
28766 this.el.addClass('open');
28768 Roo.get(document).on("mouseup", this.onMouseUp, this);
28770 this.fireEvent("show", this);
28777 this.fireEvent("beforehide", this);
28779 this.hidden = true;
28780 this.el.removeClass('open');
28782 Roo.get(document).un("mouseup", this.onMouseUp);
28784 this.fireEvent("hide", this);
28787 onMouseUp : function()
28801 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28804 * @class Roo.bootstrap.menu.Item
28805 * @extends Roo.bootstrap.Component
28806 * Bootstrap MenuItem class
28807 * @cfg {Boolean} submenu (true | false) default false
28808 * @cfg {String} html text of the item
28809 * @cfg {String} href the link
28810 * @cfg {Boolean} disable (true | false) default false
28811 * @cfg {Boolean} preventDefault (true | false) default true
28812 * @cfg {String} icon Font awesome icon
28813 * @cfg {String} pos Submenu align to (left | right) default right
28817 * Create a new Item
28818 * @param {Object} config The config object
28822 Roo.bootstrap.menu.Item = function(config){
28823 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28827 * Fires when the mouse is hovering over this menu
28828 * @param {Roo.bootstrap.menu.Item} this
28829 * @param {Roo.EventObject} e
28834 * Fires when the mouse exits this menu
28835 * @param {Roo.bootstrap.menu.Item} this
28836 * @param {Roo.EventObject} e
28842 * The raw click event for the entire grid.
28843 * @param {Roo.EventObject} e
28849 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28854 preventDefault: true,
28859 getAutoCreate : function()
28864 cls : 'roo-menu-item-text',
28872 cls : 'fa ' + this.icon
28881 href : this.href || '#',
28888 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28892 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28894 if(this.pos == 'left'){
28895 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28902 initEvents : function()
28904 this.el.on('mouseover', this.onMouseOver, this);
28905 this.el.on('mouseout', this.onMouseOut, this);
28907 this.el.select('a', true).first().on('click', this.onClick, this);
28911 onClick : function(e)
28913 if(this.preventDefault){
28914 e.preventDefault();
28917 this.fireEvent("click", this, e);
28920 onMouseOver : function(e)
28922 if(this.submenu && this.pos == 'left'){
28923 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28926 this.fireEvent("mouseover", this, e);
28929 onMouseOut : function(e)
28931 this.fireEvent("mouseout", this, e);
28943 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28946 * @class Roo.bootstrap.menu.Separator
28947 * @extends Roo.bootstrap.Component
28948 * Bootstrap Separator class
28951 * Create a new Separator
28952 * @param {Object} config The config object
28956 Roo.bootstrap.menu.Separator = function(config){
28957 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28960 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28962 getAutoCreate : function(){
28965 cls: 'dropdown-divider divider'
28983 * @class Roo.bootstrap.Tooltip
28984 * Bootstrap Tooltip class
28985 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28986 * to determine which dom element triggers the tooltip.
28988 * It needs to add support for additional attributes like tooltip-position
28991 * Create a new Toolti
28992 * @param {Object} config The config object
28995 Roo.bootstrap.Tooltip = function(config){
28996 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28998 this.alignment = Roo.bootstrap.Tooltip.alignment;
29000 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29001 this.alignment = config.alignment;
29006 Roo.apply(Roo.bootstrap.Tooltip, {
29008 * @function init initialize tooltip monitoring.
29012 currentTip : false,
29013 currentRegion : false,
29019 Roo.get(document).on('mouseover', this.enter ,this);
29020 Roo.get(document).on('mouseout', this.leave, this);
29023 this.currentTip = new Roo.bootstrap.Tooltip();
29026 enter : function(ev)
29028 var dom = ev.getTarget();
29030 //Roo.log(['enter',dom]);
29031 var el = Roo.fly(dom);
29032 if (this.currentEl) {
29034 //Roo.log(this.currentEl);
29035 //Roo.log(this.currentEl.contains(dom));
29036 if (this.currentEl == el) {
29039 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29045 if (this.currentTip.el) {
29046 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29050 if(!el || el.dom == document){
29056 // you can not look for children, as if el is the body.. then everythign is the child..
29057 if (!el.attr('tooltip')) { //
29058 if (!el.select("[tooltip]").elements.length) {
29061 // is the mouse over this child...?
29062 bindEl = el.select("[tooltip]").first();
29063 var xy = ev.getXY();
29064 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29065 //Roo.log("not in region.");
29068 //Roo.log("child element over..");
29071 this.currentEl = bindEl;
29072 this.currentTip.bind(bindEl);
29073 this.currentRegion = Roo.lib.Region.getRegion(dom);
29074 this.currentTip.enter();
29077 leave : function(ev)
29079 var dom = ev.getTarget();
29080 //Roo.log(['leave',dom]);
29081 if (!this.currentEl) {
29086 if (dom != this.currentEl.dom) {
29089 var xy = ev.getXY();
29090 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29093 // only activate leave if mouse cursor is outside... bounding box..
29098 if (this.currentTip) {
29099 this.currentTip.leave();
29101 //Roo.log('clear currentEl');
29102 this.currentEl = false;
29107 'left' : ['r-l', [-2,0], 'right'],
29108 'right' : ['l-r', [2,0], 'left'],
29109 'bottom' : ['t-b', [0,2], 'top'],
29110 'top' : [ 'b-t', [0,-2], 'bottom']
29116 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29121 delay : null, // can be { show : 300 , hide: 500}
29125 hoverState : null, //???
29127 placement : 'bottom',
29131 getAutoCreate : function(){
29138 cls : 'tooltip-arrow arrow'
29141 cls : 'tooltip-inner'
29148 bind : function(el)
29153 initEvents : function()
29155 this.arrowEl = this.el.select('.arrow', true).first();
29156 this.innerEl = this.el.select('.tooltip-inner', true).first();
29159 enter : function () {
29161 if (this.timeout != null) {
29162 clearTimeout(this.timeout);
29165 this.hoverState = 'in';
29166 //Roo.log("enter - show");
29167 if (!this.delay || !this.delay.show) {
29172 this.timeout = setTimeout(function () {
29173 if (_t.hoverState == 'in') {
29176 }, this.delay.show);
29180 clearTimeout(this.timeout);
29182 this.hoverState = 'out';
29183 if (!this.delay || !this.delay.hide) {
29189 this.timeout = setTimeout(function () {
29190 //Roo.log("leave - timeout");
29192 if (_t.hoverState == 'out') {
29194 Roo.bootstrap.Tooltip.currentEl = false;
29199 show : function (msg)
29202 this.render(document.body);
29205 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29207 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29209 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29211 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29212 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29214 var placement = typeof this.placement == 'function' ?
29215 this.placement.call(this, this.el, on_el) :
29218 var autoToken = /\s?auto?\s?/i;
29219 var autoPlace = autoToken.test(placement);
29221 placement = placement.replace(autoToken, '') || 'top';
29225 //this.el.setXY([0,0]);
29227 //this.el.dom.style.display='block';
29229 //this.el.appendTo(on_el);
29231 var p = this.getPosition();
29232 var box = this.el.getBox();
29238 var align = this.alignment[placement];
29240 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29242 if(placement == 'top' || placement == 'bottom'){
29244 placement = 'right';
29247 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29248 placement = 'left';
29251 var scroll = Roo.select('body', true).first().getScroll();
29253 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29257 align = this.alignment[placement];
29259 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29263 this.el.alignTo(this.bindEl, align[0],align[1]);
29264 //var arrow = this.el.select('.arrow',true).first();
29265 //arrow.set(align[2],
29267 this.el.addClass(placement);
29268 this.el.addClass("bs-tooltip-"+ placement);
29270 this.el.addClass('in fade show');
29272 this.hoverState = null;
29274 if (this.el.hasClass('fade')) {
29289 //this.el.setXY([0,0]);
29290 this.el.removeClass(['show', 'in']);
29306 * @class Roo.bootstrap.LocationPicker
29307 * @extends Roo.bootstrap.Component
29308 * Bootstrap LocationPicker class
29309 * @cfg {Number} latitude Position when init default 0
29310 * @cfg {Number} longitude Position when init default 0
29311 * @cfg {Number} zoom default 15
29312 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29313 * @cfg {Boolean} mapTypeControl default false
29314 * @cfg {Boolean} disableDoubleClickZoom default false
29315 * @cfg {Boolean} scrollwheel default true
29316 * @cfg {Boolean} streetViewControl default false
29317 * @cfg {Number} radius default 0
29318 * @cfg {String} locationName
29319 * @cfg {Boolean} draggable default true
29320 * @cfg {Boolean} enableAutocomplete default false
29321 * @cfg {Boolean} enableReverseGeocode default true
29322 * @cfg {String} markerTitle
29325 * Create a new LocationPicker
29326 * @param {Object} config The config object
29330 Roo.bootstrap.LocationPicker = function(config){
29332 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29337 * Fires when the picker initialized.
29338 * @param {Roo.bootstrap.LocationPicker} this
29339 * @param {Google Location} location
29343 * @event positionchanged
29344 * Fires when the picker position changed.
29345 * @param {Roo.bootstrap.LocationPicker} this
29346 * @param {Google Location} location
29348 positionchanged : true,
29351 * Fires when the map resize.
29352 * @param {Roo.bootstrap.LocationPicker} this
29357 * Fires when the map show.
29358 * @param {Roo.bootstrap.LocationPicker} this
29363 * Fires when the map hide.
29364 * @param {Roo.bootstrap.LocationPicker} this
29369 * Fires when click the map.
29370 * @param {Roo.bootstrap.LocationPicker} this
29371 * @param {Map event} e
29375 * @event mapRightClick
29376 * Fires when right click the map.
29377 * @param {Roo.bootstrap.LocationPicker} this
29378 * @param {Map event} e
29380 mapRightClick : true,
29382 * @event markerClick
29383 * Fires when click the marker.
29384 * @param {Roo.bootstrap.LocationPicker} this
29385 * @param {Map event} e
29387 markerClick : true,
29389 * @event markerRightClick
29390 * Fires when right click the marker.
29391 * @param {Roo.bootstrap.LocationPicker} this
29392 * @param {Map event} e
29394 markerRightClick : true,
29396 * @event OverlayViewDraw
29397 * Fires when OverlayView Draw
29398 * @param {Roo.bootstrap.LocationPicker} this
29400 OverlayViewDraw : true,
29402 * @event OverlayViewOnAdd
29403 * Fires when OverlayView Draw
29404 * @param {Roo.bootstrap.LocationPicker} this
29406 OverlayViewOnAdd : true,
29408 * @event OverlayViewOnRemove
29409 * Fires when OverlayView Draw
29410 * @param {Roo.bootstrap.LocationPicker} this
29412 OverlayViewOnRemove : true,
29414 * @event OverlayViewShow
29415 * Fires when OverlayView Draw
29416 * @param {Roo.bootstrap.LocationPicker} this
29417 * @param {Pixel} cpx
29419 OverlayViewShow : true,
29421 * @event OverlayViewHide
29422 * Fires when OverlayView Draw
29423 * @param {Roo.bootstrap.LocationPicker} this
29425 OverlayViewHide : true,
29427 * @event loadexception
29428 * Fires when load google lib failed.
29429 * @param {Roo.bootstrap.LocationPicker} this
29431 loadexception : true
29436 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29438 gMapContext: false,
29444 mapTypeControl: false,
29445 disableDoubleClickZoom: false,
29447 streetViewControl: false,
29451 enableAutocomplete: false,
29452 enableReverseGeocode: true,
29455 getAutoCreate: function()
29460 cls: 'roo-location-picker'
29466 initEvents: function(ct, position)
29468 if(!this.el.getWidth() || this.isApplied()){
29472 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29477 initial: function()
29479 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29480 this.fireEvent('loadexception', this);
29484 if(!this.mapTypeId){
29485 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29488 this.gMapContext = this.GMapContext();
29490 this.initOverlayView();
29492 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29496 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29497 _this.setPosition(_this.gMapContext.marker.position);
29500 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29501 _this.fireEvent('mapClick', this, event);
29505 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29506 _this.fireEvent('mapRightClick', this, event);
29510 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29511 _this.fireEvent('markerClick', this, event);
29515 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29516 _this.fireEvent('markerRightClick', this, event);
29520 this.setPosition(this.gMapContext.location);
29522 this.fireEvent('initial', this, this.gMapContext.location);
29525 initOverlayView: function()
29529 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29533 _this.fireEvent('OverlayViewDraw', _this);
29538 _this.fireEvent('OverlayViewOnAdd', _this);
29541 onRemove: function()
29543 _this.fireEvent('OverlayViewOnRemove', _this);
29546 show: function(cpx)
29548 _this.fireEvent('OverlayViewShow', _this, cpx);
29553 _this.fireEvent('OverlayViewHide', _this);
29559 fromLatLngToContainerPixel: function(event)
29561 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29564 isApplied: function()
29566 return this.getGmapContext() == false ? false : true;
29569 getGmapContext: function()
29571 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29574 GMapContext: function()
29576 var position = new google.maps.LatLng(this.latitude, this.longitude);
29578 var _map = new google.maps.Map(this.el.dom, {
29581 mapTypeId: this.mapTypeId,
29582 mapTypeControl: this.mapTypeControl,
29583 disableDoubleClickZoom: this.disableDoubleClickZoom,
29584 scrollwheel: this.scrollwheel,
29585 streetViewControl: this.streetViewControl,
29586 locationName: this.locationName,
29587 draggable: this.draggable,
29588 enableAutocomplete: this.enableAutocomplete,
29589 enableReverseGeocode: this.enableReverseGeocode
29592 var _marker = new google.maps.Marker({
29593 position: position,
29595 title: this.markerTitle,
29596 draggable: this.draggable
29603 location: position,
29604 radius: this.radius,
29605 locationName: this.locationName,
29606 addressComponents: {
29607 formatted_address: null,
29608 addressLine1: null,
29609 addressLine2: null,
29611 streetNumber: null,
29615 stateOrProvince: null
29618 domContainer: this.el.dom,
29619 geodecoder: new google.maps.Geocoder()
29623 drawCircle: function(center, radius, options)
29625 if (this.gMapContext.circle != null) {
29626 this.gMapContext.circle.setMap(null);
29630 options = Roo.apply({}, options, {
29631 strokeColor: "#0000FF",
29632 strokeOpacity: .35,
29634 fillColor: "#0000FF",
29638 options.map = this.gMapContext.map;
29639 options.radius = radius;
29640 options.center = center;
29641 this.gMapContext.circle = new google.maps.Circle(options);
29642 return this.gMapContext.circle;
29648 setPosition: function(location)
29650 this.gMapContext.location = location;
29651 this.gMapContext.marker.setPosition(location);
29652 this.gMapContext.map.panTo(location);
29653 this.drawCircle(location, this.gMapContext.radius, {});
29657 if (this.gMapContext.settings.enableReverseGeocode) {
29658 this.gMapContext.geodecoder.geocode({
29659 latLng: this.gMapContext.location
29660 }, function(results, status) {
29662 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29663 _this.gMapContext.locationName = results[0].formatted_address;
29664 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29666 _this.fireEvent('positionchanged', this, location);
29673 this.fireEvent('positionchanged', this, location);
29678 google.maps.event.trigger(this.gMapContext.map, "resize");
29680 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29682 this.fireEvent('resize', this);
29685 setPositionByLatLng: function(latitude, longitude)
29687 this.setPosition(new google.maps.LatLng(latitude, longitude));
29690 getCurrentPosition: function()
29693 latitude: this.gMapContext.location.lat(),
29694 longitude: this.gMapContext.location.lng()
29698 getAddressName: function()
29700 return this.gMapContext.locationName;
29703 getAddressComponents: function()
29705 return this.gMapContext.addressComponents;
29708 address_component_from_google_geocode: function(address_components)
29712 for (var i = 0; i < address_components.length; i++) {
29713 var component = address_components[i];
29714 if (component.types.indexOf("postal_code") >= 0) {
29715 result.postalCode = component.short_name;
29716 } else if (component.types.indexOf("street_number") >= 0) {
29717 result.streetNumber = component.short_name;
29718 } else if (component.types.indexOf("route") >= 0) {
29719 result.streetName = component.short_name;
29720 } else if (component.types.indexOf("neighborhood") >= 0) {
29721 result.city = component.short_name;
29722 } else if (component.types.indexOf("locality") >= 0) {
29723 result.city = component.short_name;
29724 } else if (component.types.indexOf("sublocality") >= 0) {
29725 result.district = component.short_name;
29726 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29727 result.stateOrProvince = component.short_name;
29728 } else if (component.types.indexOf("country") >= 0) {
29729 result.country = component.short_name;
29733 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29734 result.addressLine2 = "";
29738 setZoomLevel: function(zoom)
29740 this.gMapContext.map.setZoom(zoom);
29753 this.fireEvent('show', this);
29764 this.fireEvent('hide', this);
29769 Roo.apply(Roo.bootstrap.LocationPicker, {
29771 OverlayView : function(map, options)
29773 options = options || {};
29780 * @class Roo.bootstrap.Alert
29781 * @extends Roo.bootstrap.Component
29782 * Bootstrap Alert class - shows an alert area box
29784 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29785 Enter a valid email address
29788 * @cfg {String} title The title of alert
29789 * @cfg {String} html The content of alert
29790 * @cfg {String} weight ( success | info | warning | danger )
29791 * @cfg {String} faicon font-awesomeicon
29794 * Create a new alert
29795 * @param {Object} config The config object
29799 Roo.bootstrap.Alert = function(config){
29800 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29804 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29811 getAutoCreate : function()
29820 cls : 'roo-alert-icon'
29825 cls : 'roo-alert-title',
29830 cls : 'roo-alert-text',
29837 cfg.cn[0].cls += ' fa ' + this.faicon;
29841 cfg.cls += ' alert-' + this.weight;
29847 initEvents: function()
29849 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29852 setTitle : function(str)
29854 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29857 setText : function(str)
29859 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29862 setWeight : function(weight)
29865 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29868 this.weight = weight;
29870 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29873 setIcon : function(icon)
29876 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29879 this.faicon = icon;
29881 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29902 * @class Roo.bootstrap.UploadCropbox
29903 * @extends Roo.bootstrap.Component
29904 * Bootstrap UploadCropbox class
29905 * @cfg {String} emptyText show when image has been loaded
29906 * @cfg {String} rotateNotify show when image too small to rotate
29907 * @cfg {Number} errorTimeout default 3000
29908 * @cfg {Number} minWidth default 300
29909 * @cfg {Number} minHeight default 300
29910 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29911 * @cfg {Boolean} isDocument (true|false) default false
29912 * @cfg {String} url action url
29913 * @cfg {String} paramName default 'imageUpload'
29914 * @cfg {String} method default POST
29915 * @cfg {Boolean} loadMask (true|false) default true
29916 * @cfg {Boolean} loadingText default 'Loading...'
29919 * Create a new UploadCropbox
29920 * @param {Object} config The config object
29923 Roo.bootstrap.UploadCropbox = function(config){
29924 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29928 * @event beforeselectfile
29929 * Fire before select file
29930 * @param {Roo.bootstrap.UploadCropbox} this
29932 "beforeselectfile" : true,
29935 * Fire after initEvent
29936 * @param {Roo.bootstrap.UploadCropbox} this
29941 * Fire after initEvent
29942 * @param {Roo.bootstrap.UploadCropbox} this
29943 * @param {String} data
29948 * Fire when preparing the file data
29949 * @param {Roo.bootstrap.UploadCropbox} this
29950 * @param {Object} file
29955 * Fire when get exception
29956 * @param {Roo.bootstrap.UploadCropbox} this
29957 * @param {XMLHttpRequest} xhr
29959 "exception" : true,
29961 * @event beforeloadcanvas
29962 * Fire before load the canvas
29963 * @param {Roo.bootstrap.UploadCropbox} this
29964 * @param {String} src
29966 "beforeloadcanvas" : true,
29969 * Fire when trash image
29970 * @param {Roo.bootstrap.UploadCropbox} this
29975 * Fire when download the image
29976 * @param {Roo.bootstrap.UploadCropbox} this
29980 * @event footerbuttonclick
29981 * Fire when footerbuttonclick
29982 * @param {Roo.bootstrap.UploadCropbox} this
29983 * @param {String} type
29985 "footerbuttonclick" : true,
29989 * @param {Roo.bootstrap.UploadCropbox} this
29994 * Fire when rotate the image
29995 * @param {Roo.bootstrap.UploadCropbox} this
29996 * @param {String} pos
30001 * Fire when inspect the file
30002 * @param {Roo.bootstrap.UploadCropbox} this
30003 * @param {Object} file
30008 * Fire when xhr upload the file
30009 * @param {Roo.bootstrap.UploadCropbox} this
30010 * @param {Object} data
30015 * Fire when arrange the file data
30016 * @param {Roo.bootstrap.UploadCropbox} this
30017 * @param {Object} formData
30022 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30025 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30027 emptyText : 'Click to upload image',
30028 rotateNotify : 'Image is too small to rotate',
30029 errorTimeout : 3000,
30043 cropType : 'image/jpeg',
30045 canvasLoaded : false,
30046 isDocument : false,
30048 paramName : 'imageUpload',
30050 loadingText : 'Loading...',
30053 getAutoCreate : function()
30057 cls : 'roo-upload-cropbox',
30061 cls : 'roo-upload-cropbox-selector',
30066 cls : 'roo-upload-cropbox-body',
30067 style : 'cursor:pointer',
30071 cls : 'roo-upload-cropbox-preview'
30075 cls : 'roo-upload-cropbox-thumb'
30079 cls : 'roo-upload-cropbox-empty-notify',
30080 html : this.emptyText
30084 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30085 html : this.rotateNotify
30091 cls : 'roo-upload-cropbox-footer',
30094 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30104 onRender : function(ct, position)
30106 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30108 if (this.buttons.length) {
30110 Roo.each(this.buttons, function(bb) {
30112 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30114 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30120 this.maskEl = this.el;
30124 initEvents : function()
30126 this.urlAPI = (window.createObjectURL && window) ||
30127 (window.URL && URL.revokeObjectURL && URL) ||
30128 (window.webkitURL && webkitURL);
30130 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30131 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30133 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30134 this.selectorEl.hide();
30136 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30137 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30139 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30140 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141 this.thumbEl.hide();
30143 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30144 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30146 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30147 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30148 this.errorEl.hide();
30150 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30151 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30152 this.footerEl.hide();
30154 this.setThumbBoxSize();
30160 this.fireEvent('initial', this);
30167 window.addEventListener("resize", function() { _this.resize(); } );
30169 this.bodyEl.on('click', this.beforeSelectFile, this);
30172 this.bodyEl.on('touchstart', this.onTouchStart, this);
30173 this.bodyEl.on('touchmove', this.onTouchMove, this);
30174 this.bodyEl.on('touchend', this.onTouchEnd, this);
30178 this.bodyEl.on('mousedown', this.onMouseDown, this);
30179 this.bodyEl.on('mousemove', this.onMouseMove, this);
30180 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30181 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30182 Roo.get(document).on('mouseup', this.onMouseUp, this);
30185 this.selectorEl.on('change', this.onFileSelected, this);
30191 this.baseScale = 1;
30193 this.baseRotate = 1;
30194 this.dragable = false;
30195 this.pinching = false;
30198 this.cropData = false;
30199 this.notifyEl.dom.innerHTML = this.emptyText;
30201 this.selectorEl.dom.value = '';
30205 resize : function()
30207 if(this.fireEvent('resize', this) != false){
30208 this.setThumbBoxPosition();
30209 this.setCanvasPosition();
30213 onFooterButtonClick : function(e, el, o, type)
30216 case 'rotate-left' :
30217 this.onRotateLeft(e);
30219 case 'rotate-right' :
30220 this.onRotateRight(e);
30223 this.beforeSelectFile(e);
30238 this.fireEvent('footerbuttonclick', this, type);
30241 beforeSelectFile : function(e)
30243 e.preventDefault();
30245 if(this.fireEvent('beforeselectfile', this) != false){
30246 this.selectorEl.dom.click();
30250 onFileSelected : function(e)
30252 e.preventDefault();
30254 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30258 var file = this.selectorEl.dom.files[0];
30260 if(this.fireEvent('inspect', this, file) != false){
30261 this.prepare(file);
30266 trash : function(e)
30268 this.fireEvent('trash', this);
30271 download : function(e)
30273 this.fireEvent('download', this);
30276 loadCanvas : function(src)
30278 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30282 this.imageEl = document.createElement('img');
30286 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30288 this.imageEl.src = src;
30292 onLoadCanvas : function()
30294 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30295 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30297 this.bodyEl.un('click', this.beforeSelectFile, this);
30299 this.notifyEl.hide();
30300 this.thumbEl.show();
30301 this.footerEl.show();
30303 this.baseRotateLevel();
30305 if(this.isDocument){
30306 this.setThumbBoxSize();
30309 this.setThumbBoxPosition();
30311 this.baseScaleLevel();
30317 this.canvasLoaded = true;
30320 this.maskEl.unmask();
30325 setCanvasPosition : function()
30327 if(!this.canvasEl){
30331 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30332 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30334 this.previewEl.setLeft(pw);
30335 this.previewEl.setTop(ph);
30339 onMouseDown : function(e)
30343 this.dragable = true;
30344 this.pinching = false;
30346 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30347 this.dragable = false;
30351 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30352 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30356 onMouseMove : function(e)
30360 if(!this.canvasLoaded){
30364 if (!this.dragable){
30368 var minX = Math.ceil(this.thumbEl.getLeft(true));
30369 var minY = Math.ceil(this.thumbEl.getTop(true));
30371 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30372 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30374 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30375 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30377 x = x - this.mouseX;
30378 y = y - this.mouseY;
30380 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30381 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30383 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30384 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30386 this.previewEl.setLeft(bgX);
30387 this.previewEl.setTop(bgY);
30389 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30390 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30393 onMouseUp : function(e)
30397 this.dragable = false;
30400 onMouseWheel : function(e)
30404 this.startScale = this.scale;
30406 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30408 if(!this.zoomable()){
30409 this.scale = this.startScale;
30418 zoomable : function()
30420 var minScale = this.thumbEl.getWidth() / this.minWidth;
30422 if(this.minWidth < this.minHeight){
30423 minScale = this.thumbEl.getHeight() / this.minHeight;
30426 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30427 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30431 (this.rotate == 0 || this.rotate == 180) &&
30433 width > this.imageEl.OriginWidth ||
30434 height > this.imageEl.OriginHeight ||
30435 (width < this.minWidth && height < this.minHeight)
30443 (this.rotate == 90 || this.rotate == 270) &&
30445 width > this.imageEl.OriginWidth ||
30446 height > this.imageEl.OriginHeight ||
30447 (width < this.minHeight && height < this.minWidth)
30454 !this.isDocument &&
30455 (this.rotate == 0 || this.rotate == 180) &&
30457 width < this.minWidth ||
30458 width > this.imageEl.OriginWidth ||
30459 height < this.minHeight ||
30460 height > this.imageEl.OriginHeight
30467 !this.isDocument &&
30468 (this.rotate == 90 || this.rotate == 270) &&
30470 width < this.minHeight ||
30471 width > this.imageEl.OriginWidth ||
30472 height < this.minWidth ||
30473 height > this.imageEl.OriginHeight
30483 onRotateLeft : function(e)
30485 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30487 var minScale = this.thumbEl.getWidth() / this.minWidth;
30489 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30490 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30492 this.startScale = this.scale;
30494 while (this.getScaleLevel() < minScale){
30496 this.scale = this.scale + 1;
30498 if(!this.zoomable()){
30503 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30504 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30509 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30516 this.scale = this.startScale;
30518 this.onRotateFail();
30523 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30525 if(this.isDocument){
30526 this.setThumbBoxSize();
30527 this.setThumbBoxPosition();
30528 this.setCanvasPosition();
30533 this.fireEvent('rotate', this, 'left');
30537 onRotateRight : function(e)
30539 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30541 var minScale = this.thumbEl.getWidth() / this.minWidth;
30543 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30544 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30546 this.startScale = this.scale;
30548 while (this.getScaleLevel() < minScale){
30550 this.scale = this.scale + 1;
30552 if(!this.zoomable()){
30557 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30558 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30563 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30570 this.scale = this.startScale;
30572 this.onRotateFail();
30577 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30579 if(this.isDocument){
30580 this.setThumbBoxSize();
30581 this.setThumbBoxPosition();
30582 this.setCanvasPosition();
30587 this.fireEvent('rotate', this, 'right');
30590 onRotateFail : function()
30592 this.errorEl.show(true);
30596 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30601 this.previewEl.dom.innerHTML = '';
30603 var canvasEl = document.createElement("canvas");
30605 var contextEl = canvasEl.getContext("2d");
30607 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30608 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30609 var center = this.imageEl.OriginWidth / 2;
30611 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30612 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30613 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30614 center = this.imageEl.OriginHeight / 2;
30617 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30619 contextEl.translate(center, center);
30620 contextEl.rotate(this.rotate * Math.PI / 180);
30622 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30624 this.canvasEl = document.createElement("canvas");
30626 this.contextEl = this.canvasEl.getContext("2d");
30628 switch (this.rotate) {
30631 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30632 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30634 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30639 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30640 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30642 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30643 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);
30647 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30652 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30653 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30655 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30656 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);
30660 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);
30665 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30666 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30668 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30669 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30673 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);
30680 this.previewEl.appendChild(this.canvasEl);
30682 this.setCanvasPosition();
30687 if(!this.canvasLoaded){
30691 var imageCanvas = document.createElement("canvas");
30693 var imageContext = imageCanvas.getContext("2d");
30695 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30696 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30698 var center = imageCanvas.width / 2;
30700 imageContext.translate(center, center);
30702 imageContext.rotate(this.rotate * Math.PI / 180);
30704 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30706 var canvas = document.createElement("canvas");
30708 var context = canvas.getContext("2d");
30710 canvas.width = this.minWidth;
30711 canvas.height = this.minHeight;
30713 switch (this.rotate) {
30716 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30717 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30719 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30720 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30722 var targetWidth = this.minWidth - 2 * x;
30723 var targetHeight = this.minHeight - 2 * y;
30727 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30728 scale = targetWidth / width;
30731 if(x > 0 && y == 0){
30732 scale = targetHeight / height;
30735 if(x > 0 && y > 0){
30736 scale = targetWidth / width;
30738 if(width < height){
30739 scale = targetHeight / height;
30743 context.scale(scale, scale);
30745 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30746 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30748 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30749 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30751 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30756 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30757 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30759 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30760 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30762 var targetWidth = this.minWidth - 2 * x;
30763 var targetHeight = this.minHeight - 2 * y;
30767 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30768 scale = targetWidth / width;
30771 if(x > 0 && y == 0){
30772 scale = targetHeight / height;
30775 if(x > 0 && y > 0){
30776 scale = targetWidth / width;
30778 if(width < height){
30779 scale = targetHeight / height;
30783 context.scale(scale, scale);
30785 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30786 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30788 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30789 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30791 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30793 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30798 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30799 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30801 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30802 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30804 var targetWidth = this.minWidth - 2 * x;
30805 var targetHeight = this.minHeight - 2 * y;
30809 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30810 scale = targetWidth / width;
30813 if(x > 0 && y == 0){
30814 scale = targetHeight / height;
30817 if(x > 0 && y > 0){
30818 scale = targetWidth / width;
30820 if(width < height){
30821 scale = targetHeight / height;
30825 context.scale(scale, scale);
30827 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30828 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30830 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30831 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30833 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30834 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30836 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30841 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30842 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30844 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30845 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30847 var targetWidth = this.minWidth - 2 * x;
30848 var targetHeight = this.minHeight - 2 * y;
30852 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30853 scale = targetWidth / width;
30856 if(x > 0 && y == 0){
30857 scale = targetHeight / height;
30860 if(x > 0 && y > 0){
30861 scale = targetWidth / width;
30863 if(width < height){
30864 scale = targetHeight / height;
30868 context.scale(scale, scale);
30870 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30871 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30873 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30874 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30876 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30878 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30885 this.cropData = canvas.toDataURL(this.cropType);
30887 if(this.fireEvent('crop', this, this.cropData) !== false){
30888 this.process(this.file, this.cropData);
30895 setThumbBoxSize : function()
30899 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30900 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30901 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30903 this.minWidth = width;
30904 this.minHeight = height;
30906 if(this.rotate == 90 || this.rotate == 270){
30907 this.minWidth = height;
30908 this.minHeight = width;
30913 width = Math.ceil(this.minWidth * height / this.minHeight);
30915 if(this.minWidth > this.minHeight){
30917 height = Math.ceil(this.minHeight * width / this.minWidth);
30920 this.thumbEl.setStyle({
30921 width : width + 'px',
30922 height : height + 'px'
30929 setThumbBoxPosition : function()
30931 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30932 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30934 this.thumbEl.setLeft(x);
30935 this.thumbEl.setTop(y);
30939 baseRotateLevel : function()
30941 this.baseRotate = 1;
30944 typeof(this.exif) != 'undefined' &&
30945 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30946 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30948 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30951 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30955 baseScaleLevel : function()
30959 if(this.isDocument){
30961 if(this.baseRotate == 6 || this.baseRotate == 8){
30963 height = this.thumbEl.getHeight();
30964 this.baseScale = height / this.imageEl.OriginWidth;
30966 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30967 width = this.thumbEl.getWidth();
30968 this.baseScale = width / this.imageEl.OriginHeight;
30974 height = this.thumbEl.getHeight();
30975 this.baseScale = height / this.imageEl.OriginHeight;
30977 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30978 width = this.thumbEl.getWidth();
30979 this.baseScale = width / this.imageEl.OriginWidth;
30985 if(this.baseRotate == 6 || this.baseRotate == 8){
30987 width = this.thumbEl.getHeight();
30988 this.baseScale = width / this.imageEl.OriginHeight;
30990 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30991 height = this.thumbEl.getWidth();
30992 this.baseScale = height / this.imageEl.OriginHeight;
30995 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30996 height = this.thumbEl.getWidth();
30997 this.baseScale = height / this.imageEl.OriginHeight;
30999 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31000 width = this.thumbEl.getHeight();
31001 this.baseScale = width / this.imageEl.OriginWidth;
31008 width = this.thumbEl.getWidth();
31009 this.baseScale = width / this.imageEl.OriginWidth;
31011 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31012 height = this.thumbEl.getHeight();
31013 this.baseScale = height / this.imageEl.OriginHeight;
31016 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31018 height = this.thumbEl.getHeight();
31019 this.baseScale = height / this.imageEl.OriginHeight;
31021 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31022 width = this.thumbEl.getWidth();
31023 this.baseScale = width / this.imageEl.OriginWidth;
31031 getScaleLevel : function()
31033 return this.baseScale * Math.pow(1.1, this.scale);
31036 onTouchStart : function(e)
31038 if(!this.canvasLoaded){
31039 this.beforeSelectFile(e);
31043 var touches = e.browserEvent.touches;
31049 if(touches.length == 1){
31050 this.onMouseDown(e);
31054 if(touches.length != 2){
31060 for(var i = 0, finger; finger = touches[i]; i++){
31061 coords.push(finger.pageX, finger.pageY);
31064 var x = Math.pow(coords[0] - coords[2], 2);
31065 var y = Math.pow(coords[1] - coords[3], 2);
31067 this.startDistance = Math.sqrt(x + y);
31069 this.startScale = this.scale;
31071 this.pinching = true;
31072 this.dragable = false;
31076 onTouchMove : function(e)
31078 if(!this.pinching && !this.dragable){
31082 var touches = e.browserEvent.touches;
31089 this.onMouseMove(e);
31095 for(var i = 0, finger; finger = touches[i]; i++){
31096 coords.push(finger.pageX, finger.pageY);
31099 var x = Math.pow(coords[0] - coords[2], 2);
31100 var y = Math.pow(coords[1] - coords[3], 2);
31102 this.endDistance = Math.sqrt(x + y);
31104 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31106 if(!this.zoomable()){
31107 this.scale = this.startScale;
31115 onTouchEnd : function(e)
31117 this.pinching = false;
31118 this.dragable = false;
31122 process : function(file, crop)
31125 this.maskEl.mask(this.loadingText);
31128 this.xhr = new XMLHttpRequest();
31130 file.xhr = this.xhr;
31132 this.xhr.open(this.method, this.url, true);
31135 "Accept": "application/json",
31136 "Cache-Control": "no-cache",
31137 "X-Requested-With": "XMLHttpRequest"
31140 for (var headerName in headers) {
31141 var headerValue = headers[headerName];
31143 this.xhr.setRequestHeader(headerName, headerValue);
31149 this.xhr.onload = function()
31151 _this.xhrOnLoad(_this.xhr);
31154 this.xhr.onerror = function()
31156 _this.xhrOnError(_this.xhr);
31159 var formData = new FormData();
31161 formData.append('returnHTML', 'NO');
31164 formData.append('crop', crop);
31167 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31168 formData.append(this.paramName, file, file.name);
31171 if(typeof(file.filename) != 'undefined'){
31172 formData.append('filename', file.filename);
31175 if(typeof(file.mimetype) != 'undefined'){
31176 formData.append('mimetype', file.mimetype);
31179 if(this.fireEvent('arrange', this, formData) != false){
31180 this.xhr.send(formData);
31184 xhrOnLoad : function(xhr)
31187 this.maskEl.unmask();
31190 if (xhr.readyState !== 4) {
31191 this.fireEvent('exception', this, xhr);
31195 var response = Roo.decode(xhr.responseText);
31197 if(!response.success){
31198 this.fireEvent('exception', this, xhr);
31202 var response = Roo.decode(xhr.responseText);
31204 this.fireEvent('upload', this, response);
31208 xhrOnError : function()
31211 this.maskEl.unmask();
31214 Roo.log('xhr on error');
31216 var response = Roo.decode(xhr.responseText);
31222 prepare : function(file)
31225 this.maskEl.mask(this.loadingText);
31231 if(typeof(file) === 'string'){
31232 this.loadCanvas(file);
31236 if(!file || !this.urlAPI){
31241 this.cropType = file.type;
31245 if(this.fireEvent('prepare', this, this.file) != false){
31247 var reader = new FileReader();
31249 reader.onload = function (e) {
31250 if (e.target.error) {
31251 Roo.log(e.target.error);
31255 var buffer = e.target.result,
31256 dataView = new DataView(buffer),
31258 maxOffset = dataView.byteLength - 4,
31262 if (dataView.getUint16(0) === 0xffd8) {
31263 while (offset < maxOffset) {
31264 markerBytes = dataView.getUint16(offset);
31266 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31267 markerLength = dataView.getUint16(offset + 2) + 2;
31268 if (offset + markerLength > dataView.byteLength) {
31269 Roo.log('Invalid meta data: Invalid segment size.');
31273 if(markerBytes == 0xffe1){
31274 _this.parseExifData(
31281 offset += markerLength;
31291 var url = _this.urlAPI.createObjectURL(_this.file);
31293 _this.loadCanvas(url);
31298 reader.readAsArrayBuffer(this.file);
31304 parseExifData : function(dataView, offset, length)
31306 var tiffOffset = offset + 10,
31310 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31311 // No Exif data, might be XMP data instead
31315 // Check for the ASCII code for "Exif" (0x45786966):
31316 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31317 // No Exif data, might be XMP data instead
31320 if (tiffOffset + 8 > dataView.byteLength) {
31321 Roo.log('Invalid Exif data: Invalid segment size.');
31324 // Check for the two null bytes:
31325 if (dataView.getUint16(offset + 8) !== 0x0000) {
31326 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31329 // Check the byte alignment:
31330 switch (dataView.getUint16(tiffOffset)) {
31332 littleEndian = true;
31335 littleEndian = false;
31338 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31341 // Check for the TIFF tag marker (0x002A):
31342 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31343 Roo.log('Invalid Exif data: Missing TIFF marker.');
31346 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31347 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31349 this.parseExifTags(
31352 tiffOffset + dirOffset,
31357 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31362 if (dirOffset + 6 > dataView.byteLength) {
31363 Roo.log('Invalid Exif data: Invalid directory offset.');
31366 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31367 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31368 if (dirEndOffset + 4 > dataView.byteLength) {
31369 Roo.log('Invalid Exif data: Invalid directory size.');
31372 for (i = 0; i < tagsNumber; i += 1) {
31376 dirOffset + 2 + 12 * i, // tag offset
31380 // Return the offset to the next directory:
31381 return dataView.getUint32(dirEndOffset, littleEndian);
31384 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31386 var tag = dataView.getUint16(offset, littleEndian);
31388 this.exif[tag] = this.getExifValue(
31392 dataView.getUint16(offset + 2, littleEndian), // tag type
31393 dataView.getUint32(offset + 4, littleEndian), // tag length
31398 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31400 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31409 Roo.log('Invalid Exif data: Invalid tag type.');
31413 tagSize = tagType.size * length;
31414 // Determine if the value is contained in the dataOffset bytes,
31415 // or if the value at the dataOffset is a pointer to the actual data:
31416 dataOffset = tagSize > 4 ?
31417 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31418 if (dataOffset + tagSize > dataView.byteLength) {
31419 Roo.log('Invalid Exif data: Invalid data offset.');
31422 if (length === 1) {
31423 return tagType.getValue(dataView, dataOffset, littleEndian);
31426 for (i = 0; i < length; i += 1) {
31427 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31430 if (tagType.ascii) {
31432 // Concatenate the chars:
31433 for (i = 0; i < values.length; i += 1) {
31435 // Ignore the terminating NULL byte(s):
31436 if (c === '\u0000') {
31448 Roo.apply(Roo.bootstrap.UploadCropbox, {
31450 'Orientation': 0x0112
31454 1: 0, //'top-left',
31456 3: 180, //'bottom-right',
31457 // 4: 'bottom-left',
31459 6: 90, //'right-top',
31460 // 7: 'right-bottom',
31461 8: 270 //'left-bottom'
31465 // byte, 8-bit unsigned int:
31467 getValue: function (dataView, dataOffset) {
31468 return dataView.getUint8(dataOffset);
31472 // ascii, 8-bit byte:
31474 getValue: function (dataView, dataOffset) {
31475 return String.fromCharCode(dataView.getUint8(dataOffset));
31480 // short, 16 bit int:
31482 getValue: function (dataView, dataOffset, littleEndian) {
31483 return dataView.getUint16(dataOffset, littleEndian);
31487 // long, 32 bit int:
31489 getValue: function (dataView, dataOffset, littleEndian) {
31490 return dataView.getUint32(dataOffset, littleEndian);
31494 // rational = two long values, first is numerator, second is denominator:
31496 getValue: function (dataView, dataOffset, littleEndian) {
31497 return dataView.getUint32(dataOffset, littleEndian) /
31498 dataView.getUint32(dataOffset + 4, littleEndian);
31502 // slong, 32 bit signed int:
31504 getValue: function (dataView, dataOffset, littleEndian) {
31505 return dataView.getInt32(dataOffset, littleEndian);
31509 // srational, two slongs, first is numerator, second is denominator:
31511 getValue: function (dataView, dataOffset, littleEndian) {
31512 return dataView.getInt32(dataOffset, littleEndian) /
31513 dataView.getInt32(dataOffset + 4, littleEndian);
31523 cls : 'btn-group roo-upload-cropbox-rotate-left',
31524 action : 'rotate-left',
31528 cls : 'btn btn-default',
31529 html : '<i class="fa fa-undo"></i>'
31535 cls : 'btn-group roo-upload-cropbox-picture',
31536 action : 'picture',
31540 cls : 'btn btn-default',
31541 html : '<i class="fa fa-picture-o"></i>'
31547 cls : 'btn-group roo-upload-cropbox-rotate-right',
31548 action : 'rotate-right',
31552 cls : 'btn btn-default',
31553 html : '<i class="fa fa-repeat"></i>'
31561 cls : 'btn-group roo-upload-cropbox-rotate-left',
31562 action : 'rotate-left',
31566 cls : 'btn btn-default',
31567 html : '<i class="fa fa-undo"></i>'
31573 cls : 'btn-group roo-upload-cropbox-download',
31574 action : 'download',
31578 cls : 'btn btn-default',
31579 html : '<i class="fa fa-download"></i>'
31585 cls : 'btn-group roo-upload-cropbox-crop',
31590 cls : 'btn btn-default',
31591 html : '<i class="fa fa-crop"></i>'
31597 cls : 'btn-group roo-upload-cropbox-trash',
31602 cls : 'btn btn-default',
31603 html : '<i class="fa fa-trash"></i>'
31609 cls : 'btn-group roo-upload-cropbox-rotate-right',
31610 action : 'rotate-right',
31614 cls : 'btn btn-default',
31615 html : '<i class="fa fa-repeat"></i>'
31623 cls : 'btn-group roo-upload-cropbox-rotate-left',
31624 action : 'rotate-left',
31628 cls : 'btn btn-default',
31629 html : '<i class="fa fa-undo"></i>'
31635 cls : 'btn-group roo-upload-cropbox-rotate-right',
31636 action : 'rotate-right',
31640 cls : 'btn btn-default',
31641 html : '<i class="fa fa-repeat"></i>'
31654 * @class Roo.bootstrap.DocumentManager
31655 * @extends Roo.bootstrap.Component
31656 * Bootstrap DocumentManager class
31657 * @cfg {String} paramName default 'imageUpload'
31658 * @cfg {String} toolTipName default 'filename'
31659 * @cfg {String} method default POST
31660 * @cfg {String} url action url
31661 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31662 * @cfg {Boolean} multiple multiple upload default true
31663 * @cfg {Number} thumbSize default 300
31664 * @cfg {String} fieldLabel
31665 * @cfg {Number} labelWidth default 4
31666 * @cfg {String} labelAlign (left|top) default left
31667 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31668 * @cfg {Number} labellg set the width of label (1-12)
31669 * @cfg {Number} labelmd set the width of label (1-12)
31670 * @cfg {Number} labelsm set the width of label (1-12)
31671 * @cfg {Number} labelxs set the width of label (1-12)
31674 * Create a new DocumentManager
31675 * @param {Object} config The config object
31678 Roo.bootstrap.DocumentManager = function(config){
31679 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31682 this.delegates = [];
31687 * Fire when initial the DocumentManager
31688 * @param {Roo.bootstrap.DocumentManager} this
31693 * inspect selected file
31694 * @param {Roo.bootstrap.DocumentManager} this
31695 * @param {File} file
31700 * Fire when xhr load exception
31701 * @param {Roo.bootstrap.DocumentManager} this
31702 * @param {XMLHttpRequest} xhr
31704 "exception" : true,
31706 * @event afterupload
31707 * Fire when xhr load exception
31708 * @param {Roo.bootstrap.DocumentManager} this
31709 * @param {XMLHttpRequest} xhr
31711 "afterupload" : true,
31714 * prepare the form data
31715 * @param {Roo.bootstrap.DocumentManager} this
31716 * @param {Object} formData
31721 * Fire when remove the file
31722 * @param {Roo.bootstrap.DocumentManager} this
31723 * @param {Object} file
31728 * Fire after refresh the file
31729 * @param {Roo.bootstrap.DocumentManager} this
31734 * Fire after click the image
31735 * @param {Roo.bootstrap.DocumentManager} this
31736 * @param {Object} file
31741 * Fire when upload a image and editable set to true
31742 * @param {Roo.bootstrap.DocumentManager} this
31743 * @param {Object} file
31747 * @event beforeselectfile
31748 * Fire before select file
31749 * @param {Roo.bootstrap.DocumentManager} this
31751 "beforeselectfile" : true,
31754 * Fire before process file
31755 * @param {Roo.bootstrap.DocumentManager} this
31756 * @param {Object} file
31760 * @event previewrendered
31761 * Fire when preview rendered
31762 * @param {Roo.bootstrap.DocumentManager} this
31763 * @param {Object} file
31765 "previewrendered" : true,
31768 "previewResize" : true
31773 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31782 paramName : 'imageUpload',
31783 toolTipName : 'filename',
31786 labelAlign : 'left',
31796 getAutoCreate : function()
31798 var managerWidget = {
31800 cls : 'roo-document-manager',
31804 cls : 'roo-document-manager-selector',
31809 cls : 'roo-document-manager-uploader',
31813 cls : 'roo-document-manager-upload-btn',
31814 html : '<i class="fa fa-plus"></i>'
31825 cls : 'column col-md-12',
31830 if(this.fieldLabel.length){
31835 cls : 'column col-md-12',
31836 html : this.fieldLabel
31840 cls : 'column col-md-12',
31845 if(this.labelAlign == 'left'){
31850 html : this.fieldLabel
31859 if(this.labelWidth > 12){
31860 content[0].style = "width: " + this.labelWidth + 'px';
31863 if(this.labelWidth < 13 && this.labelmd == 0){
31864 this.labelmd = this.labelWidth;
31867 if(this.labellg > 0){
31868 content[0].cls += ' col-lg-' + this.labellg;
31869 content[1].cls += ' col-lg-' + (12 - this.labellg);
31872 if(this.labelmd > 0){
31873 content[0].cls += ' col-md-' + this.labelmd;
31874 content[1].cls += ' col-md-' + (12 - this.labelmd);
31877 if(this.labelsm > 0){
31878 content[0].cls += ' col-sm-' + this.labelsm;
31879 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31882 if(this.labelxs > 0){
31883 content[0].cls += ' col-xs-' + this.labelxs;
31884 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31892 cls : 'row clearfix',
31900 initEvents : function()
31902 this.managerEl = this.el.select('.roo-document-manager', true).first();
31903 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31905 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31906 this.selectorEl.hide();
31909 this.selectorEl.attr('multiple', 'multiple');
31912 this.selectorEl.on('change', this.onFileSelected, this);
31914 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31915 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31917 this.uploader.on('click', this.onUploaderClick, this);
31919 this.renderProgressDialog();
31923 window.addEventListener("resize", function() { _this.refresh(); } );
31925 this.fireEvent('initial', this);
31928 renderProgressDialog : function()
31932 this.progressDialog = new Roo.bootstrap.Modal({
31933 cls : 'roo-document-manager-progress-dialog',
31934 allow_close : false,
31945 btnclick : function() {
31946 _this.uploadCancel();
31952 this.progressDialog.render(Roo.get(document.body));
31954 this.progress = new Roo.bootstrap.Progress({
31955 cls : 'roo-document-manager-progress',
31960 this.progress.render(this.progressDialog.getChildContainer());
31962 this.progressBar = new Roo.bootstrap.ProgressBar({
31963 cls : 'roo-document-manager-progress-bar',
31966 aria_valuemax : 12,
31970 this.progressBar.render(this.progress.getChildContainer());
31973 onUploaderClick : function(e)
31975 e.preventDefault();
31977 if(this.fireEvent('beforeselectfile', this) != false){
31978 this.selectorEl.dom.click();
31983 onFileSelected : function(e)
31985 e.preventDefault();
31987 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31991 Roo.each(this.selectorEl.dom.files, function(file){
31992 if(this.fireEvent('inspect', this, file) != false){
31993 this.files.push(file);
32003 this.selectorEl.dom.value = '';
32005 if(!this.files || !this.files.length){
32009 if(this.boxes > 0 && this.files.length > this.boxes){
32010 this.files = this.files.slice(0, this.boxes);
32013 this.uploader.show();
32015 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32016 this.uploader.hide();
32025 Roo.each(this.files, function(file){
32027 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32028 var f = this.renderPreview(file);
32033 if(file.type.indexOf('image') != -1){
32034 this.delegates.push(
32036 _this.process(file);
32037 }).createDelegate(this)
32045 _this.process(file);
32046 }).createDelegate(this)
32051 this.files = files;
32053 this.delegates = this.delegates.concat(docs);
32055 if(!this.delegates.length){
32060 this.progressBar.aria_valuemax = this.delegates.length;
32067 arrange : function()
32069 if(!this.delegates.length){
32070 this.progressDialog.hide();
32075 var delegate = this.delegates.shift();
32077 this.progressDialog.show();
32079 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32081 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32086 refresh : function()
32088 this.uploader.show();
32090 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32091 this.uploader.hide();
32094 Roo.isTouch ? this.closable(false) : this.closable(true);
32096 this.fireEvent('refresh', this);
32099 onRemove : function(e, el, o)
32101 e.preventDefault();
32103 this.fireEvent('remove', this, o);
32107 remove : function(o)
32111 Roo.each(this.files, function(file){
32112 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32121 this.files = files;
32128 Roo.each(this.files, function(file){
32133 file.target.remove();
32142 onClick : function(e, el, o)
32144 e.preventDefault();
32146 this.fireEvent('click', this, o);
32150 closable : function(closable)
32152 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32154 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32166 xhrOnLoad : function(xhr)
32168 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32172 if (xhr.readyState !== 4) {
32174 this.fireEvent('exception', this, xhr);
32178 var response = Roo.decode(xhr.responseText);
32180 if(!response.success){
32182 this.fireEvent('exception', this, xhr);
32186 var file = this.renderPreview(response.data);
32188 this.files.push(file);
32192 this.fireEvent('afterupload', this, xhr);
32196 xhrOnError : function(xhr)
32198 Roo.log('xhr on error');
32200 var response = Roo.decode(xhr.responseText);
32207 process : function(file)
32209 if(this.fireEvent('process', this, file) !== false){
32210 if(this.editable && file.type.indexOf('image') != -1){
32211 this.fireEvent('edit', this, file);
32215 this.uploadStart(file, false);
32222 uploadStart : function(file, crop)
32224 this.xhr = new XMLHttpRequest();
32226 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32231 file.xhr = this.xhr;
32233 this.managerEl.createChild({
32235 cls : 'roo-document-manager-loading',
32239 tooltip : file.name,
32240 cls : 'roo-document-manager-thumb',
32241 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32247 this.xhr.open(this.method, this.url, true);
32250 "Accept": "application/json",
32251 "Cache-Control": "no-cache",
32252 "X-Requested-With": "XMLHttpRequest"
32255 for (var headerName in headers) {
32256 var headerValue = headers[headerName];
32258 this.xhr.setRequestHeader(headerName, headerValue);
32264 this.xhr.onload = function()
32266 _this.xhrOnLoad(_this.xhr);
32269 this.xhr.onerror = function()
32271 _this.xhrOnError(_this.xhr);
32274 var formData = new FormData();
32276 formData.append('returnHTML', 'NO');
32279 formData.append('crop', crop);
32282 formData.append(this.paramName, file, file.name);
32289 if(this.fireEvent('prepare', this, formData, options) != false){
32291 if(options.manually){
32295 this.xhr.send(formData);
32299 this.uploadCancel();
32302 uploadCancel : function()
32308 this.delegates = [];
32310 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32317 renderPreview : function(file)
32319 if(typeof(file.target) != 'undefined' && file.target){
32323 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32325 var previewEl = this.managerEl.createChild({
32327 cls : 'roo-document-manager-preview',
32331 tooltip : file[this.toolTipName],
32332 cls : 'roo-document-manager-thumb',
32333 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32338 html : '<i class="fa fa-times-circle"></i>'
32343 var close = previewEl.select('button.close', true).first();
32345 close.on('click', this.onRemove, this, file);
32347 file.target = previewEl;
32349 var image = previewEl.select('img', true).first();
32353 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32355 image.on('click', this.onClick, this, file);
32357 this.fireEvent('previewrendered', this, file);
32363 onPreviewLoad : function(file, image)
32365 if(typeof(file.target) == 'undefined' || !file.target){
32369 var width = image.dom.naturalWidth || image.dom.width;
32370 var height = image.dom.naturalHeight || image.dom.height;
32372 if(!this.previewResize) {
32376 if(width > height){
32377 file.target.addClass('wide');
32381 file.target.addClass('tall');
32386 uploadFromSource : function(file, crop)
32388 this.xhr = new XMLHttpRequest();
32390 this.managerEl.createChild({
32392 cls : 'roo-document-manager-loading',
32396 tooltip : file.name,
32397 cls : 'roo-document-manager-thumb',
32398 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32404 this.xhr.open(this.method, this.url, true);
32407 "Accept": "application/json",
32408 "Cache-Control": "no-cache",
32409 "X-Requested-With": "XMLHttpRequest"
32412 for (var headerName in headers) {
32413 var headerValue = headers[headerName];
32415 this.xhr.setRequestHeader(headerName, headerValue);
32421 this.xhr.onload = function()
32423 _this.xhrOnLoad(_this.xhr);
32426 this.xhr.onerror = function()
32428 _this.xhrOnError(_this.xhr);
32431 var formData = new FormData();
32433 formData.append('returnHTML', 'NO');
32435 formData.append('crop', crop);
32437 if(typeof(file.filename) != 'undefined'){
32438 formData.append('filename', file.filename);
32441 if(typeof(file.mimetype) != 'undefined'){
32442 formData.append('mimetype', file.mimetype);
32447 if(this.fireEvent('prepare', this, formData) != false){
32448 this.xhr.send(formData);
32458 * @class Roo.bootstrap.DocumentViewer
32459 * @extends Roo.bootstrap.Component
32460 * Bootstrap DocumentViewer class
32461 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32462 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32465 * Create a new DocumentViewer
32466 * @param {Object} config The config object
32469 Roo.bootstrap.DocumentViewer = function(config){
32470 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32475 * Fire after initEvent
32476 * @param {Roo.bootstrap.DocumentViewer} this
32482 * @param {Roo.bootstrap.DocumentViewer} this
32487 * Fire after download button
32488 * @param {Roo.bootstrap.DocumentViewer} this
32493 * Fire after trash button
32494 * @param {Roo.bootstrap.DocumentViewer} this
32501 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32503 showDownload : true,
32507 getAutoCreate : function()
32511 cls : 'roo-document-viewer',
32515 cls : 'roo-document-viewer-body',
32519 cls : 'roo-document-viewer-thumb',
32523 cls : 'roo-document-viewer-image'
32531 cls : 'roo-document-viewer-footer',
32534 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32538 cls : 'btn-group roo-document-viewer-download',
32542 cls : 'btn btn-default',
32543 html : '<i class="fa fa-download"></i>'
32549 cls : 'btn-group roo-document-viewer-trash',
32553 cls : 'btn btn-default',
32554 html : '<i class="fa fa-trash"></i>'
32567 initEvents : function()
32569 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32570 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32572 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32573 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32575 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32576 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32578 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32579 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32581 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32582 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32584 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32585 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32587 this.bodyEl.on('click', this.onClick, this);
32588 this.downloadBtn.on('click', this.onDownload, this);
32589 this.trashBtn.on('click', this.onTrash, this);
32591 this.downloadBtn.hide();
32592 this.trashBtn.hide();
32594 if(this.showDownload){
32595 this.downloadBtn.show();
32598 if(this.showTrash){
32599 this.trashBtn.show();
32602 if(!this.showDownload && !this.showTrash) {
32603 this.footerEl.hide();
32608 initial : function()
32610 this.fireEvent('initial', this);
32614 onClick : function(e)
32616 e.preventDefault();
32618 this.fireEvent('click', this);
32621 onDownload : function(e)
32623 e.preventDefault();
32625 this.fireEvent('download', this);
32628 onTrash : function(e)
32630 e.preventDefault();
32632 this.fireEvent('trash', this);
32644 * @class Roo.bootstrap.NavProgressBar
32645 * @extends Roo.bootstrap.Component
32646 * Bootstrap NavProgressBar class
32649 * Create a new nav progress bar
32650 * @param {Object} config The config object
32653 Roo.bootstrap.NavProgressBar = function(config){
32654 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32656 this.bullets = this.bullets || [];
32658 // Roo.bootstrap.NavProgressBar.register(this);
32662 * Fires when the active item changes
32663 * @param {Roo.bootstrap.NavProgressBar} this
32664 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32665 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32672 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32677 getAutoCreate : function()
32679 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32683 cls : 'roo-navigation-bar-group',
32687 cls : 'roo-navigation-top-bar'
32691 cls : 'roo-navigation-bullets-bar',
32695 cls : 'roo-navigation-bar'
32702 cls : 'roo-navigation-bottom-bar'
32712 initEvents: function()
32717 onRender : function(ct, position)
32719 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32721 if(this.bullets.length){
32722 Roo.each(this.bullets, function(b){
32731 addItem : function(cfg)
32733 var item = new Roo.bootstrap.NavProgressItem(cfg);
32735 item.parentId = this.id;
32736 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32739 var top = new Roo.bootstrap.Element({
32741 cls : 'roo-navigation-bar-text'
32744 var bottom = new Roo.bootstrap.Element({
32746 cls : 'roo-navigation-bar-text'
32749 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32750 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32752 var topText = new Roo.bootstrap.Element({
32754 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32757 var bottomText = new Roo.bootstrap.Element({
32759 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32762 topText.onRender(top.el, null);
32763 bottomText.onRender(bottom.el, null);
32766 item.bottomEl = bottom;
32769 this.barItems.push(item);
32774 getActive : function()
32776 var active = false;
32778 Roo.each(this.barItems, function(v){
32780 if (!v.isActive()) {
32792 setActiveItem : function(item)
32796 Roo.each(this.barItems, function(v){
32797 if (v.rid == item.rid) {
32801 if (v.isActive()) {
32802 v.setActive(false);
32807 item.setActive(true);
32809 this.fireEvent('changed', this, item, prev);
32812 getBarItem: function(rid)
32816 Roo.each(this.barItems, function(e) {
32817 if (e.rid != rid) {
32828 indexOfItem : function(item)
32832 Roo.each(this.barItems, function(v, i){
32834 if (v.rid != item.rid) {
32845 setActiveNext : function()
32847 var i = this.indexOfItem(this.getActive());
32849 if (i > this.barItems.length) {
32853 this.setActiveItem(this.barItems[i+1]);
32856 setActivePrev : function()
32858 var i = this.indexOfItem(this.getActive());
32864 this.setActiveItem(this.barItems[i-1]);
32867 format : function()
32869 if(!this.barItems.length){
32873 var width = 100 / this.barItems.length;
32875 Roo.each(this.barItems, function(i){
32876 i.el.setStyle('width', width + '%');
32877 i.topEl.el.setStyle('width', width + '%');
32878 i.bottomEl.el.setStyle('width', width + '%');
32887 * Nav Progress Item
32892 * @class Roo.bootstrap.NavProgressItem
32893 * @extends Roo.bootstrap.Component
32894 * Bootstrap NavProgressItem class
32895 * @cfg {String} rid the reference id
32896 * @cfg {Boolean} active (true|false) Is item active default false
32897 * @cfg {Boolean} disabled (true|false) Is item active default false
32898 * @cfg {String} html
32899 * @cfg {String} position (top|bottom) text position default bottom
32900 * @cfg {String} icon show icon instead of number
32903 * Create a new NavProgressItem
32904 * @param {Object} config The config object
32906 Roo.bootstrap.NavProgressItem = function(config){
32907 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32912 * The raw click event for the entire grid.
32913 * @param {Roo.bootstrap.NavProgressItem} this
32914 * @param {Roo.EventObject} e
32921 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32927 position : 'bottom',
32930 getAutoCreate : function()
32932 var iconCls = 'roo-navigation-bar-item-icon';
32934 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32938 cls: 'roo-navigation-bar-item',
32948 cfg.cls += ' active';
32951 cfg.cls += ' disabled';
32957 disable : function()
32959 this.setDisabled(true);
32962 enable : function()
32964 this.setDisabled(false);
32967 initEvents: function()
32969 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32971 this.iconEl.on('click', this.onClick, this);
32974 onClick : function(e)
32976 e.preventDefault();
32982 if(this.fireEvent('click', this, e) === false){
32986 this.parent().setActiveItem(this);
32989 isActive: function ()
32991 return this.active;
32994 setActive : function(state)
32996 if(this.active == state){
33000 this.active = state;
33003 this.el.addClass('active');
33007 this.el.removeClass('active');
33012 setDisabled : function(state)
33014 if(this.disabled == state){
33018 this.disabled = state;
33021 this.el.addClass('disabled');
33025 this.el.removeClass('disabled');
33028 tooltipEl : function()
33030 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33043 * @class Roo.bootstrap.FieldLabel
33044 * @extends Roo.bootstrap.Component
33045 * Bootstrap FieldLabel class
33046 * @cfg {String} html contents of the element
33047 * @cfg {String} tag tag of the element default label
33048 * @cfg {String} cls class of the element
33049 * @cfg {String} target label target
33050 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33051 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33052 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33053 * @cfg {String} iconTooltip default "This field is required"
33054 * @cfg {String} indicatorpos (left|right) default left
33057 * Create a new FieldLabel
33058 * @param {Object} config The config object
33061 Roo.bootstrap.FieldLabel = function(config){
33062 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33067 * Fires after the field has been marked as invalid.
33068 * @param {Roo.form.FieldLabel} this
33069 * @param {String} msg The validation message
33074 * Fires after the field has been validated with no errors.
33075 * @param {Roo.form.FieldLabel} this
33081 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33088 invalidClass : 'has-warning',
33089 validClass : 'has-success',
33090 iconTooltip : 'This field is required',
33091 indicatorpos : 'left',
33093 getAutoCreate : function(){
33096 if (!this.allowBlank) {
33102 cls : 'roo-bootstrap-field-label ' + this.cls,
33107 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33108 tooltip : this.iconTooltip
33117 if(this.indicatorpos == 'right'){
33120 cls : 'roo-bootstrap-field-label ' + this.cls,
33129 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33130 tooltip : this.iconTooltip
33139 initEvents: function()
33141 Roo.bootstrap.Element.superclass.initEvents.call(this);
33143 this.indicator = this.indicatorEl();
33145 if(this.indicator){
33146 this.indicator.removeClass('visible');
33147 this.indicator.addClass('invisible');
33150 Roo.bootstrap.FieldLabel.register(this);
33153 indicatorEl : function()
33155 var indicator = this.el.select('i.roo-required-indicator',true).first();
33166 * Mark this field as valid
33168 markValid : function()
33170 if(this.indicator){
33171 this.indicator.removeClass('visible');
33172 this.indicator.addClass('invisible');
33174 if (Roo.bootstrap.version == 3) {
33175 this.el.removeClass(this.invalidClass);
33176 this.el.addClass(this.validClass);
33178 this.el.removeClass('is-invalid');
33179 this.el.addClass('is-valid');
33183 this.fireEvent('valid', this);
33187 * Mark this field as invalid
33188 * @param {String} msg The validation message
33190 markInvalid : function(msg)
33192 if(this.indicator){
33193 this.indicator.removeClass('invisible');
33194 this.indicator.addClass('visible');
33196 if (Roo.bootstrap.version == 3) {
33197 this.el.removeClass(this.validClass);
33198 this.el.addClass(this.invalidClass);
33200 this.el.removeClass('is-valid');
33201 this.el.addClass('is-invalid');
33205 this.fireEvent('invalid', this, msg);
33211 Roo.apply(Roo.bootstrap.FieldLabel, {
33216 * register a FieldLabel Group
33217 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33219 register : function(label)
33221 if(this.groups.hasOwnProperty(label.target)){
33225 this.groups[label.target] = label;
33229 * fetch a FieldLabel Group based on the target
33230 * @param {string} target
33231 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33233 get: function(target) {
33234 if (typeof(this.groups[target]) == 'undefined') {
33238 return this.groups[target] ;
33247 * page DateSplitField.
33253 * @class Roo.bootstrap.DateSplitField
33254 * @extends Roo.bootstrap.Component
33255 * Bootstrap DateSplitField class
33256 * @cfg {string} fieldLabel - the label associated
33257 * @cfg {Number} labelWidth set the width of label (0-12)
33258 * @cfg {String} labelAlign (top|left)
33259 * @cfg {Boolean} dayAllowBlank (true|false) default false
33260 * @cfg {Boolean} monthAllowBlank (true|false) default false
33261 * @cfg {Boolean} yearAllowBlank (true|false) default false
33262 * @cfg {string} dayPlaceholder
33263 * @cfg {string} monthPlaceholder
33264 * @cfg {string} yearPlaceholder
33265 * @cfg {string} dayFormat default 'd'
33266 * @cfg {string} monthFormat default 'm'
33267 * @cfg {string} yearFormat default 'Y'
33268 * @cfg {Number} labellg set the width of label (1-12)
33269 * @cfg {Number} labelmd set the width of label (1-12)
33270 * @cfg {Number} labelsm set the width of label (1-12)
33271 * @cfg {Number} labelxs set the width of label (1-12)
33275 * Create a new DateSplitField
33276 * @param {Object} config The config object
33279 Roo.bootstrap.DateSplitField = function(config){
33280 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33286 * getting the data of years
33287 * @param {Roo.bootstrap.DateSplitField} this
33288 * @param {Object} years
33293 * getting the data of days
33294 * @param {Roo.bootstrap.DateSplitField} this
33295 * @param {Object} days
33300 * Fires after the field has been marked as invalid.
33301 * @param {Roo.form.Field} this
33302 * @param {String} msg The validation message
33307 * Fires after the field has been validated with no errors.
33308 * @param {Roo.form.Field} this
33314 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33317 labelAlign : 'top',
33319 dayAllowBlank : false,
33320 monthAllowBlank : false,
33321 yearAllowBlank : false,
33322 dayPlaceholder : '',
33323 monthPlaceholder : '',
33324 yearPlaceholder : '',
33328 isFormField : true,
33334 getAutoCreate : function()
33338 cls : 'row roo-date-split-field-group',
33343 cls : 'form-hidden-field roo-date-split-field-group-value',
33349 var labelCls = 'col-md-12';
33350 var contentCls = 'col-md-4';
33352 if(this.fieldLabel){
33356 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33360 html : this.fieldLabel
33365 if(this.labelAlign == 'left'){
33367 if(this.labelWidth > 12){
33368 label.style = "width: " + this.labelWidth + 'px';
33371 if(this.labelWidth < 13 && this.labelmd == 0){
33372 this.labelmd = this.labelWidth;
33375 if(this.labellg > 0){
33376 labelCls = ' col-lg-' + this.labellg;
33377 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33380 if(this.labelmd > 0){
33381 labelCls = ' col-md-' + this.labelmd;
33382 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33385 if(this.labelsm > 0){
33386 labelCls = ' col-sm-' + this.labelsm;
33387 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33390 if(this.labelxs > 0){
33391 labelCls = ' col-xs-' + this.labelxs;
33392 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33396 label.cls += ' ' + labelCls;
33398 cfg.cn.push(label);
33401 Roo.each(['day', 'month', 'year'], function(t){
33404 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33411 inputEl: function ()
33413 return this.el.select('.roo-date-split-field-group-value', true).first();
33416 onRender : function(ct, position)
33420 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33422 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33424 this.dayField = new Roo.bootstrap.ComboBox({
33425 allowBlank : this.dayAllowBlank,
33426 alwaysQuery : true,
33427 displayField : 'value',
33430 forceSelection : true,
33432 placeholder : this.dayPlaceholder,
33433 selectOnFocus : true,
33434 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33435 triggerAction : 'all',
33437 valueField : 'value',
33438 store : new Roo.data.SimpleStore({
33439 data : (function() {
33441 _this.fireEvent('days', _this, days);
33444 fields : [ 'value' ]
33447 select : function (_self, record, index)
33449 _this.setValue(_this.getValue());
33454 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33456 this.monthField = new Roo.bootstrap.MonthField({
33457 after : '<i class=\"fa fa-calendar\"></i>',
33458 allowBlank : this.monthAllowBlank,
33459 placeholder : this.monthPlaceholder,
33462 render : function (_self)
33464 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33465 e.preventDefault();
33469 select : function (_self, oldvalue, newvalue)
33471 _this.setValue(_this.getValue());
33476 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33478 this.yearField = new Roo.bootstrap.ComboBox({
33479 allowBlank : this.yearAllowBlank,
33480 alwaysQuery : true,
33481 displayField : 'value',
33484 forceSelection : true,
33486 placeholder : this.yearPlaceholder,
33487 selectOnFocus : true,
33488 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33489 triggerAction : 'all',
33491 valueField : 'value',
33492 store : new Roo.data.SimpleStore({
33493 data : (function() {
33495 _this.fireEvent('years', _this, years);
33498 fields : [ 'value' ]
33501 select : function (_self, record, index)
33503 _this.setValue(_this.getValue());
33508 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33511 setValue : function(v, format)
33513 this.inputEl.dom.value = v;
33515 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33517 var d = Date.parseDate(v, f);
33524 this.setDay(d.format(this.dayFormat));
33525 this.setMonth(d.format(this.monthFormat));
33526 this.setYear(d.format(this.yearFormat));
33533 setDay : function(v)
33535 this.dayField.setValue(v);
33536 this.inputEl.dom.value = this.getValue();
33541 setMonth : function(v)
33543 this.monthField.setValue(v, true);
33544 this.inputEl.dom.value = this.getValue();
33549 setYear : function(v)
33551 this.yearField.setValue(v);
33552 this.inputEl.dom.value = this.getValue();
33557 getDay : function()
33559 return this.dayField.getValue();
33562 getMonth : function()
33564 return this.monthField.getValue();
33567 getYear : function()
33569 return this.yearField.getValue();
33572 getValue : function()
33574 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33576 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33586 this.inputEl.dom.value = '';
33591 validate : function()
33593 var d = this.dayField.validate();
33594 var m = this.monthField.validate();
33595 var y = this.yearField.validate();
33600 (!this.dayAllowBlank && !d) ||
33601 (!this.monthAllowBlank && !m) ||
33602 (!this.yearAllowBlank && !y)
33607 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33616 this.markInvalid();
33621 markValid : function()
33624 var label = this.el.select('label', true).first();
33625 var icon = this.el.select('i.fa-star', true).first();
33631 this.fireEvent('valid', this);
33635 * Mark this field as invalid
33636 * @param {String} msg The validation message
33638 markInvalid : function(msg)
33641 var label = this.el.select('label', true).first();
33642 var icon = this.el.select('i.fa-star', true).first();
33644 if(label && !icon){
33645 this.el.select('.roo-date-split-field-label', true).createChild({
33647 cls : 'text-danger fa fa-lg fa-star',
33648 tooltip : 'This field is required',
33649 style : 'margin-right:5px;'
33653 this.fireEvent('invalid', this, msg);
33656 clearInvalid : function()
33658 var label = this.el.select('label', true).first();
33659 var icon = this.el.select('i.fa-star', true).first();
33665 this.fireEvent('valid', this);
33668 getName: function()
33678 * http://masonry.desandro.com
33680 * The idea is to render all the bricks based on vertical width...
33682 * The original code extends 'outlayer' - we might need to use that....
33688 * @class Roo.bootstrap.LayoutMasonry
33689 * @extends Roo.bootstrap.Component
33690 * Bootstrap Layout Masonry class
33693 * Create a new Element
33694 * @param {Object} config The config object
33697 Roo.bootstrap.LayoutMasonry = function(config){
33699 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33703 Roo.bootstrap.LayoutMasonry.register(this);
33709 * Fire after layout the items
33710 * @param {Roo.bootstrap.LayoutMasonry} this
33711 * @param {Roo.EventObject} e
33718 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33721 * @cfg {Boolean} isLayoutInstant = no animation?
33723 isLayoutInstant : false, // needed?
33726 * @cfg {Number} boxWidth width of the columns
33731 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33736 * @cfg {Number} padWidth padding below box..
33741 * @cfg {Number} gutter gutter width..
33746 * @cfg {Number} maxCols maximum number of columns
33752 * @cfg {Boolean} isAutoInitial defalut true
33754 isAutoInitial : true,
33759 * @cfg {Boolean} isHorizontal defalut false
33761 isHorizontal : false,
33763 currentSize : null,
33769 bricks: null, //CompositeElement
33773 _isLayoutInited : false,
33775 // isAlternative : false, // only use for vertical layout...
33778 * @cfg {Number} alternativePadWidth padding below box..
33780 alternativePadWidth : 50,
33782 selectedBrick : [],
33784 getAutoCreate : function(){
33786 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33790 cls: 'blog-masonary-wrapper ' + this.cls,
33792 cls : 'mas-boxes masonary'
33799 getChildContainer: function( )
33801 if (this.boxesEl) {
33802 return this.boxesEl;
33805 this.boxesEl = this.el.select('.mas-boxes').first();
33807 return this.boxesEl;
33811 initEvents : function()
33815 if(this.isAutoInitial){
33816 Roo.log('hook children rendered');
33817 this.on('childrenrendered', function() {
33818 Roo.log('children rendered');
33824 initial : function()
33826 this.selectedBrick = [];
33828 this.currentSize = this.el.getBox(true);
33830 Roo.EventManager.onWindowResize(this.resize, this);
33832 if(!this.isAutoInitial){
33840 //this.layout.defer(500,this);
33844 resize : function()
33846 var cs = this.el.getBox(true);
33849 this.currentSize.width == cs.width &&
33850 this.currentSize.x == cs.x &&
33851 this.currentSize.height == cs.height &&
33852 this.currentSize.y == cs.y
33854 Roo.log("no change in with or X or Y");
33858 this.currentSize = cs;
33864 layout : function()
33866 this._resetLayout();
33868 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33870 this.layoutItems( isInstant );
33872 this._isLayoutInited = true;
33874 this.fireEvent('layout', this);
33878 _resetLayout : function()
33880 if(this.isHorizontal){
33881 this.horizontalMeasureColumns();
33885 this.verticalMeasureColumns();
33889 verticalMeasureColumns : function()
33891 this.getContainerWidth();
33893 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33894 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33898 var boxWidth = this.boxWidth + this.padWidth;
33900 if(this.containerWidth < this.boxWidth){
33901 boxWidth = this.containerWidth
33904 var containerWidth = this.containerWidth;
33906 var cols = Math.floor(containerWidth / boxWidth);
33908 this.cols = Math.max( cols, 1 );
33910 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33912 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33914 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33916 this.colWidth = boxWidth + avail - this.padWidth;
33918 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33919 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33922 horizontalMeasureColumns : function()
33924 this.getContainerWidth();
33926 var boxWidth = this.boxWidth;
33928 if(this.containerWidth < boxWidth){
33929 boxWidth = this.containerWidth;
33932 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33934 this.el.setHeight(boxWidth);
33938 getContainerWidth : function()
33940 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33943 layoutItems : function( isInstant )
33945 Roo.log(this.bricks);
33947 var items = Roo.apply([], this.bricks);
33949 if(this.isHorizontal){
33950 this._horizontalLayoutItems( items , isInstant );
33954 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33955 // this._verticalAlternativeLayoutItems( items , isInstant );
33959 this._verticalLayoutItems( items , isInstant );
33963 _verticalLayoutItems : function ( items , isInstant)
33965 if ( !items || !items.length ) {
33970 ['xs', 'xs', 'xs', 'tall'],
33971 ['xs', 'xs', 'tall'],
33972 ['xs', 'xs', 'sm'],
33973 ['xs', 'xs', 'xs'],
33979 ['sm', 'xs', 'xs'],
33983 ['tall', 'xs', 'xs', 'xs'],
33984 ['tall', 'xs', 'xs'],
33996 Roo.each(items, function(item, k){
33998 switch (item.size) {
33999 // these layouts take up a full box,
34010 boxes.push([item]);
34033 var filterPattern = function(box, length)
34041 var pattern = box.slice(0, length);
34045 Roo.each(pattern, function(i){
34046 format.push(i.size);
34049 Roo.each(standard, function(s){
34051 if(String(s) != String(format)){
34060 if(!match && length == 1){
34065 filterPattern(box, length - 1);
34069 queue.push(pattern);
34071 box = box.slice(length, box.length);
34073 filterPattern(box, 4);
34079 Roo.each(boxes, function(box, k){
34085 if(box.length == 1){
34090 filterPattern(box, 4);
34094 this._processVerticalLayoutQueue( queue, isInstant );
34098 // _verticalAlternativeLayoutItems : function( items , isInstant )
34100 // if ( !items || !items.length ) {
34104 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34108 _horizontalLayoutItems : function ( items , isInstant)
34110 if ( !items || !items.length || items.length < 3) {
34116 var eItems = items.slice(0, 3);
34118 items = items.slice(3, items.length);
34121 ['xs', 'xs', 'xs', 'wide'],
34122 ['xs', 'xs', 'wide'],
34123 ['xs', 'xs', 'sm'],
34124 ['xs', 'xs', 'xs'],
34130 ['sm', 'xs', 'xs'],
34134 ['wide', 'xs', 'xs', 'xs'],
34135 ['wide', 'xs', 'xs'],
34148 Roo.each(items, function(item, k){
34150 switch (item.size) {
34161 boxes.push([item]);
34185 var filterPattern = function(box, length)
34193 var pattern = box.slice(0, length);
34197 Roo.each(pattern, function(i){
34198 format.push(i.size);
34201 Roo.each(standard, function(s){
34203 if(String(s) != String(format)){
34212 if(!match && length == 1){
34217 filterPattern(box, length - 1);
34221 queue.push(pattern);
34223 box = box.slice(length, box.length);
34225 filterPattern(box, 4);
34231 Roo.each(boxes, function(box, k){
34237 if(box.length == 1){
34242 filterPattern(box, 4);
34249 var pos = this.el.getBox(true);
34253 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34255 var hit_end = false;
34257 Roo.each(queue, function(box){
34261 Roo.each(box, function(b){
34263 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34273 Roo.each(box, function(b){
34275 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34278 mx = Math.max(mx, b.x);
34282 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34286 Roo.each(box, function(b){
34288 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34302 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34305 /** Sets position of item in DOM
34306 * @param {Element} item
34307 * @param {Number} x - horizontal position
34308 * @param {Number} y - vertical position
34309 * @param {Boolean} isInstant - disables transitions
34311 _processVerticalLayoutQueue : function( queue, isInstant )
34313 var pos = this.el.getBox(true);
34318 for (var i = 0; i < this.cols; i++){
34322 Roo.each(queue, function(box, k){
34324 var col = k % this.cols;
34326 Roo.each(box, function(b,kk){
34328 b.el.position('absolute');
34330 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34331 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34333 if(b.size == 'md-left' || b.size == 'md-right'){
34334 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34335 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34338 b.el.setWidth(width);
34339 b.el.setHeight(height);
34341 b.el.select('iframe',true).setSize(width,height);
34345 for (var i = 0; i < this.cols; i++){
34347 if(maxY[i] < maxY[col]){
34352 col = Math.min(col, i);
34356 x = pos.x + col * (this.colWidth + this.padWidth);
34360 var positions = [];
34362 switch (box.length){
34364 positions = this.getVerticalOneBoxColPositions(x, y, box);
34367 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34370 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34373 positions = this.getVerticalFourBoxColPositions(x, y, box);
34379 Roo.each(box, function(b,kk){
34381 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34383 var sz = b.el.getSize();
34385 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34393 for (var i = 0; i < this.cols; i++){
34394 mY = Math.max(mY, maxY[i]);
34397 this.el.setHeight(mY - pos.y);
34401 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34403 // var pos = this.el.getBox(true);
34406 // var maxX = pos.right;
34408 // var maxHeight = 0;
34410 // Roo.each(items, function(item, k){
34414 // item.el.position('absolute');
34416 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34418 // item.el.setWidth(width);
34420 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34422 // item.el.setHeight(height);
34425 // item.el.setXY([x, y], isInstant ? false : true);
34427 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34430 // y = y + height + this.alternativePadWidth;
34432 // maxHeight = maxHeight + height + this.alternativePadWidth;
34436 // this.el.setHeight(maxHeight);
34440 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34442 var pos = this.el.getBox(true);
34447 var maxX = pos.right;
34449 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34451 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34453 Roo.each(queue, function(box, k){
34455 Roo.each(box, function(b, kk){
34457 b.el.position('absolute');
34459 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34460 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34462 if(b.size == 'md-left' || b.size == 'md-right'){
34463 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34464 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34467 b.el.setWidth(width);
34468 b.el.setHeight(height);
34476 var positions = [];
34478 switch (box.length){
34480 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34483 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34486 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34489 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34495 Roo.each(box, function(b,kk){
34497 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34499 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34507 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34509 Roo.each(eItems, function(b,k){
34511 b.size = (k == 0) ? 'sm' : 'xs';
34512 b.x = (k == 0) ? 2 : 1;
34513 b.y = (k == 0) ? 2 : 1;
34515 b.el.position('absolute');
34517 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34519 b.el.setWidth(width);
34521 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34523 b.el.setHeight(height);
34527 var positions = [];
34530 x : maxX - this.unitWidth * 2 - this.gutter,
34535 x : maxX - this.unitWidth,
34536 y : minY + (this.unitWidth + this.gutter) * 2
34540 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34544 Roo.each(eItems, function(b,k){
34546 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34552 getVerticalOneBoxColPositions : function(x, y, box)
34556 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34558 if(box[0].size == 'md-left'){
34562 if(box[0].size == 'md-right'){
34567 x : x + (this.unitWidth + this.gutter) * rand,
34574 getVerticalTwoBoxColPositions : function(x, y, box)
34578 if(box[0].size == 'xs'){
34582 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34586 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34600 x : x + (this.unitWidth + this.gutter) * 2,
34601 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34608 getVerticalThreeBoxColPositions : function(x, y, box)
34612 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34620 x : x + (this.unitWidth + this.gutter) * 1,
34625 x : x + (this.unitWidth + this.gutter) * 2,
34633 if(box[0].size == 'xs' && box[1].size == 'xs'){
34642 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34646 x : x + (this.unitWidth + this.gutter) * 1,
34660 x : x + (this.unitWidth + this.gutter) * 2,
34665 x : x + (this.unitWidth + this.gutter) * 2,
34666 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34673 getVerticalFourBoxColPositions : function(x, y, box)
34677 if(box[0].size == 'xs'){
34686 y : y + (this.unitHeight + this.gutter) * 1
34691 y : y + (this.unitHeight + this.gutter) * 2
34695 x : x + (this.unitWidth + this.gutter) * 1,
34709 x : x + (this.unitWidth + this.gutter) * 2,
34714 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34715 y : y + (this.unitHeight + this.gutter) * 1
34719 x : x + (this.unitWidth + this.gutter) * 2,
34720 y : y + (this.unitWidth + this.gutter) * 2
34727 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34731 if(box[0].size == 'md-left'){
34733 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34740 if(box[0].size == 'md-right'){
34742 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34743 y : minY + (this.unitWidth + this.gutter) * 1
34749 var rand = Math.floor(Math.random() * (4 - box[0].y));
34752 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34753 y : minY + (this.unitWidth + this.gutter) * rand
34760 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34764 if(box[0].size == 'xs'){
34767 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34772 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34773 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34786 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34787 y : minY + (this.unitWidth + this.gutter) * 2
34794 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34798 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34801 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34806 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34807 y : minY + (this.unitWidth + this.gutter) * 1
34811 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34812 y : minY + (this.unitWidth + this.gutter) * 2
34819 if(box[0].size == 'xs' && box[1].size == 'xs'){
34822 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34827 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34832 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34833 y : minY + (this.unitWidth + this.gutter) * 1
34841 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34846 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34847 y : minY + (this.unitWidth + this.gutter) * 2
34851 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34852 y : minY + (this.unitWidth + this.gutter) * 2
34859 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34863 if(box[0].size == 'xs'){
34866 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34871 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34876 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),
34881 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34882 y : minY + (this.unitWidth + this.gutter) * 1
34890 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34896 y : minY + (this.unitWidth + this.gutter) * 2
34900 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34901 y : minY + (this.unitWidth + this.gutter) * 2
34905 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),
34906 y : minY + (this.unitWidth + this.gutter) * 2
34914 * remove a Masonry Brick
34915 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34917 removeBrick : function(brick_id)
34923 for (var i = 0; i<this.bricks.length; i++) {
34924 if (this.bricks[i].id == brick_id) {
34925 this.bricks.splice(i,1);
34926 this.el.dom.removeChild(Roo.get(brick_id).dom);
34933 * adds a Masonry Brick
34934 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34936 addBrick : function(cfg)
34938 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34939 //this.register(cn);
34940 cn.parentId = this.id;
34941 cn.render(this.el);
34946 * register a Masonry Brick
34947 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34950 register : function(brick)
34952 this.bricks.push(brick);
34953 brick.masonryId = this.id;
34957 * clear all the Masonry Brick
34959 clearAll : function()
34962 //this.getChildContainer().dom.innerHTML = "";
34963 this.el.dom.innerHTML = '';
34966 getSelected : function()
34968 if (!this.selectedBrick) {
34972 return this.selectedBrick;
34976 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34980 * register a Masonry Layout
34981 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34984 register : function(layout)
34986 this.groups[layout.id] = layout;
34989 * fetch a Masonry Layout based on the masonry layout ID
34990 * @param {string} the masonry layout to add
34991 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34994 get: function(layout_id) {
34995 if (typeof(this.groups[layout_id]) == 'undefined') {
34998 return this.groups[layout_id] ;
35010 * http://masonry.desandro.com
35012 * The idea is to render all the bricks based on vertical width...
35014 * The original code extends 'outlayer' - we might need to use that....
35020 * @class Roo.bootstrap.LayoutMasonryAuto
35021 * @extends Roo.bootstrap.Component
35022 * Bootstrap Layout Masonry class
35025 * Create a new Element
35026 * @param {Object} config The config object
35029 Roo.bootstrap.LayoutMasonryAuto = function(config){
35030 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35033 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35036 * @cfg {Boolean} isFitWidth - resize the width..
35038 isFitWidth : false, // options..
35040 * @cfg {Boolean} isOriginLeft = left align?
35042 isOriginLeft : true,
35044 * @cfg {Boolean} isOriginTop = top align?
35046 isOriginTop : false,
35048 * @cfg {Boolean} isLayoutInstant = no animation?
35050 isLayoutInstant : false, // needed?
35052 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35054 isResizingContainer : true,
35056 * @cfg {Number} columnWidth width of the columns
35062 * @cfg {Number} maxCols maximum number of columns
35067 * @cfg {Number} padHeight padding below box..
35073 * @cfg {Boolean} isAutoInitial defalut true
35076 isAutoInitial : true,
35082 initialColumnWidth : 0,
35083 currentSize : null,
35085 colYs : null, // array.
35092 bricks: null, //CompositeElement
35093 cols : 0, // array?
35094 // element : null, // wrapped now this.el
35095 _isLayoutInited : null,
35098 getAutoCreate : function(){
35102 cls: 'blog-masonary-wrapper ' + this.cls,
35104 cls : 'mas-boxes masonary'
35111 getChildContainer: function( )
35113 if (this.boxesEl) {
35114 return this.boxesEl;
35117 this.boxesEl = this.el.select('.mas-boxes').first();
35119 return this.boxesEl;
35123 initEvents : function()
35127 if(this.isAutoInitial){
35128 Roo.log('hook children rendered');
35129 this.on('childrenrendered', function() {
35130 Roo.log('children rendered');
35137 initial : function()
35139 this.reloadItems();
35141 this.currentSize = this.el.getBox(true);
35143 /// was window resize... - let's see if this works..
35144 Roo.EventManager.onWindowResize(this.resize, this);
35146 if(!this.isAutoInitial){
35151 this.layout.defer(500,this);
35154 reloadItems: function()
35156 this.bricks = this.el.select('.masonry-brick', true);
35158 this.bricks.each(function(b) {
35159 //Roo.log(b.getSize());
35160 if (!b.attr('originalwidth')) {
35161 b.attr('originalwidth', b.getSize().width);
35166 Roo.log(this.bricks.elements.length);
35169 resize : function()
35172 var cs = this.el.getBox(true);
35174 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35175 Roo.log("no change in with or X");
35178 this.currentSize = cs;
35182 layout : function()
35185 this._resetLayout();
35186 //this._manageStamps();
35188 // don't animate first layout
35189 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35190 this.layoutItems( isInstant );
35192 // flag for initalized
35193 this._isLayoutInited = true;
35196 layoutItems : function( isInstant )
35198 //var items = this._getItemsForLayout( this.items );
35199 // original code supports filtering layout items.. we just ignore it..
35201 this._layoutItems( this.bricks , isInstant );
35203 this._postLayout();
35205 _layoutItems : function ( items , isInstant)
35207 //this.fireEvent( 'layout', this, items );
35210 if ( !items || !items.elements.length ) {
35211 // no items, emit event with empty array
35216 items.each(function(item) {
35217 Roo.log("layout item");
35219 // get x/y object from method
35220 var position = this._getItemLayoutPosition( item );
35222 position.item = item;
35223 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35224 queue.push( position );
35227 this._processLayoutQueue( queue );
35229 /** Sets position of item in DOM
35230 * @param {Element} item
35231 * @param {Number} x - horizontal position
35232 * @param {Number} y - vertical position
35233 * @param {Boolean} isInstant - disables transitions
35235 _processLayoutQueue : function( queue )
35237 for ( var i=0, len = queue.length; i < len; i++ ) {
35238 var obj = queue[i];
35239 obj.item.position('absolute');
35240 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35246 * Any logic you want to do after each layout,
35247 * i.e. size the container
35249 _postLayout : function()
35251 this.resizeContainer();
35254 resizeContainer : function()
35256 if ( !this.isResizingContainer ) {
35259 var size = this._getContainerSize();
35261 this.el.setSize(size.width,size.height);
35262 this.boxesEl.setSize(size.width,size.height);
35268 _resetLayout : function()
35270 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35271 this.colWidth = this.el.getWidth();
35272 //this.gutter = this.el.getWidth();
35274 this.measureColumns();
35280 this.colYs.push( 0 );
35286 measureColumns : function()
35288 this.getContainerWidth();
35289 // if columnWidth is 0, default to outerWidth of first item
35290 if ( !this.columnWidth ) {
35291 var firstItem = this.bricks.first();
35292 Roo.log(firstItem);
35293 this.columnWidth = this.containerWidth;
35294 if (firstItem && firstItem.attr('originalwidth') ) {
35295 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35297 // columnWidth fall back to item of first element
35298 Roo.log("set column width?");
35299 this.initialColumnWidth = this.columnWidth ;
35301 // if first elem has no width, default to size of container
35306 if (this.initialColumnWidth) {
35307 this.columnWidth = this.initialColumnWidth;
35312 // column width is fixed at the top - however if container width get's smaller we should
35315 // this bit calcs how man columns..
35317 var columnWidth = this.columnWidth += this.gutter;
35319 // calculate columns
35320 var containerWidth = this.containerWidth + this.gutter;
35322 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35323 // fix rounding errors, typically with gutters
35324 var excess = columnWidth - containerWidth % columnWidth;
35327 // if overshoot is less than a pixel, round up, otherwise floor it
35328 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35329 cols = Math[ mathMethod ]( cols );
35330 this.cols = Math.max( cols, 1 );
35331 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35333 // padding positioning..
35334 var totalColWidth = this.cols * this.columnWidth;
35335 var padavail = this.containerWidth - totalColWidth;
35336 // so for 2 columns - we need 3 'pads'
35338 var padNeeded = (1+this.cols) * this.padWidth;
35340 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35342 this.columnWidth += padExtra
35343 //this.padWidth = Math.floor(padavail / ( this.cols));
35345 // adjust colum width so that padding is fixed??
35347 // we have 3 columns ... total = width * 3
35348 // we have X left over... that should be used by
35350 //if (this.expandC) {
35358 getContainerWidth : function()
35360 /* // container is parent if fit width
35361 var container = this.isFitWidth ? this.element.parentNode : this.element;
35362 // check that this.size and size are there
35363 // IE8 triggers resize on body size change, so they might not be
35365 var size = getSize( container ); //FIXME
35366 this.containerWidth = size && size.innerWidth; //FIXME
35369 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35373 _getItemLayoutPosition : function( item ) // what is item?
35375 // we resize the item to our columnWidth..
35377 item.setWidth(this.columnWidth);
35378 item.autoBoxAdjust = false;
35380 var sz = item.getSize();
35382 // how many columns does this brick span
35383 var remainder = this.containerWidth % this.columnWidth;
35385 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35386 // round if off by 1 pixel, otherwise use ceil
35387 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35388 colSpan = Math.min( colSpan, this.cols );
35390 // normally this should be '1' as we dont' currently allow multi width columns..
35392 var colGroup = this._getColGroup( colSpan );
35393 // get the minimum Y value from the columns
35394 var minimumY = Math.min.apply( Math, colGroup );
35395 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35397 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35399 // position the brick
35401 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35402 y: this.currentSize.y + minimumY + this.padHeight
35406 // apply setHeight to necessary columns
35407 var setHeight = minimumY + sz.height + this.padHeight;
35408 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35410 var setSpan = this.cols + 1 - colGroup.length;
35411 for ( var i = 0; i < setSpan; i++ ) {
35412 this.colYs[ shortColIndex + i ] = setHeight ;
35419 * @param {Number} colSpan - number of columns the element spans
35420 * @returns {Array} colGroup
35422 _getColGroup : function( colSpan )
35424 if ( colSpan < 2 ) {
35425 // if brick spans only one column, use all the column Ys
35430 // how many different places could this brick fit horizontally
35431 var groupCount = this.cols + 1 - colSpan;
35432 // for each group potential horizontal position
35433 for ( var i = 0; i < groupCount; i++ ) {
35434 // make an array of colY values for that one group
35435 var groupColYs = this.colYs.slice( i, i + colSpan );
35436 // and get the max value of the array
35437 colGroup[i] = Math.max.apply( Math, groupColYs );
35442 _manageStamp : function( stamp )
35444 var stampSize = stamp.getSize();
35445 var offset = stamp.getBox();
35446 // get the columns that this stamp affects
35447 var firstX = this.isOriginLeft ? offset.x : offset.right;
35448 var lastX = firstX + stampSize.width;
35449 var firstCol = Math.floor( firstX / this.columnWidth );
35450 firstCol = Math.max( 0, firstCol );
35452 var lastCol = Math.floor( lastX / this.columnWidth );
35453 // lastCol should not go over if multiple of columnWidth #425
35454 lastCol -= lastX % this.columnWidth ? 0 : 1;
35455 lastCol = Math.min( this.cols - 1, lastCol );
35457 // set colYs to bottom of the stamp
35458 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35461 for ( var i = firstCol; i <= lastCol; i++ ) {
35462 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35467 _getContainerSize : function()
35469 this.maxY = Math.max.apply( Math, this.colYs );
35474 if ( this.isFitWidth ) {
35475 size.width = this._getContainerFitWidth();
35481 _getContainerFitWidth : function()
35483 var unusedCols = 0;
35484 // count unused columns
35487 if ( this.colYs[i] !== 0 ) {
35492 // fit container to columns that have been used
35493 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35496 needsResizeLayout : function()
35498 var previousWidth = this.containerWidth;
35499 this.getContainerWidth();
35500 return previousWidth !== this.containerWidth;
35515 * @class Roo.bootstrap.MasonryBrick
35516 * @extends Roo.bootstrap.Component
35517 * Bootstrap MasonryBrick class
35520 * Create a new MasonryBrick
35521 * @param {Object} config The config object
35524 Roo.bootstrap.MasonryBrick = function(config){
35526 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35528 Roo.bootstrap.MasonryBrick.register(this);
35534 * When a MasonryBrick is clcik
35535 * @param {Roo.bootstrap.MasonryBrick} this
35536 * @param {Roo.EventObject} e
35542 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35545 * @cfg {String} title
35549 * @cfg {String} html
35553 * @cfg {String} bgimage
35557 * @cfg {String} videourl
35561 * @cfg {String} cls
35565 * @cfg {String} href
35569 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35574 * @cfg {String} placetitle (center|bottom)
35579 * @cfg {Boolean} isFitContainer defalut true
35581 isFitContainer : true,
35584 * @cfg {Boolean} preventDefault defalut false
35586 preventDefault : false,
35589 * @cfg {Boolean} inverse defalut false
35591 maskInverse : false,
35593 getAutoCreate : function()
35595 if(!this.isFitContainer){
35596 return this.getSplitAutoCreate();
35599 var cls = 'masonry-brick masonry-brick-full';
35601 if(this.href.length){
35602 cls += ' masonry-brick-link';
35605 if(this.bgimage.length){
35606 cls += ' masonry-brick-image';
35609 if(this.maskInverse){
35610 cls += ' mask-inverse';
35613 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35614 cls += ' enable-mask';
35618 cls += ' masonry-' + this.size + '-brick';
35621 if(this.placetitle.length){
35623 switch (this.placetitle) {
35625 cls += ' masonry-center-title';
35628 cls += ' masonry-bottom-title';
35635 if(!this.html.length && !this.bgimage.length){
35636 cls += ' masonry-center-title';
35639 if(!this.html.length && this.bgimage.length){
35640 cls += ' masonry-bottom-title';
35645 cls += ' ' + this.cls;
35649 tag: (this.href.length) ? 'a' : 'div',
35654 cls: 'masonry-brick-mask'
35658 cls: 'masonry-brick-paragraph',
35664 if(this.href.length){
35665 cfg.href = this.href;
35668 var cn = cfg.cn[1].cn;
35670 if(this.title.length){
35673 cls: 'masonry-brick-title',
35678 if(this.html.length){
35681 cls: 'masonry-brick-text',
35686 if (!this.title.length && !this.html.length) {
35687 cfg.cn[1].cls += ' hide';
35690 if(this.bgimage.length){
35693 cls: 'masonry-brick-image-view',
35698 if(this.videourl.length){
35699 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35700 // youtube support only?
35703 cls: 'masonry-brick-image-view',
35706 allowfullscreen : true
35714 getSplitAutoCreate : function()
35716 var cls = 'masonry-brick masonry-brick-split';
35718 if(this.href.length){
35719 cls += ' masonry-brick-link';
35722 if(this.bgimage.length){
35723 cls += ' masonry-brick-image';
35727 cls += ' masonry-' + this.size + '-brick';
35730 switch (this.placetitle) {
35732 cls += ' masonry-center-title';
35735 cls += ' masonry-bottom-title';
35738 if(!this.bgimage.length){
35739 cls += ' masonry-center-title';
35742 if(this.bgimage.length){
35743 cls += ' masonry-bottom-title';
35749 cls += ' ' + this.cls;
35753 tag: (this.href.length) ? 'a' : 'div',
35758 cls: 'masonry-brick-split-head',
35762 cls: 'masonry-brick-paragraph',
35769 cls: 'masonry-brick-split-body',
35775 if(this.href.length){
35776 cfg.href = this.href;
35779 if(this.title.length){
35780 cfg.cn[0].cn[0].cn.push({
35782 cls: 'masonry-brick-title',
35787 if(this.html.length){
35788 cfg.cn[1].cn.push({
35790 cls: 'masonry-brick-text',
35795 if(this.bgimage.length){
35796 cfg.cn[0].cn.push({
35798 cls: 'masonry-brick-image-view',
35803 if(this.videourl.length){
35804 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35805 // youtube support only?
35806 cfg.cn[0].cn.cn.push({
35808 cls: 'masonry-brick-image-view',
35811 allowfullscreen : true
35818 initEvents: function()
35820 switch (this.size) {
35853 this.el.on('touchstart', this.onTouchStart, this);
35854 this.el.on('touchmove', this.onTouchMove, this);
35855 this.el.on('touchend', this.onTouchEnd, this);
35856 this.el.on('contextmenu', this.onContextMenu, this);
35858 this.el.on('mouseenter' ,this.enter, this);
35859 this.el.on('mouseleave', this.leave, this);
35860 this.el.on('click', this.onClick, this);
35863 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35864 this.parent().bricks.push(this);
35869 onClick: function(e, el)
35871 var time = this.endTimer - this.startTimer;
35872 // Roo.log(e.preventDefault());
35875 e.preventDefault();
35880 if(!this.preventDefault){
35884 e.preventDefault();
35886 if (this.activeClass != '') {
35887 this.selectBrick();
35890 this.fireEvent('click', this, e);
35893 enter: function(e, el)
35895 e.preventDefault();
35897 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35901 if(this.bgimage.length && this.html.length){
35902 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35906 leave: function(e, el)
35908 e.preventDefault();
35910 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35914 if(this.bgimage.length && this.html.length){
35915 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35919 onTouchStart: function(e, el)
35921 // e.preventDefault();
35923 this.touchmoved = false;
35925 if(!this.isFitContainer){
35929 if(!this.bgimage.length || !this.html.length){
35933 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35935 this.timer = new Date().getTime();
35939 onTouchMove: function(e, el)
35941 this.touchmoved = true;
35944 onContextMenu : function(e,el)
35946 e.preventDefault();
35947 e.stopPropagation();
35951 onTouchEnd: function(e, el)
35953 // e.preventDefault();
35955 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35962 if(!this.bgimage.length || !this.html.length){
35964 if(this.href.length){
35965 window.location.href = this.href;
35971 if(!this.isFitContainer){
35975 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35977 window.location.href = this.href;
35980 //selection on single brick only
35981 selectBrick : function() {
35983 if (!this.parentId) {
35987 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35988 var index = m.selectedBrick.indexOf(this.id);
35991 m.selectedBrick.splice(index,1);
35992 this.el.removeClass(this.activeClass);
35996 for(var i = 0; i < m.selectedBrick.length; i++) {
35997 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35998 b.el.removeClass(b.activeClass);
36001 m.selectedBrick = [];
36003 m.selectedBrick.push(this.id);
36004 this.el.addClass(this.activeClass);
36008 isSelected : function(){
36009 return this.el.hasClass(this.activeClass);
36014 Roo.apply(Roo.bootstrap.MasonryBrick, {
36017 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36019 * register a Masonry Brick
36020 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36023 register : function(brick)
36025 //this.groups[brick.id] = brick;
36026 this.groups.add(brick.id, brick);
36029 * fetch a masonry brick based on the masonry brick ID
36030 * @param {string} the masonry brick to add
36031 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36034 get: function(brick_id)
36036 // if (typeof(this.groups[brick_id]) == 'undefined') {
36039 // return this.groups[brick_id] ;
36041 if(this.groups.key(brick_id)) {
36042 return this.groups.key(brick_id);
36060 * @class Roo.bootstrap.Brick
36061 * @extends Roo.bootstrap.Component
36062 * Bootstrap Brick class
36065 * Create a new Brick
36066 * @param {Object} config The config object
36069 Roo.bootstrap.Brick = function(config){
36070 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36076 * When a Brick is click
36077 * @param {Roo.bootstrap.Brick} this
36078 * @param {Roo.EventObject} e
36084 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36087 * @cfg {String} title
36091 * @cfg {String} html
36095 * @cfg {String} bgimage
36099 * @cfg {String} cls
36103 * @cfg {String} href
36107 * @cfg {String} video
36111 * @cfg {Boolean} square
36115 getAutoCreate : function()
36117 var cls = 'roo-brick';
36119 if(this.href.length){
36120 cls += ' roo-brick-link';
36123 if(this.bgimage.length){
36124 cls += ' roo-brick-image';
36127 if(!this.html.length && !this.bgimage.length){
36128 cls += ' roo-brick-center-title';
36131 if(!this.html.length && this.bgimage.length){
36132 cls += ' roo-brick-bottom-title';
36136 cls += ' ' + this.cls;
36140 tag: (this.href.length) ? 'a' : 'div',
36145 cls: 'roo-brick-paragraph',
36151 if(this.href.length){
36152 cfg.href = this.href;
36155 var cn = cfg.cn[0].cn;
36157 if(this.title.length){
36160 cls: 'roo-brick-title',
36165 if(this.html.length){
36168 cls: 'roo-brick-text',
36175 if(this.bgimage.length){
36178 cls: 'roo-brick-image-view',
36186 initEvents: function()
36188 if(this.title.length || this.html.length){
36189 this.el.on('mouseenter' ,this.enter, this);
36190 this.el.on('mouseleave', this.leave, this);
36193 Roo.EventManager.onWindowResize(this.resize, this);
36195 if(this.bgimage.length){
36196 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36197 this.imageEl.on('load', this.onImageLoad, this);
36204 onImageLoad : function()
36209 resize : function()
36211 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36213 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36215 if(this.bgimage.length){
36216 var image = this.el.select('.roo-brick-image-view', true).first();
36218 image.setWidth(paragraph.getWidth());
36221 image.setHeight(paragraph.getWidth());
36224 this.el.setHeight(image.getHeight());
36225 paragraph.setHeight(image.getHeight());
36231 enter: function(e, el)
36233 e.preventDefault();
36235 if(this.bgimage.length){
36236 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36237 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36241 leave: function(e, el)
36243 e.preventDefault();
36245 if(this.bgimage.length){
36246 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36247 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36262 * @class Roo.bootstrap.NumberField
36263 * @extends Roo.bootstrap.Input
36264 * Bootstrap NumberField class
36270 * Create a new NumberField
36271 * @param {Object} config The config object
36274 Roo.bootstrap.NumberField = function(config){
36275 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36278 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36281 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36283 allowDecimals : true,
36285 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36287 decimalSeparator : ".",
36289 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36291 decimalPrecision : 2,
36293 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36295 allowNegative : true,
36298 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36302 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36304 minValue : Number.NEGATIVE_INFINITY,
36306 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36308 maxValue : Number.MAX_VALUE,
36310 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36312 minText : "The minimum value for this field is {0}",
36314 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36316 maxText : "The maximum value for this field is {0}",
36318 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36319 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36321 nanText : "{0} is not a valid number",
36323 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36325 thousandsDelimiter : false,
36327 * @cfg {String} valueAlign alignment of value
36329 valueAlign : "left",
36331 getAutoCreate : function()
36333 var hiddenInput = {
36337 cls: 'hidden-number-input'
36341 hiddenInput.name = this.name;
36346 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36348 this.name = hiddenInput.name;
36350 if(cfg.cn.length > 0) {
36351 cfg.cn.push(hiddenInput);
36358 initEvents : function()
36360 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36362 var allowed = "0123456789";
36364 if(this.allowDecimals){
36365 allowed += this.decimalSeparator;
36368 if(this.allowNegative){
36372 if(this.thousandsDelimiter) {
36376 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36378 var keyPress = function(e){
36380 var k = e.getKey();
36382 var c = e.getCharCode();
36385 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36386 allowed.indexOf(String.fromCharCode(c)) === -1
36392 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36396 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36401 this.el.on("keypress", keyPress, this);
36404 validateValue : function(value)
36407 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36411 var num = this.parseValue(value);
36414 this.markInvalid(String.format(this.nanText, value));
36418 if(num < this.minValue){
36419 this.markInvalid(String.format(this.minText, this.minValue));
36423 if(num > this.maxValue){
36424 this.markInvalid(String.format(this.maxText, this.maxValue));
36431 getValue : function()
36433 var v = this.hiddenEl().getValue();
36435 return this.fixPrecision(this.parseValue(v));
36438 parseValue : function(value)
36440 if(this.thousandsDelimiter) {
36442 r = new RegExp(",", "g");
36443 value = value.replace(r, "");
36446 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36447 return isNaN(value) ? '' : value;
36450 fixPrecision : function(value)
36452 if(this.thousandsDelimiter) {
36454 r = new RegExp(",", "g");
36455 value = value.replace(r, "");
36458 var nan = isNaN(value);
36460 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36461 return nan ? '' : value;
36463 return parseFloat(value).toFixed(this.decimalPrecision);
36466 setValue : function(v)
36468 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36474 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36476 this.inputEl().dom.value = (v == '') ? '' :
36477 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36479 if(!this.allowZero && v === '0') {
36480 this.hiddenEl().dom.value = '';
36481 this.inputEl().dom.value = '';
36488 decimalPrecisionFcn : function(v)
36490 return Math.floor(v);
36493 beforeBlur : function()
36495 var v = this.parseValue(this.getRawValue());
36497 if(v || v === 0 || v === ''){
36502 hiddenEl : function()
36504 return this.el.select('input.hidden-number-input',true).first();
36516 * @class Roo.bootstrap.DocumentSlider
36517 * @extends Roo.bootstrap.Component
36518 * Bootstrap DocumentSlider class
36521 * Create a new DocumentViewer
36522 * @param {Object} config The config object
36525 Roo.bootstrap.DocumentSlider = function(config){
36526 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36533 * Fire after initEvent
36534 * @param {Roo.bootstrap.DocumentSlider} this
36539 * Fire after update
36540 * @param {Roo.bootstrap.DocumentSlider} this
36546 * @param {Roo.bootstrap.DocumentSlider} this
36552 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36558 getAutoCreate : function()
36562 cls : 'roo-document-slider',
36566 cls : 'roo-document-slider-header',
36570 cls : 'roo-document-slider-header-title'
36576 cls : 'roo-document-slider-body',
36580 cls : 'roo-document-slider-prev',
36584 cls : 'fa fa-chevron-left'
36590 cls : 'roo-document-slider-thumb',
36594 cls : 'roo-document-slider-image'
36600 cls : 'roo-document-slider-next',
36604 cls : 'fa fa-chevron-right'
36616 initEvents : function()
36618 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36619 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36621 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36622 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36624 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36625 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36627 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36628 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36630 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36631 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36633 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36634 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36636 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36637 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36639 this.thumbEl.on('click', this.onClick, this);
36641 this.prevIndicator.on('click', this.prev, this);
36643 this.nextIndicator.on('click', this.next, this);
36647 initial : function()
36649 if(this.files.length){
36650 this.indicator = 1;
36654 this.fireEvent('initial', this);
36657 update : function()
36659 this.imageEl.attr('src', this.files[this.indicator - 1]);
36661 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36663 this.prevIndicator.show();
36665 if(this.indicator == 1){
36666 this.prevIndicator.hide();
36669 this.nextIndicator.show();
36671 if(this.indicator == this.files.length){
36672 this.nextIndicator.hide();
36675 this.thumbEl.scrollTo('top');
36677 this.fireEvent('update', this);
36680 onClick : function(e)
36682 e.preventDefault();
36684 this.fireEvent('click', this);
36689 e.preventDefault();
36691 this.indicator = Math.max(1, this.indicator - 1);
36698 e.preventDefault();
36700 this.indicator = Math.min(this.files.length, this.indicator + 1);
36714 * @class Roo.bootstrap.RadioSet
36715 * @extends Roo.bootstrap.Input
36716 * Bootstrap RadioSet class
36717 * @cfg {String} indicatorpos (left|right) default left
36718 * @cfg {Boolean} inline (true|false) inline the element (default true)
36719 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36721 * Create a new RadioSet
36722 * @param {Object} config The config object
36725 Roo.bootstrap.RadioSet = function(config){
36727 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36731 Roo.bootstrap.RadioSet.register(this);
36736 * Fires when the element is checked or unchecked.
36737 * @param {Roo.bootstrap.RadioSet} this This radio
36738 * @param {Roo.bootstrap.Radio} item The checked item
36743 * Fires when the element is click.
36744 * @param {Roo.bootstrap.RadioSet} this This radio set
36745 * @param {Roo.bootstrap.Radio} item The checked item
36746 * @param {Roo.EventObject} e The event object
36753 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36761 indicatorpos : 'left',
36763 getAutoCreate : function()
36767 cls : 'roo-radio-set-label',
36771 html : this.fieldLabel
36775 if (Roo.bootstrap.version == 3) {
36778 if(this.indicatorpos == 'left'){
36781 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36782 tooltip : 'This field is required'
36787 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36788 tooltip : 'This field is required'
36794 cls : 'roo-radio-set-items'
36797 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36799 if (align === 'left' && this.fieldLabel.length) {
36802 cls : "roo-radio-set-right",
36808 if(this.labelWidth > 12){
36809 label.style = "width: " + this.labelWidth + 'px';
36812 if(this.labelWidth < 13 && this.labelmd == 0){
36813 this.labelmd = this.labelWidth;
36816 if(this.labellg > 0){
36817 label.cls += ' col-lg-' + this.labellg;
36818 items.cls += ' col-lg-' + (12 - this.labellg);
36821 if(this.labelmd > 0){
36822 label.cls += ' col-md-' + this.labelmd;
36823 items.cls += ' col-md-' + (12 - this.labelmd);
36826 if(this.labelsm > 0){
36827 label.cls += ' col-sm-' + this.labelsm;
36828 items.cls += ' col-sm-' + (12 - this.labelsm);
36831 if(this.labelxs > 0){
36832 label.cls += ' col-xs-' + this.labelxs;
36833 items.cls += ' col-xs-' + (12 - this.labelxs);
36839 cls : 'roo-radio-set',
36843 cls : 'roo-radio-set-input',
36846 value : this.value ? this.value : ''
36853 if(this.weight.length){
36854 cfg.cls += ' roo-radio-' + this.weight;
36858 cfg.cls += ' roo-radio-set-inline';
36862 ['xs','sm','md','lg'].map(function(size){
36863 if (settings[size]) {
36864 cfg.cls += ' col-' + size + '-' + settings[size];
36872 initEvents : function()
36874 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36875 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36877 if(!this.fieldLabel.length){
36878 this.labelEl.hide();
36881 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36882 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36884 this.indicator = this.indicatorEl();
36886 if(this.indicator){
36887 this.indicator.addClass('invisible');
36890 this.originalValue = this.getValue();
36894 inputEl: function ()
36896 return this.el.select('.roo-radio-set-input', true).first();
36899 getChildContainer : function()
36901 return this.itemsEl;
36904 register : function(item)
36906 this.radioes.push(item);
36910 validate : function()
36912 if(this.getVisibilityEl().hasClass('hidden')){
36918 Roo.each(this.radioes, function(i){
36927 if(this.allowBlank) {
36931 if(this.disabled || valid){
36936 this.markInvalid();
36941 markValid : function()
36943 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36944 this.indicatorEl().removeClass('visible');
36945 this.indicatorEl().addClass('invisible');
36949 if (Roo.bootstrap.version == 3) {
36950 this.el.removeClass([this.invalidClass, this.validClass]);
36951 this.el.addClass(this.validClass);
36953 this.el.removeClass(['is-invalid','is-valid']);
36954 this.el.addClass(['is-valid']);
36956 this.fireEvent('valid', this);
36959 markInvalid : function(msg)
36961 if(this.allowBlank || this.disabled){
36965 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36966 this.indicatorEl().removeClass('invisible');
36967 this.indicatorEl().addClass('visible');
36969 if (Roo.bootstrap.version == 3) {
36970 this.el.removeClass([this.invalidClass, this.validClass]);
36971 this.el.addClass(this.invalidClass);
36973 this.el.removeClass(['is-invalid','is-valid']);
36974 this.el.addClass(['is-invalid']);
36977 this.fireEvent('invalid', this, msg);
36981 setValue : function(v, suppressEvent)
36983 if(this.value === v){
36990 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36993 Roo.each(this.radioes, function(i){
36995 i.el.removeClass('checked');
36998 Roo.each(this.radioes, function(i){
37000 if(i.value === v || i.value.toString() === v.toString()){
37002 i.el.addClass('checked');
37004 if(suppressEvent !== true){
37005 this.fireEvent('check', this, i);
37016 clearInvalid : function(){
37018 if(!this.el || this.preventMark){
37022 this.el.removeClass([this.invalidClass]);
37024 this.fireEvent('valid', this);
37029 Roo.apply(Roo.bootstrap.RadioSet, {
37033 register : function(set)
37035 this.groups[set.name] = set;
37038 get: function(name)
37040 if (typeof(this.groups[name]) == 'undefined') {
37044 return this.groups[name] ;
37050 * Ext JS Library 1.1.1
37051 * Copyright(c) 2006-2007, Ext JS, LLC.
37053 * Originally Released Under LGPL - original licence link has changed is not relivant.
37056 * <script type="text/javascript">
37061 * @class Roo.bootstrap.SplitBar
37062 * @extends Roo.util.Observable
37063 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37067 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37068 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37069 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37070 split.minSize = 100;
37071 split.maxSize = 600;
37072 split.animate = true;
37073 split.on('moved', splitterMoved);
37076 * Create a new SplitBar
37077 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37078 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37079 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37080 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37081 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37082 position of the SplitBar).
37084 Roo.bootstrap.SplitBar = function(cfg){
37089 // dragElement : elm
37090 // resizingElement: el,
37092 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37093 // placement : Roo.bootstrap.SplitBar.LEFT ,
37094 // existingProxy ???
37097 this.el = Roo.get(cfg.dragElement, true);
37098 this.el.dom.unselectable = "on";
37100 this.resizingEl = Roo.get(cfg.resizingElement, true);
37104 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37105 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37108 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37111 * The minimum size of the resizing element. (Defaults to 0)
37117 * The maximum size of the resizing element. (Defaults to 2000)
37120 this.maxSize = 2000;
37123 * Whether to animate the transition to the new size
37126 this.animate = false;
37129 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37132 this.useShim = false;
37137 if(!cfg.existingProxy){
37139 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37141 this.proxy = Roo.get(cfg.existingProxy).dom;
37144 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37147 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37150 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37153 this.dragSpecs = {};
37156 * @private The adapter to use to positon and resize elements
37158 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37159 this.adapter.init(this);
37161 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37163 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37164 this.el.addClass("roo-splitbar-h");
37167 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37168 this.el.addClass("roo-splitbar-v");
37174 * Fires when the splitter is moved (alias for {@link #event-moved})
37175 * @param {Roo.bootstrap.SplitBar} this
37176 * @param {Number} newSize the new width or height
37181 * Fires when the splitter is moved
37182 * @param {Roo.bootstrap.SplitBar} this
37183 * @param {Number} newSize the new width or height
37187 * @event beforeresize
37188 * Fires before the splitter is dragged
37189 * @param {Roo.bootstrap.SplitBar} this
37191 "beforeresize" : true,
37193 "beforeapply" : true
37196 Roo.util.Observable.call(this);
37199 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37200 onStartProxyDrag : function(x, y){
37201 this.fireEvent("beforeresize", this);
37203 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37205 o.enableDisplayMode("block");
37206 // all splitbars share the same overlay
37207 Roo.bootstrap.SplitBar.prototype.overlay = o;
37209 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37210 this.overlay.show();
37211 Roo.get(this.proxy).setDisplayed("block");
37212 var size = this.adapter.getElementSize(this);
37213 this.activeMinSize = this.getMinimumSize();;
37214 this.activeMaxSize = this.getMaximumSize();;
37215 var c1 = size - this.activeMinSize;
37216 var c2 = Math.max(this.activeMaxSize - size, 0);
37217 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37218 this.dd.resetConstraints();
37219 this.dd.setXConstraint(
37220 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37221 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37223 this.dd.setYConstraint(0, 0);
37225 this.dd.resetConstraints();
37226 this.dd.setXConstraint(0, 0);
37227 this.dd.setYConstraint(
37228 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37229 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37232 this.dragSpecs.startSize = size;
37233 this.dragSpecs.startPoint = [x, y];
37234 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37238 * @private Called after the drag operation by the DDProxy
37240 onEndProxyDrag : function(e){
37241 Roo.get(this.proxy).setDisplayed(false);
37242 var endPoint = Roo.lib.Event.getXY(e);
37244 this.overlay.hide();
37247 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37248 newSize = this.dragSpecs.startSize +
37249 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37250 endPoint[0] - this.dragSpecs.startPoint[0] :
37251 this.dragSpecs.startPoint[0] - endPoint[0]
37254 newSize = this.dragSpecs.startSize +
37255 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37256 endPoint[1] - this.dragSpecs.startPoint[1] :
37257 this.dragSpecs.startPoint[1] - endPoint[1]
37260 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37261 if(newSize != this.dragSpecs.startSize){
37262 if(this.fireEvent('beforeapply', this, newSize) !== false){
37263 this.adapter.setElementSize(this, newSize);
37264 this.fireEvent("moved", this, newSize);
37265 this.fireEvent("resize", this, newSize);
37271 * Get the adapter this SplitBar uses
37272 * @return The adapter object
37274 getAdapter : function(){
37275 return this.adapter;
37279 * Set the adapter this SplitBar uses
37280 * @param {Object} adapter A SplitBar adapter object
37282 setAdapter : function(adapter){
37283 this.adapter = adapter;
37284 this.adapter.init(this);
37288 * Gets the minimum size for the resizing element
37289 * @return {Number} The minimum size
37291 getMinimumSize : function(){
37292 return this.minSize;
37296 * Sets the minimum size for the resizing element
37297 * @param {Number} minSize The minimum size
37299 setMinimumSize : function(minSize){
37300 this.minSize = minSize;
37304 * Gets the maximum size for the resizing element
37305 * @return {Number} The maximum size
37307 getMaximumSize : function(){
37308 return this.maxSize;
37312 * Sets the maximum size for the resizing element
37313 * @param {Number} maxSize The maximum size
37315 setMaximumSize : function(maxSize){
37316 this.maxSize = maxSize;
37320 * Sets the initialize size for the resizing element
37321 * @param {Number} size The initial size
37323 setCurrentSize : function(size){
37324 var oldAnimate = this.animate;
37325 this.animate = false;
37326 this.adapter.setElementSize(this, size);
37327 this.animate = oldAnimate;
37331 * Destroy this splitbar.
37332 * @param {Boolean} removeEl True to remove the element
37334 destroy : function(removeEl){
37336 this.shim.remove();
37339 this.proxy.parentNode.removeChild(this.proxy);
37347 * @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.
37349 Roo.bootstrap.SplitBar.createProxy = function(dir){
37350 var proxy = new Roo.Element(document.createElement("div"));
37351 proxy.unselectable();
37352 var cls = 'roo-splitbar-proxy';
37353 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37354 document.body.appendChild(proxy.dom);
37359 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37360 * Default Adapter. It assumes the splitter and resizing element are not positioned
37361 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37363 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37366 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37367 // do nothing for now
37368 init : function(s){
37372 * Called before drag operations to get the current size of the resizing element.
37373 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37375 getElementSize : function(s){
37376 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37377 return s.resizingEl.getWidth();
37379 return s.resizingEl.getHeight();
37384 * Called after drag operations to set the size of the resizing element.
37385 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37386 * @param {Number} newSize The new size to set
37387 * @param {Function} onComplete A function to be invoked when resizing is complete
37389 setElementSize : function(s, newSize, onComplete){
37390 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37392 s.resizingEl.setWidth(newSize);
37394 onComplete(s, newSize);
37397 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37402 s.resizingEl.setHeight(newSize);
37404 onComplete(s, newSize);
37407 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37414 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37415 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37416 * Adapter that moves the splitter element to align with the resized sizing element.
37417 * Used with an absolute positioned SplitBar.
37418 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37419 * document.body, make sure you assign an id to the body element.
37421 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37422 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37423 this.container = Roo.get(container);
37426 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37427 init : function(s){
37428 this.basic.init(s);
37431 getElementSize : function(s){
37432 return this.basic.getElementSize(s);
37435 setElementSize : function(s, newSize, onComplete){
37436 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37439 moveSplitter : function(s){
37440 var yes = Roo.bootstrap.SplitBar;
37441 switch(s.placement){
37443 s.el.setX(s.resizingEl.getRight());
37446 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37449 s.el.setY(s.resizingEl.getBottom());
37452 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37459 * Orientation constant - Create a vertical SplitBar
37463 Roo.bootstrap.SplitBar.VERTICAL = 1;
37466 * Orientation constant - Create a horizontal SplitBar
37470 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37473 * Placement constant - The resizing element is to the left of the splitter element
37477 Roo.bootstrap.SplitBar.LEFT = 1;
37480 * Placement constant - The resizing element is to the right of the splitter element
37484 Roo.bootstrap.SplitBar.RIGHT = 2;
37487 * Placement constant - The resizing element is positioned above the splitter element
37491 Roo.bootstrap.SplitBar.TOP = 3;
37494 * Placement constant - The resizing element is positioned under splitter element
37498 Roo.bootstrap.SplitBar.BOTTOM = 4;
37499 Roo.namespace("Roo.bootstrap.layout");/*
37501 * Ext JS Library 1.1.1
37502 * Copyright(c) 2006-2007, Ext JS, LLC.
37504 * Originally Released Under LGPL - original licence link has changed is not relivant.
37507 * <script type="text/javascript">
37511 * @class Roo.bootstrap.layout.Manager
37512 * @extends Roo.bootstrap.Component
37513 * Base class for layout managers.
37515 Roo.bootstrap.layout.Manager = function(config)
37517 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37523 /** false to disable window resize monitoring @type Boolean */
37524 this.monitorWindowResize = true;
37529 * Fires when a layout is performed.
37530 * @param {Roo.LayoutManager} this
37534 * @event regionresized
37535 * Fires when the user resizes a region.
37536 * @param {Roo.LayoutRegion} region The resized region
37537 * @param {Number} newSize The new size (width for east/west, height for north/south)
37539 "regionresized" : true,
37541 * @event regioncollapsed
37542 * Fires when a region is collapsed.
37543 * @param {Roo.LayoutRegion} region The collapsed region
37545 "regioncollapsed" : true,
37547 * @event regionexpanded
37548 * Fires when a region is expanded.
37549 * @param {Roo.LayoutRegion} region The expanded region
37551 "regionexpanded" : true
37553 this.updating = false;
37556 this.el = Roo.get(config.el);
37562 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37567 monitorWindowResize : true,
37573 onRender : function(ct, position)
37576 this.el = Roo.get(ct);
37579 //this.fireEvent('render',this);
37583 initEvents: function()
37587 // ie scrollbar fix
37588 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37589 document.body.scroll = "no";
37590 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37591 this.el.position('relative');
37593 this.id = this.el.id;
37594 this.el.addClass("roo-layout-container");
37595 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37596 if(this.el.dom != document.body ) {
37597 this.el.on('resize', this.layout,this);
37598 this.el.on('show', this.layout,this);
37604 * Returns true if this layout is currently being updated
37605 * @return {Boolean}
37607 isUpdating : function(){
37608 return this.updating;
37612 * Suspend the LayoutManager from doing auto-layouts while
37613 * making multiple add or remove calls
37615 beginUpdate : function(){
37616 this.updating = true;
37620 * Restore auto-layouts and optionally disable the manager from performing a layout
37621 * @param {Boolean} noLayout true to disable a layout update
37623 endUpdate : function(noLayout){
37624 this.updating = false;
37630 layout: function(){
37634 onRegionResized : function(region, newSize){
37635 this.fireEvent("regionresized", region, newSize);
37639 onRegionCollapsed : function(region){
37640 this.fireEvent("regioncollapsed", region);
37643 onRegionExpanded : function(region){
37644 this.fireEvent("regionexpanded", region);
37648 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37649 * performs box-model adjustments.
37650 * @return {Object} The size as an object {width: (the width), height: (the height)}
37652 getViewSize : function()
37655 if(this.el.dom != document.body){
37656 size = this.el.getSize();
37658 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37660 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37661 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37666 * Returns the Element this layout is bound to.
37667 * @return {Roo.Element}
37669 getEl : function(){
37674 * Returns the specified region.
37675 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37676 * @return {Roo.LayoutRegion}
37678 getRegion : function(target){
37679 return this.regions[target.toLowerCase()];
37682 onWindowResize : function(){
37683 if(this.monitorWindowResize){
37690 * Ext JS Library 1.1.1
37691 * Copyright(c) 2006-2007, Ext JS, LLC.
37693 * Originally Released Under LGPL - original licence link has changed is not relivant.
37696 * <script type="text/javascript">
37699 * @class Roo.bootstrap.layout.Border
37700 * @extends Roo.bootstrap.layout.Manager
37701 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37702 * please see: examples/bootstrap/nested.html<br><br>
37704 <b>The container the layout is rendered into can be either the body element or any other element.
37705 If it is not the body element, the container needs to either be an absolute positioned element,
37706 or you will need to add "position:relative" to the css of the container. You will also need to specify
37707 the container size if it is not the body element.</b>
37710 * Create a new Border
37711 * @param {Object} config Configuration options
37713 Roo.bootstrap.layout.Border = function(config){
37714 config = config || {};
37715 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37719 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37720 if(config[region]){
37721 config[region].region = region;
37722 this.addRegion(config[region]);
37728 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37730 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37732 parent : false, // this might point to a 'nest' or a ???
37735 * Creates and adds a new region if it doesn't already exist.
37736 * @param {String} target The target region key (north, south, east, west or center).
37737 * @param {Object} config The regions config object
37738 * @return {BorderLayoutRegion} The new region
37740 addRegion : function(config)
37742 if(!this.regions[config.region]){
37743 var r = this.factory(config);
37744 this.bindRegion(r);
37746 return this.regions[config.region];
37750 bindRegion : function(r){
37751 this.regions[r.config.region] = r;
37753 r.on("visibilitychange", this.layout, this);
37754 r.on("paneladded", this.layout, this);
37755 r.on("panelremoved", this.layout, this);
37756 r.on("invalidated", this.layout, this);
37757 r.on("resized", this.onRegionResized, this);
37758 r.on("collapsed", this.onRegionCollapsed, this);
37759 r.on("expanded", this.onRegionExpanded, this);
37763 * Performs a layout update.
37765 layout : function()
37767 if(this.updating) {
37771 // render all the rebions if they have not been done alreayd?
37772 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37773 if(this.regions[region] && !this.regions[region].bodyEl){
37774 this.regions[region].onRender(this.el)
37778 var size = this.getViewSize();
37779 var w = size.width;
37780 var h = size.height;
37785 //var x = 0, y = 0;
37787 var rs = this.regions;
37788 var north = rs["north"];
37789 var south = rs["south"];
37790 var west = rs["west"];
37791 var east = rs["east"];
37792 var center = rs["center"];
37793 //if(this.hideOnLayout){ // not supported anymore
37794 //c.el.setStyle("display", "none");
37796 if(north && north.isVisible()){
37797 var b = north.getBox();
37798 var m = north.getMargins();
37799 b.width = w - (m.left+m.right);
37802 centerY = b.height + b.y + m.bottom;
37803 centerH -= centerY;
37804 north.updateBox(this.safeBox(b));
37806 if(south && south.isVisible()){
37807 var b = south.getBox();
37808 var m = south.getMargins();
37809 b.width = w - (m.left+m.right);
37811 var totalHeight = (b.height + m.top + m.bottom);
37812 b.y = h - totalHeight + m.top;
37813 centerH -= totalHeight;
37814 south.updateBox(this.safeBox(b));
37816 if(west && west.isVisible()){
37817 var b = west.getBox();
37818 var m = west.getMargins();
37819 b.height = centerH - (m.top+m.bottom);
37821 b.y = centerY + m.top;
37822 var totalWidth = (b.width + m.left + m.right);
37823 centerX += totalWidth;
37824 centerW -= totalWidth;
37825 west.updateBox(this.safeBox(b));
37827 if(east && east.isVisible()){
37828 var b = east.getBox();
37829 var m = east.getMargins();
37830 b.height = centerH - (m.top+m.bottom);
37831 var totalWidth = (b.width + m.left + m.right);
37832 b.x = w - totalWidth + m.left;
37833 b.y = centerY + m.top;
37834 centerW -= totalWidth;
37835 east.updateBox(this.safeBox(b));
37838 var m = center.getMargins();
37840 x: centerX + m.left,
37841 y: centerY + m.top,
37842 width: centerW - (m.left+m.right),
37843 height: centerH - (m.top+m.bottom)
37845 //if(this.hideOnLayout){
37846 //center.el.setStyle("display", "block");
37848 center.updateBox(this.safeBox(centerBox));
37851 this.fireEvent("layout", this);
37855 safeBox : function(box){
37856 box.width = Math.max(0, box.width);
37857 box.height = Math.max(0, box.height);
37862 * Adds a ContentPanel (or subclass) to this layout.
37863 * @param {String} target The target region key (north, south, east, west or center).
37864 * @param {Roo.ContentPanel} panel The panel to add
37865 * @return {Roo.ContentPanel} The added panel
37867 add : function(target, panel){
37869 target = target.toLowerCase();
37870 return this.regions[target].add(panel);
37874 * Remove a ContentPanel (or subclass) to this layout.
37875 * @param {String} target The target region key (north, south, east, west or center).
37876 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37877 * @return {Roo.ContentPanel} The removed panel
37879 remove : function(target, panel){
37880 target = target.toLowerCase();
37881 return this.regions[target].remove(panel);
37885 * Searches all regions for a panel with the specified id
37886 * @param {String} panelId
37887 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37889 findPanel : function(panelId){
37890 var rs = this.regions;
37891 for(var target in rs){
37892 if(typeof rs[target] != "function"){
37893 var p = rs[target].getPanel(panelId);
37903 * Searches all regions for a panel with the specified id and activates (shows) it.
37904 * @param {String/ContentPanel} panelId The panels id or the panel itself
37905 * @return {Roo.ContentPanel} The shown panel or null
37907 showPanel : function(panelId) {
37908 var rs = this.regions;
37909 for(var target in rs){
37910 var r = rs[target];
37911 if(typeof r != "function"){
37912 if(r.hasPanel(panelId)){
37913 return r.showPanel(panelId);
37921 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37922 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37925 restoreState : function(provider){
37927 provider = Roo.state.Manager;
37929 var sm = new Roo.LayoutStateManager();
37930 sm.init(this, provider);
37936 * Adds a xtype elements to the layout.
37940 xtype : 'ContentPanel',
37947 xtype : 'NestedLayoutPanel',
37953 items : [ ... list of content panels or nested layout panels.. ]
37957 * @param {Object} cfg Xtype definition of item to add.
37959 addxtype : function(cfg)
37961 // basically accepts a pannel...
37962 // can accept a layout region..!?!?
37963 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37966 // theory? children can only be panels??
37968 //if (!cfg.xtype.match(/Panel$/)) {
37973 if (typeof(cfg.region) == 'undefined') {
37974 Roo.log("Failed to add Panel, region was not set");
37978 var region = cfg.region;
37984 xitems = cfg.items;
37989 if ( region == 'center') {
37990 Roo.log("Center: " + cfg.title);
37996 case 'Content': // ContentPanel (el, cfg)
37997 case 'Scroll': // ContentPanel (el, cfg)
37999 cfg.autoCreate = cfg.autoCreate || true;
38000 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38002 // var el = this.el.createChild();
38003 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38006 this.add(region, ret);
38010 case 'TreePanel': // our new panel!
38011 cfg.el = this.el.createChild();
38012 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38013 this.add(region, ret);
38018 // create a new Layout (which is a Border Layout...
38020 var clayout = cfg.layout;
38021 clayout.el = this.el.createChild();
38022 clayout.items = clayout.items || [];
38026 // replace this exitems with the clayout ones..
38027 xitems = clayout.items;
38029 // force background off if it's in center...
38030 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38031 cfg.background = false;
38033 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38036 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38037 //console.log('adding nested layout panel ' + cfg.toSource());
38038 this.add(region, ret);
38039 nb = {}; /// find first...
38044 // needs grid and region
38046 //var el = this.getRegion(region).el.createChild();
38048 *var el = this.el.createChild();
38049 // create the grid first...
38050 cfg.grid.container = el;
38051 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38054 if (region == 'center' && this.active ) {
38055 cfg.background = false;
38058 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38060 this.add(region, ret);
38062 if (cfg.background) {
38063 // render grid on panel activation (if panel background)
38064 ret.on('activate', function(gp) {
38065 if (!gp.grid.rendered) {
38066 // gp.grid.render(el);
38070 // cfg.grid.render(el);
38076 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38077 // it was the old xcomponent building that caused this before.
38078 // espeically if border is the top element in the tree.
38088 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38090 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38091 this.add(region, ret);
38095 throw "Can not add '" + cfg.xtype + "' to Border";
38101 this.beginUpdate();
38105 Roo.each(xitems, function(i) {
38106 region = nb && i.region ? i.region : false;
38108 var add = ret.addxtype(i);
38111 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38112 if (!i.background) {
38113 abn[region] = nb[region] ;
38120 // make the last non-background panel active..
38121 //if (nb) { Roo.log(abn); }
38124 for(var r in abn) {
38125 region = this.getRegion(r);
38127 // tried using nb[r], but it does not work..
38129 region.showPanel(abn[r]);
38140 factory : function(cfg)
38143 var validRegions = Roo.bootstrap.layout.Border.regions;
38145 var target = cfg.region;
38148 var r = Roo.bootstrap.layout;
38152 return new r.North(cfg);
38154 return new r.South(cfg);
38156 return new r.East(cfg);
38158 return new r.West(cfg);
38160 return new r.Center(cfg);
38162 throw 'Layout region "'+target+'" not supported.';
38169 * Ext JS Library 1.1.1
38170 * Copyright(c) 2006-2007, Ext JS, LLC.
38172 * Originally Released Under LGPL - original licence link has changed is not relivant.
38175 * <script type="text/javascript">
38179 * @class Roo.bootstrap.layout.Basic
38180 * @extends Roo.util.Observable
38181 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38182 * and does not have a titlebar, tabs or any other features. All it does is size and position
38183 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38184 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38185 * @cfg {string} region the region that it inhabits..
38186 * @cfg {bool} skipConfig skip config?
38190 Roo.bootstrap.layout.Basic = function(config){
38192 this.mgr = config.mgr;
38194 this.position = config.region;
38196 var skipConfig = config.skipConfig;
38200 * @scope Roo.BasicLayoutRegion
38204 * @event beforeremove
38205 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38206 * @param {Roo.LayoutRegion} this
38207 * @param {Roo.ContentPanel} panel The panel
38208 * @param {Object} e The cancel event object
38210 "beforeremove" : true,
38212 * @event invalidated
38213 * Fires when the layout for this region is changed.
38214 * @param {Roo.LayoutRegion} this
38216 "invalidated" : true,
38218 * @event visibilitychange
38219 * Fires when this region is shown or hidden
38220 * @param {Roo.LayoutRegion} this
38221 * @param {Boolean} visibility true or false
38223 "visibilitychange" : true,
38225 * @event paneladded
38226 * Fires when a panel is added.
38227 * @param {Roo.LayoutRegion} this
38228 * @param {Roo.ContentPanel} panel The panel
38230 "paneladded" : true,
38232 * @event panelremoved
38233 * Fires when a panel is removed.
38234 * @param {Roo.LayoutRegion} this
38235 * @param {Roo.ContentPanel} panel The panel
38237 "panelremoved" : true,
38239 * @event beforecollapse
38240 * Fires when this region before collapse.
38241 * @param {Roo.LayoutRegion} this
38243 "beforecollapse" : true,
38246 * Fires when this region is collapsed.
38247 * @param {Roo.LayoutRegion} this
38249 "collapsed" : true,
38252 * Fires when this region is expanded.
38253 * @param {Roo.LayoutRegion} this
38258 * Fires when this region is slid into view.
38259 * @param {Roo.LayoutRegion} this
38261 "slideshow" : true,
38264 * Fires when this region slides out of view.
38265 * @param {Roo.LayoutRegion} this
38267 "slidehide" : true,
38269 * @event panelactivated
38270 * Fires when a panel is activated.
38271 * @param {Roo.LayoutRegion} this
38272 * @param {Roo.ContentPanel} panel The activated panel
38274 "panelactivated" : true,
38277 * Fires when the user resizes this region.
38278 * @param {Roo.LayoutRegion} this
38279 * @param {Number} newSize The new size (width for east/west, height for north/south)
38283 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38284 this.panels = new Roo.util.MixedCollection();
38285 this.panels.getKey = this.getPanelId.createDelegate(this);
38287 this.activePanel = null;
38288 // ensure listeners are added...
38290 if (config.listeners || config.events) {
38291 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38292 listeners : config.listeners || {},
38293 events : config.events || {}
38297 if(skipConfig !== true){
38298 this.applyConfig(config);
38302 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38304 getPanelId : function(p){
38308 applyConfig : function(config){
38309 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38310 this.config = config;
38315 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38316 * the width, for horizontal (north, south) the height.
38317 * @param {Number} newSize The new width or height
38319 resizeTo : function(newSize){
38320 var el = this.el ? this.el :
38321 (this.activePanel ? this.activePanel.getEl() : null);
38323 switch(this.position){
38326 el.setWidth(newSize);
38327 this.fireEvent("resized", this, newSize);
38331 el.setHeight(newSize);
38332 this.fireEvent("resized", this, newSize);
38338 getBox : function(){
38339 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38342 getMargins : function(){
38343 return this.margins;
38346 updateBox : function(box){
38348 var el = this.activePanel.getEl();
38349 el.dom.style.left = box.x + "px";
38350 el.dom.style.top = box.y + "px";
38351 this.activePanel.setSize(box.width, box.height);
38355 * Returns the container element for this region.
38356 * @return {Roo.Element}
38358 getEl : function(){
38359 return this.activePanel;
38363 * Returns true if this region is currently visible.
38364 * @return {Boolean}
38366 isVisible : function(){
38367 return this.activePanel ? true : false;
38370 setActivePanel : function(panel){
38371 panel = this.getPanel(panel);
38372 if(this.activePanel && this.activePanel != panel){
38373 this.activePanel.setActiveState(false);
38374 this.activePanel.getEl().setLeftTop(-10000,-10000);
38376 this.activePanel = panel;
38377 panel.setActiveState(true);
38379 panel.setSize(this.box.width, this.box.height);
38381 this.fireEvent("panelactivated", this, panel);
38382 this.fireEvent("invalidated");
38386 * Show the specified panel.
38387 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38388 * @return {Roo.ContentPanel} The shown panel or null
38390 showPanel : function(panel){
38391 panel = this.getPanel(panel);
38393 this.setActivePanel(panel);
38399 * Get the active panel for this region.
38400 * @return {Roo.ContentPanel} The active panel or null
38402 getActivePanel : function(){
38403 return this.activePanel;
38407 * Add the passed ContentPanel(s)
38408 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38409 * @return {Roo.ContentPanel} The panel added (if only one was added)
38411 add : function(panel){
38412 if(arguments.length > 1){
38413 for(var i = 0, len = arguments.length; i < len; i++) {
38414 this.add(arguments[i]);
38418 if(this.hasPanel(panel)){
38419 this.showPanel(panel);
38422 var el = panel.getEl();
38423 if(el.dom.parentNode != this.mgr.el.dom){
38424 this.mgr.el.dom.appendChild(el.dom);
38426 if(panel.setRegion){
38427 panel.setRegion(this);
38429 this.panels.add(panel);
38430 el.setStyle("position", "absolute");
38431 if(!panel.background){
38432 this.setActivePanel(panel);
38433 if(this.config.initialSize && this.panels.getCount()==1){
38434 this.resizeTo(this.config.initialSize);
38437 this.fireEvent("paneladded", this, panel);
38442 * Returns true if the panel is in this region.
38443 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38444 * @return {Boolean}
38446 hasPanel : function(panel){
38447 if(typeof panel == "object"){ // must be panel obj
38448 panel = panel.getId();
38450 return this.getPanel(panel) ? true : false;
38454 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38455 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38456 * @param {Boolean} preservePanel Overrides the config preservePanel option
38457 * @return {Roo.ContentPanel} The panel that was removed
38459 remove : function(panel, preservePanel){
38460 panel = this.getPanel(panel);
38465 this.fireEvent("beforeremove", this, panel, e);
38466 if(e.cancel === true){
38469 var panelId = panel.getId();
38470 this.panels.removeKey(panelId);
38475 * Returns the panel specified or null if it's not in this region.
38476 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38477 * @return {Roo.ContentPanel}
38479 getPanel : function(id){
38480 if(typeof id == "object"){ // must be panel obj
38483 return this.panels.get(id);
38487 * Returns this regions position (north/south/east/west/center).
38490 getPosition: function(){
38491 return this.position;
38495 * Ext JS Library 1.1.1
38496 * Copyright(c) 2006-2007, Ext JS, LLC.
38498 * Originally Released Under LGPL - original licence link has changed is not relivant.
38501 * <script type="text/javascript">
38505 * @class Roo.bootstrap.layout.Region
38506 * @extends Roo.bootstrap.layout.Basic
38507 * This class represents a region in a layout manager.
38509 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38510 * @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})
38511 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38512 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38513 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38514 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38515 * @cfg {String} title The title for the region (overrides panel titles)
38516 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38517 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38518 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38519 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38520 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38521 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38522 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38523 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38524 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38525 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38527 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38528 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38529 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38530 * @cfg {Number} width For East/West panels
38531 * @cfg {Number} height For North/South panels
38532 * @cfg {Boolean} split To show the splitter
38533 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38535 * @cfg {string} cls Extra CSS classes to add to region
38537 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38538 * @cfg {string} region the region that it inhabits..
38541 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38542 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38544 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38545 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38546 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38548 Roo.bootstrap.layout.Region = function(config)
38550 this.applyConfig(config);
38552 var mgr = config.mgr;
38553 var pos = config.region;
38554 config.skipConfig = true;
38555 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38558 this.onRender(mgr.el);
38561 this.visible = true;
38562 this.collapsed = false;
38563 this.unrendered_panels = [];
38566 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38568 position: '', // set by wrapper (eg. north/south etc..)
38569 unrendered_panels : null, // unrendered panels.
38571 tabPosition : false,
38573 mgr: false, // points to 'Border'
38576 createBody : function(){
38577 /** This region's body element
38578 * @type Roo.Element */
38579 this.bodyEl = this.el.createChild({
38581 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38585 onRender: function(ctr, pos)
38587 var dh = Roo.DomHelper;
38588 /** This region's container element
38589 * @type Roo.Element */
38590 this.el = dh.append(ctr.dom, {
38592 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38594 /** This region's title element
38595 * @type Roo.Element */
38597 this.titleEl = dh.append(this.el.dom, {
38599 unselectable: "on",
38600 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38602 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38603 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38607 this.titleEl.enableDisplayMode();
38608 /** This region's title text element
38609 * @type HTMLElement */
38610 this.titleTextEl = this.titleEl.dom.firstChild;
38611 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38613 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38614 this.closeBtn.enableDisplayMode();
38615 this.closeBtn.on("click", this.closeClicked, this);
38616 this.closeBtn.hide();
38618 this.createBody(this.config);
38619 if(this.config.hideWhenEmpty){
38621 this.on("paneladded", this.validateVisibility, this);
38622 this.on("panelremoved", this.validateVisibility, this);
38624 if(this.autoScroll){
38625 this.bodyEl.setStyle("overflow", "auto");
38627 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38629 //if(c.titlebar !== false){
38630 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38631 this.titleEl.hide();
38633 this.titleEl.show();
38634 if(this.config.title){
38635 this.titleTextEl.innerHTML = this.config.title;
38639 if(this.config.collapsed){
38640 this.collapse(true);
38642 if(this.config.hidden){
38646 if (this.unrendered_panels && this.unrendered_panels.length) {
38647 for (var i =0;i< this.unrendered_panels.length; i++) {
38648 this.add(this.unrendered_panels[i]);
38650 this.unrendered_panels = null;
38656 applyConfig : function(c)
38659 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38660 var dh = Roo.DomHelper;
38661 if(c.titlebar !== false){
38662 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38663 this.collapseBtn.on("click", this.collapse, this);
38664 this.collapseBtn.enableDisplayMode();
38666 if(c.showPin === true || this.showPin){
38667 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38668 this.stickBtn.enableDisplayMode();
38669 this.stickBtn.on("click", this.expand, this);
38670 this.stickBtn.hide();
38675 /** This region's collapsed element
38676 * @type Roo.Element */
38679 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38680 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38683 if(c.floatable !== false){
38684 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38685 this.collapsedEl.on("click", this.collapseClick, this);
38688 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38689 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38690 id: "message", unselectable: "on", style:{"float":"left"}});
38691 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38693 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38694 this.expandBtn.on("click", this.expand, this);
38698 if(this.collapseBtn){
38699 this.collapseBtn.setVisible(c.collapsible == true);
38702 this.cmargins = c.cmargins || this.cmargins ||
38703 (this.position == "west" || this.position == "east" ?
38704 {top: 0, left: 2, right:2, bottom: 0} :
38705 {top: 2, left: 0, right:0, bottom: 2});
38707 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38710 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38712 this.autoScroll = c.autoScroll || false;
38717 this.duration = c.duration || .30;
38718 this.slideDuration = c.slideDuration || .45;
38723 * Returns true if this region is currently visible.
38724 * @return {Boolean}
38726 isVisible : function(){
38727 return this.visible;
38731 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38732 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38734 //setCollapsedTitle : function(title){
38735 // title = title || " ";
38736 // if(this.collapsedTitleTextEl){
38737 // this.collapsedTitleTextEl.innerHTML = title;
38741 getBox : function(){
38743 // if(!this.collapsed){
38744 b = this.el.getBox(false, true);
38746 // b = this.collapsedEl.getBox(false, true);
38751 getMargins : function(){
38752 return this.margins;
38753 //return this.collapsed ? this.cmargins : this.margins;
38756 highlight : function(){
38757 this.el.addClass("x-layout-panel-dragover");
38760 unhighlight : function(){
38761 this.el.removeClass("x-layout-panel-dragover");
38764 updateBox : function(box)
38766 if (!this.bodyEl) {
38767 return; // not rendered yet..
38771 if(!this.collapsed){
38772 this.el.dom.style.left = box.x + "px";
38773 this.el.dom.style.top = box.y + "px";
38774 this.updateBody(box.width, box.height);
38776 this.collapsedEl.dom.style.left = box.x + "px";
38777 this.collapsedEl.dom.style.top = box.y + "px";
38778 this.collapsedEl.setSize(box.width, box.height);
38781 this.tabs.autoSizeTabs();
38785 updateBody : function(w, h)
38788 this.el.setWidth(w);
38789 w -= this.el.getBorderWidth("rl");
38790 if(this.config.adjustments){
38791 w += this.config.adjustments[0];
38794 if(h !== null && h > 0){
38795 this.el.setHeight(h);
38796 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38797 h -= this.el.getBorderWidth("tb");
38798 if(this.config.adjustments){
38799 h += this.config.adjustments[1];
38801 this.bodyEl.setHeight(h);
38803 h = this.tabs.syncHeight(h);
38806 if(this.panelSize){
38807 w = w !== null ? w : this.panelSize.width;
38808 h = h !== null ? h : this.panelSize.height;
38810 if(this.activePanel){
38811 var el = this.activePanel.getEl();
38812 w = w !== null ? w : el.getWidth();
38813 h = h !== null ? h : el.getHeight();
38814 this.panelSize = {width: w, height: h};
38815 this.activePanel.setSize(w, h);
38817 if(Roo.isIE && this.tabs){
38818 this.tabs.el.repaint();
38823 * Returns the container element for this region.
38824 * @return {Roo.Element}
38826 getEl : function(){
38831 * Hides this region.
38834 //if(!this.collapsed){
38835 this.el.dom.style.left = "-2000px";
38838 // this.collapsedEl.dom.style.left = "-2000px";
38839 // this.collapsedEl.hide();
38841 this.visible = false;
38842 this.fireEvent("visibilitychange", this, false);
38846 * Shows this region if it was previously hidden.
38849 //if(!this.collapsed){
38852 // this.collapsedEl.show();
38854 this.visible = true;
38855 this.fireEvent("visibilitychange", this, true);
38858 closeClicked : function(){
38859 if(this.activePanel){
38860 this.remove(this.activePanel);
38864 collapseClick : function(e){
38866 e.stopPropagation();
38869 e.stopPropagation();
38875 * Collapses this region.
38876 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38879 collapse : function(skipAnim, skipCheck = false){
38880 if(this.collapsed) {
38884 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38886 this.collapsed = true;
38888 this.split.el.hide();
38890 if(this.config.animate && skipAnim !== true){
38891 this.fireEvent("invalidated", this);
38892 this.animateCollapse();
38894 this.el.setLocation(-20000,-20000);
38896 this.collapsedEl.show();
38897 this.fireEvent("collapsed", this);
38898 this.fireEvent("invalidated", this);
38904 animateCollapse : function(){
38909 * Expands this region if it was previously collapsed.
38910 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38911 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38914 expand : function(e, skipAnim){
38916 e.stopPropagation();
38918 if(!this.collapsed || this.el.hasActiveFx()) {
38922 this.afterSlideIn();
38925 this.collapsed = false;
38926 if(this.config.animate && skipAnim !== true){
38927 this.animateExpand();
38931 this.split.el.show();
38933 this.collapsedEl.setLocation(-2000,-2000);
38934 this.collapsedEl.hide();
38935 this.fireEvent("invalidated", this);
38936 this.fireEvent("expanded", this);
38940 animateExpand : function(){
38944 initTabs : function()
38946 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38948 var ts = new Roo.bootstrap.panel.Tabs({
38949 el: this.bodyEl.dom,
38951 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38952 disableTooltips: this.config.disableTabTips,
38953 toolbar : this.config.toolbar
38956 if(this.config.hideTabs){
38957 ts.stripWrap.setDisplayed(false);
38960 ts.resizeTabs = this.config.resizeTabs === true;
38961 ts.minTabWidth = this.config.minTabWidth || 40;
38962 ts.maxTabWidth = this.config.maxTabWidth || 250;
38963 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38964 ts.monitorResize = false;
38965 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38966 ts.bodyEl.addClass('roo-layout-tabs-body');
38967 this.panels.each(this.initPanelAsTab, this);
38970 initPanelAsTab : function(panel){
38971 var ti = this.tabs.addTab(
38975 this.config.closeOnTab && panel.isClosable(),
38978 if(panel.tabTip !== undefined){
38979 ti.setTooltip(panel.tabTip);
38981 ti.on("activate", function(){
38982 this.setActivePanel(panel);
38985 if(this.config.closeOnTab){
38986 ti.on("beforeclose", function(t, e){
38988 this.remove(panel);
38992 panel.tabItem = ti;
38997 updatePanelTitle : function(panel, title)
38999 if(this.activePanel == panel){
39000 this.updateTitle(title);
39003 var ti = this.tabs.getTab(panel.getEl().id);
39005 if(panel.tabTip !== undefined){
39006 ti.setTooltip(panel.tabTip);
39011 updateTitle : function(title){
39012 if(this.titleTextEl && !this.config.title){
39013 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39017 setActivePanel : function(panel)
39019 panel = this.getPanel(panel);
39020 if(this.activePanel && this.activePanel != panel){
39021 if(this.activePanel.setActiveState(false) === false){
39025 this.activePanel = panel;
39026 panel.setActiveState(true);
39027 if(this.panelSize){
39028 panel.setSize(this.panelSize.width, this.panelSize.height);
39031 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39033 this.updateTitle(panel.getTitle());
39035 this.fireEvent("invalidated", this);
39037 this.fireEvent("panelactivated", this, panel);
39041 * Shows the specified panel.
39042 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39043 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39045 showPanel : function(panel)
39047 panel = this.getPanel(panel);
39050 var tab = this.tabs.getTab(panel.getEl().id);
39051 if(tab.isHidden()){
39052 this.tabs.unhideTab(tab.id);
39056 this.setActivePanel(panel);
39063 * Get the active panel for this region.
39064 * @return {Roo.ContentPanel} The active panel or null
39066 getActivePanel : function(){
39067 return this.activePanel;
39070 validateVisibility : function(){
39071 if(this.panels.getCount() < 1){
39072 this.updateTitle(" ");
39073 this.closeBtn.hide();
39076 if(!this.isVisible()){
39083 * Adds the passed ContentPanel(s) to this region.
39084 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39085 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39087 add : function(panel)
39089 if(arguments.length > 1){
39090 for(var i = 0, len = arguments.length; i < len; i++) {
39091 this.add(arguments[i]);
39096 // if we have not been rendered yet, then we can not really do much of this..
39097 if (!this.bodyEl) {
39098 this.unrendered_panels.push(panel);
39105 if(this.hasPanel(panel)){
39106 this.showPanel(panel);
39109 panel.setRegion(this);
39110 this.panels.add(panel);
39111 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39112 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39113 // and hide them... ???
39114 this.bodyEl.dom.appendChild(panel.getEl().dom);
39115 if(panel.background !== true){
39116 this.setActivePanel(panel);
39118 this.fireEvent("paneladded", this, panel);
39125 this.initPanelAsTab(panel);
39129 if(panel.background !== true){
39130 this.tabs.activate(panel.getEl().id);
39132 this.fireEvent("paneladded", this, panel);
39137 * Hides the tab for the specified panel.
39138 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39140 hidePanel : function(panel){
39141 if(this.tabs && (panel = this.getPanel(panel))){
39142 this.tabs.hideTab(panel.getEl().id);
39147 * Unhides the tab for a previously hidden panel.
39148 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39150 unhidePanel : function(panel){
39151 if(this.tabs && (panel = this.getPanel(panel))){
39152 this.tabs.unhideTab(panel.getEl().id);
39156 clearPanels : function(){
39157 while(this.panels.getCount() > 0){
39158 this.remove(this.panels.first());
39163 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39164 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39165 * @param {Boolean} preservePanel Overrides the config preservePanel option
39166 * @return {Roo.ContentPanel} The panel that was removed
39168 remove : function(panel, preservePanel)
39170 panel = this.getPanel(panel);
39175 this.fireEvent("beforeremove", this, panel, e);
39176 if(e.cancel === true){
39179 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39180 var panelId = panel.getId();
39181 this.panels.removeKey(panelId);
39183 document.body.appendChild(panel.getEl().dom);
39186 this.tabs.removeTab(panel.getEl().id);
39187 }else if (!preservePanel){
39188 this.bodyEl.dom.removeChild(panel.getEl().dom);
39190 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39191 var p = this.panels.first();
39192 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39193 tempEl.appendChild(p.getEl().dom);
39194 this.bodyEl.update("");
39195 this.bodyEl.dom.appendChild(p.getEl().dom);
39197 this.updateTitle(p.getTitle());
39199 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39200 this.setActivePanel(p);
39202 panel.setRegion(null);
39203 if(this.activePanel == panel){
39204 this.activePanel = null;
39206 if(this.config.autoDestroy !== false && preservePanel !== true){
39207 try{panel.destroy();}catch(e){}
39209 this.fireEvent("panelremoved", this, panel);
39214 * Returns the TabPanel component used by this region
39215 * @return {Roo.TabPanel}
39217 getTabs : function(){
39221 createTool : function(parentEl, className){
39222 var btn = Roo.DomHelper.append(parentEl, {
39224 cls: "x-layout-tools-button",
39227 cls: "roo-layout-tools-button-inner " + className,
39231 btn.addClassOnOver("roo-layout-tools-button-over");
39236 * Ext JS Library 1.1.1
39237 * Copyright(c) 2006-2007, Ext JS, LLC.
39239 * Originally Released Under LGPL - original licence link has changed is not relivant.
39242 * <script type="text/javascript">
39248 * @class Roo.SplitLayoutRegion
39249 * @extends Roo.LayoutRegion
39250 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39252 Roo.bootstrap.layout.Split = function(config){
39253 this.cursor = config.cursor;
39254 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39257 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39259 splitTip : "Drag to resize.",
39260 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39261 useSplitTips : false,
39263 applyConfig : function(config){
39264 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39267 onRender : function(ctr,pos) {
39269 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39270 if(!this.config.split){
39275 var splitEl = Roo.DomHelper.append(ctr.dom, {
39277 id: this.el.id + "-split",
39278 cls: "roo-layout-split roo-layout-split-"+this.position,
39281 /** The SplitBar for this region
39282 * @type Roo.SplitBar */
39283 // does not exist yet...
39284 Roo.log([this.position, this.orientation]);
39286 this.split = new Roo.bootstrap.SplitBar({
39287 dragElement : splitEl,
39288 resizingElement: this.el,
39289 orientation : this.orientation
39292 this.split.on("moved", this.onSplitMove, this);
39293 this.split.useShim = this.config.useShim === true;
39294 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39295 if(this.useSplitTips){
39296 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39298 //if(config.collapsible){
39299 // this.split.el.on("dblclick", this.collapse, this);
39302 if(typeof this.config.minSize != "undefined"){
39303 this.split.minSize = this.config.minSize;
39305 if(typeof this.config.maxSize != "undefined"){
39306 this.split.maxSize = this.config.maxSize;
39308 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39309 this.hideSplitter();
39314 getHMaxSize : function(){
39315 var cmax = this.config.maxSize || 10000;
39316 var center = this.mgr.getRegion("center");
39317 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39320 getVMaxSize : function(){
39321 var cmax = this.config.maxSize || 10000;
39322 var center = this.mgr.getRegion("center");
39323 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39326 onSplitMove : function(split, newSize){
39327 this.fireEvent("resized", this, newSize);
39331 * Returns the {@link Roo.SplitBar} for this region.
39332 * @return {Roo.SplitBar}
39334 getSplitBar : function(){
39339 this.hideSplitter();
39340 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39343 hideSplitter : function(){
39345 this.split.el.setLocation(-2000,-2000);
39346 this.split.el.hide();
39352 this.split.el.show();
39354 Roo.bootstrap.layout.Split.superclass.show.call(this);
39357 beforeSlide: function(){
39358 if(Roo.isGecko){// firefox overflow auto bug workaround
39359 this.bodyEl.clip();
39361 this.tabs.bodyEl.clip();
39363 if(this.activePanel){
39364 this.activePanel.getEl().clip();
39366 if(this.activePanel.beforeSlide){
39367 this.activePanel.beforeSlide();
39373 afterSlide : function(){
39374 if(Roo.isGecko){// firefox overflow auto bug workaround
39375 this.bodyEl.unclip();
39377 this.tabs.bodyEl.unclip();
39379 if(this.activePanel){
39380 this.activePanel.getEl().unclip();
39381 if(this.activePanel.afterSlide){
39382 this.activePanel.afterSlide();
39388 initAutoHide : function(){
39389 if(this.autoHide !== false){
39390 if(!this.autoHideHd){
39391 var st = new Roo.util.DelayedTask(this.slideIn, this);
39392 this.autoHideHd = {
39393 "mouseout": function(e){
39394 if(!e.within(this.el, true)){
39398 "mouseover" : function(e){
39404 this.el.on(this.autoHideHd);
39408 clearAutoHide : function(){
39409 if(this.autoHide !== false){
39410 this.el.un("mouseout", this.autoHideHd.mouseout);
39411 this.el.un("mouseover", this.autoHideHd.mouseover);
39415 clearMonitor : function(){
39416 Roo.get(document).un("click", this.slideInIf, this);
39419 // these names are backwards but not changed for compat
39420 slideOut : function(){
39421 if(this.isSlid || this.el.hasActiveFx()){
39424 this.isSlid = true;
39425 if(this.collapseBtn){
39426 this.collapseBtn.hide();
39428 this.closeBtnState = this.closeBtn.getStyle('display');
39429 this.closeBtn.hide();
39431 this.stickBtn.show();
39434 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39435 this.beforeSlide();
39436 this.el.setStyle("z-index", 10001);
39437 this.el.slideIn(this.getSlideAnchor(), {
39438 callback: function(){
39440 this.initAutoHide();
39441 Roo.get(document).on("click", this.slideInIf, this);
39442 this.fireEvent("slideshow", this);
39449 afterSlideIn : function(){
39450 this.clearAutoHide();
39451 this.isSlid = false;
39452 this.clearMonitor();
39453 this.el.setStyle("z-index", "");
39454 if(this.collapseBtn){
39455 this.collapseBtn.show();
39457 this.closeBtn.setStyle('display', this.closeBtnState);
39459 this.stickBtn.hide();
39461 this.fireEvent("slidehide", this);
39464 slideIn : function(cb){
39465 if(!this.isSlid || this.el.hasActiveFx()){
39469 this.isSlid = false;
39470 this.beforeSlide();
39471 this.el.slideOut(this.getSlideAnchor(), {
39472 callback: function(){
39473 this.el.setLeftTop(-10000, -10000);
39475 this.afterSlideIn();
39483 slideInIf : function(e){
39484 if(!e.within(this.el)){
39489 animateCollapse : function(){
39490 this.beforeSlide();
39491 this.el.setStyle("z-index", 20000);
39492 var anchor = this.getSlideAnchor();
39493 this.el.slideOut(anchor, {
39494 callback : function(){
39495 this.el.setStyle("z-index", "");
39496 this.collapsedEl.slideIn(anchor, {duration:.3});
39498 this.el.setLocation(-10000,-10000);
39500 this.fireEvent("collapsed", this);
39507 animateExpand : function(){
39508 this.beforeSlide();
39509 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39510 this.el.setStyle("z-index", 20000);
39511 this.collapsedEl.hide({
39514 this.el.slideIn(this.getSlideAnchor(), {
39515 callback : function(){
39516 this.el.setStyle("z-index", "");
39519 this.split.el.show();
39521 this.fireEvent("invalidated", this);
39522 this.fireEvent("expanded", this);
39550 getAnchor : function(){
39551 return this.anchors[this.position];
39554 getCollapseAnchor : function(){
39555 return this.canchors[this.position];
39558 getSlideAnchor : function(){
39559 return this.sanchors[this.position];
39562 getAlignAdj : function(){
39563 var cm = this.cmargins;
39564 switch(this.position){
39580 getExpandAdj : function(){
39581 var c = this.collapsedEl, cm = this.cmargins;
39582 switch(this.position){
39584 return [-(cm.right+c.getWidth()+cm.left), 0];
39587 return [cm.right+c.getWidth()+cm.left, 0];
39590 return [0, -(cm.top+cm.bottom+c.getHeight())];
39593 return [0, cm.top+cm.bottom+c.getHeight()];
39599 * Ext JS Library 1.1.1
39600 * Copyright(c) 2006-2007, Ext JS, LLC.
39602 * Originally Released Under LGPL - original licence link has changed is not relivant.
39605 * <script type="text/javascript">
39608 * These classes are private internal classes
39610 Roo.bootstrap.layout.Center = function(config){
39611 config.region = "center";
39612 Roo.bootstrap.layout.Region.call(this, config);
39613 this.visible = true;
39614 this.minWidth = config.minWidth || 20;
39615 this.minHeight = config.minHeight || 20;
39618 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39620 // center panel can't be hidden
39624 // center panel can't be hidden
39627 getMinWidth: function(){
39628 return this.minWidth;
39631 getMinHeight: function(){
39632 return this.minHeight;
39646 Roo.bootstrap.layout.North = function(config)
39648 config.region = 'north';
39649 config.cursor = 'n-resize';
39651 Roo.bootstrap.layout.Split.call(this, config);
39655 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39656 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39657 this.split.el.addClass("roo-layout-split-v");
39659 //var size = config.initialSize || config.height;
39660 //if(this.el && typeof size != "undefined"){
39661 // this.el.setHeight(size);
39664 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39666 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39669 onRender : function(ctr, pos)
39671 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39672 var size = this.config.initialSize || this.config.height;
39673 if(this.el && typeof size != "undefined"){
39674 this.el.setHeight(size);
39679 getBox : function(){
39680 if(this.collapsed){
39681 return this.collapsedEl.getBox();
39683 var box = this.el.getBox();
39685 box.height += this.split.el.getHeight();
39690 updateBox : function(box){
39691 if(this.split && !this.collapsed){
39692 box.height -= this.split.el.getHeight();
39693 this.split.el.setLeft(box.x);
39694 this.split.el.setTop(box.y+box.height);
39695 this.split.el.setWidth(box.width);
39697 if(this.collapsed){
39698 this.updateBody(box.width, null);
39700 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39708 Roo.bootstrap.layout.South = function(config){
39709 config.region = 'south';
39710 config.cursor = 's-resize';
39711 Roo.bootstrap.layout.Split.call(this, config);
39713 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39714 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39715 this.split.el.addClass("roo-layout-split-v");
39720 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39721 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39723 onRender : function(ctr, pos)
39725 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39726 var size = this.config.initialSize || this.config.height;
39727 if(this.el && typeof size != "undefined"){
39728 this.el.setHeight(size);
39733 getBox : function(){
39734 if(this.collapsed){
39735 return this.collapsedEl.getBox();
39737 var box = this.el.getBox();
39739 var sh = this.split.el.getHeight();
39746 updateBox : function(box){
39747 if(this.split && !this.collapsed){
39748 var sh = this.split.el.getHeight();
39751 this.split.el.setLeft(box.x);
39752 this.split.el.setTop(box.y-sh);
39753 this.split.el.setWidth(box.width);
39755 if(this.collapsed){
39756 this.updateBody(box.width, null);
39758 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39762 Roo.bootstrap.layout.East = function(config){
39763 config.region = "east";
39764 config.cursor = "e-resize";
39765 Roo.bootstrap.layout.Split.call(this, config);
39767 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39768 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39769 this.split.el.addClass("roo-layout-split-h");
39773 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39774 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39776 onRender : function(ctr, pos)
39778 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39779 var size = this.config.initialSize || this.config.width;
39780 if(this.el && typeof size != "undefined"){
39781 this.el.setWidth(size);
39786 getBox : function(){
39787 if(this.collapsed){
39788 return this.collapsedEl.getBox();
39790 var box = this.el.getBox();
39792 var sw = this.split.el.getWidth();
39799 updateBox : function(box){
39800 if(this.split && !this.collapsed){
39801 var sw = this.split.el.getWidth();
39803 this.split.el.setLeft(box.x);
39804 this.split.el.setTop(box.y);
39805 this.split.el.setHeight(box.height);
39808 if(this.collapsed){
39809 this.updateBody(null, box.height);
39811 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39815 Roo.bootstrap.layout.West = function(config){
39816 config.region = "west";
39817 config.cursor = "w-resize";
39819 Roo.bootstrap.layout.Split.call(this, config);
39821 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39822 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39823 this.split.el.addClass("roo-layout-split-h");
39827 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39828 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39830 onRender: function(ctr, pos)
39832 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39833 var size = this.config.initialSize || this.config.width;
39834 if(typeof size != "undefined"){
39835 this.el.setWidth(size);
39839 getBox : function(){
39840 if(this.collapsed){
39841 return this.collapsedEl.getBox();
39843 var box = this.el.getBox();
39844 if (box.width == 0) {
39845 box.width = this.config.width; // kludge?
39848 box.width += this.split.el.getWidth();
39853 updateBox : function(box){
39854 if(this.split && !this.collapsed){
39855 var sw = this.split.el.getWidth();
39857 this.split.el.setLeft(box.x+box.width);
39858 this.split.el.setTop(box.y);
39859 this.split.el.setHeight(box.height);
39861 if(this.collapsed){
39862 this.updateBody(null, box.height);
39864 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39866 });Roo.namespace("Roo.bootstrap.panel");/*
39868 * Ext JS Library 1.1.1
39869 * Copyright(c) 2006-2007, Ext JS, LLC.
39871 * Originally Released Under LGPL - original licence link has changed is not relivant.
39874 * <script type="text/javascript">
39877 * @class Roo.ContentPanel
39878 * @extends Roo.util.Observable
39879 * A basic ContentPanel element.
39880 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39881 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39882 * @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
39883 * @cfg {Boolean} closable True if the panel can be closed/removed
39884 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39885 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39886 * @cfg {Toolbar} toolbar A toolbar for this panel
39887 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39888 * @cfg {String} title The title for this panel
39889 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39890 * @cfg {String} url Calls {@link #setUrl} with this value
39891 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39892 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39893 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39894 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39895 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39896 * @cfg {Boolean} badges render the badges
39897 * @cfg {String} cls extra classes to use
39898 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39901 * Create a new ContentPanel.
39902 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39903 * @param {String/Object} config A string to set only the title or a config object
39904 * @param {String} content (optional) Set the HTML content for this panel
39905 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39907 Roo.bootstrap.panel.Content = function( config){
39909 this.tpl = config.tpl || false;
39911 var el = config.el;
39912 var content = config.content;
39914 if(config.autoCreate){ // xtype is available if this is called from factory
39917 this.el = Roo.get(el);
39918 if(!this.el && config && config.autoCreate){
39919 if(typeof config.autoCreate == "object"){
39920 if(!config.autoCreate.id){
39921 config.autoCreate.id = config.id||el;
39923 this.el = Roo.DomHelper.append(document.body,
39924 config.autoCreate, true);
39928 cls: (config.cls || '') +
39929 (config.background ? ' bg-' + config.background : '') +
39930 " roo-layout-inactive-content",
39933 if (config.iframe) {
39937 style : 'border: 0px',
39938 src : 'about:blank'
39944 elcfg.html = config.html;
39948 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39949 if (config.iframe) {
39950 this.iframeEl = this.el.select('iframe',true).first();
39955 this.closable = false;
39956 this.loaded = false;
39957 this.active = false;
39960 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39962 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39964 this.wrapEl = this.el; //this.el.wrap();
39966 if (config.toolbar.items) {
39967 ti = config.toolbar.items ;
39968 delete config.toolbar.items ;
39972 this.toolbar.render(this.wrapEl, 'before');
39973 for(var i =0;i < ti.length;i++) {
39974 // Roo.log(['add child', items[i]]);
39975 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39977 this.toolbar.items = nitems;
39978 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39979 delete config.toolbar;
39983 // xtype created footer. - not sure if will work as we normally have to render first..
39984 if (this.footer && !this.footer.el && this.footer.xtype) {
39985 if (!this.wrapEl) {
39986 this.wrapEl = this.el.wrap();
39989 this.footer.container = this.wrapEl.createChild();
39991 this.footer = Roo.factory(this.footer, Roo);
39996 if(typeof config == "string"){
39997 this.title = config;
39999 Roo.apply(this, config);
40003 this.resizeEl = Roo.get(this.resizeEl, true);
40005 this.resizeEl = this.el;
40007 // handle view.xtype
40015 * Fires when this panel is activated.
40016 * @param {Roo.ContentPanel} this
40020 * @event deactivate
40021 * Fires when this panel is activated.
40022 * @param {Roo.ContentPanel} this
40024 "deactivate" : true,
40028 * Fires when this panel is resized if fitToFrame is true.
40029 * @param {Roo.ContentPanel} this
40030 * @param {Number} width The width after any component adjustments
40031 * @param {Number} height The height after any component adjustments
40037 * Fires when this tab is created
40038 * @param {Roo.ContentPanel} this
40049 if(this.autoScroll && !this.iframe){
40050 this.resizeEl.setStyle("overflow", "auto");
40052 // fix randome scrolling
40053 //this.el.on('scroll', function() {
40054 // Roo.log('fix random scolling');
40055 // this.scrollTo('top',0);
40058 content = content || this.content;
40060 this.setContent(content);
40062 if(config && config.url){
40063 this.setUrl(this.url, this.params, this.loadOnce);
40068 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40070 if (this.view && typeof(this.view.xtype) != 'undefined') {
40071 this.view.el = this.el.appendChild(document.createElement("div"));
40072 this.view = Roo.factory(this.view);
40073 this.view.render && this.view.render(false, '');
40077 this.fireEvent('render', this);
40080 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40090 setRegion : function(region){
40091 this.region = region;
40092 this.setActiveClass(region && !this.background);
40096 setActiveClass: function(state)
40099 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40100 this.el.setStyle('position','relative');
40102 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40103 this.el.setStyle('position', 'absolute');
40108 * Returns the toolbar for this Panel if one was configured.
40109 * @return {Roo.Toolbar}
40111 getToolbar : function(){
40112 return this.toolbar;
40115 setActiveState : function(active)
40117 this.active = active;
40118 this.setActiveClass(active);
40120 if(this.fireEvent("deactivate", this) === false){
40125 this.fireEvent("activate", this);
40129 * Updates this panel's element (not for iframe)
40130 * @param {String} content The new content
40131 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40133 setContent : function(content, loadScripts){
40138 this.el.update(content, loadScripts);
40141 ignoreResize : function(w, h){
40142 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40145 this.lastSize = {width: w, height: h};
40150 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40151 * @return {Roo.UpdateManager} The UpdateManager
40153 getUpdateManager : function(){
40157 return this.el.getUpdateManager();
40160 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40161 * Does not work with IFRAME contents
40162 * @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:
40165 url: "your-url.php",
40166 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40167 callback: yourFunction,
40168 scope: yourObject, //(optional scope)
40171 text: "Loading...",
40177 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40178 * 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.
40179 * @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}
40180 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40181 * @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.
40182 * @return {Roo.ContentPanel} this
40190 var um = this.el.getUpdateManager();
40191 um.update.apply(um, arguments);
40197 * 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.
40198 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40199 * @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)
40200 * @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)
40201 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40203 setUrl : function(url, params, loadOnce){
40205 this.iframeEl.dom.src = url;
40209 if(this.refreshDelegate){
40210 this.removeListener("activate", this.refreshDelegate);
40212 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40213 this.on("activate", this.refreshDelegate);
40214 return this.el.getUpdateManager();
40217 _handleRefresh : function(url, params, loadOnce){
40218 if(!loadOnce || !this.loaded){
40219 var updater = this.el.getUpdateManager();
40220 updater.update(url, params, this._setLoaded.createDelegate(this));
40224 _setLoaded : function(){
40225 this.loaded = true;
40229 * Returns this panel's id
40232 getId : function(){
40237 * Returns this panel's element - used by regiosn to add.
40238 * @return {Roo.Element}
40240 getEl : function(){
40241 return this.wrapEl || this.el;
40246 adjustForComponents : function(width, height)
40248 //Roo.log('adjustForComponents ');
40249 if(this.resizeEl != this.el){
40250 width -= this.el.getFrameWidth('lr');
40251 height -= this.el.getFrameWidth('tb');
40254 var te = this.toolbar.getEl();
40255 te.setWidth(width);
40256 height -= te.getHeight();
40259 var te = this.footer.getEl();
40260 te.setWidth(width);
40261 height -= te.getHeight();
40265 if(this.adjustments){
40266 width += this.adjustments[0];
40267 height += this.adjustments[1];
40269 return {"width": width, "height": height};
40272 setSize : function(width, height){
40273 if(this.fitToFrame && !this.ignoreResize(width, height)){
40274 if(this.fitContainer && this.resizeEl != this.el){
40275 this.el.setSize(width, height);
40277 var size = this.adjustForComponents(width, height);
40279 this.iframeEl.setSize(width,height);
40282 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40283 this.fireEvent('resize', this, size.width, size.height);
40290 * Returns this panel's title
40293 getTitle : function(){
40295 if (typeof(this.title) != 'object') {
40300 for (var k in this.title) {
40301 if (!this.title.hasOwnProperty(k)) {
40305 if (k.indexOf('-') >= 0) {
40306 var s = k.split('-');
40307 for (var i = 0; i<s.length; i++) {
40308 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40311 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40318 * Set this panel's title
40319 * @param {String} title
40321 setTitle : function(title){
40322 this.title = title;
40324 this.region.updatePanelTitle(this, title);
40329 * Returns true is this panel was configured to be closable
40330 * @return {Boolean}
40332 isClosable : function(){
40333 return this.closable;
40336 beforeSlide : function(){
40338 this.resizeEl.clip();
40341 afterSlide : function(){
40343 this.resizeEl.unclip();
40347 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40348 * Will fail silently if the {@link #setUrl} method has not been called.
40349 * This does not activate the panel, just updates its content.
40351 refresh : function(){
40352 if(this.refreshDelegate){
40353 this.loaded = false;
40354 this.refreshDelegate();
40359 * Destroys this panel
40361 destroy : function(){
40362 this.el.removeAllListeners();
40363 var tempEl = document.createElement("span");
40364 tempEl.appendChild(this.el.dom);
40365 tempEl.innerHTML = "";
40371 * form - if the content panel contains a form - this is a reference to it.
40372 * @type {Roo.form.Form}
40376 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40377 * This contains a reference to it.
40383 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40393 * @param {Object} cfg Xtype definition of item to add.
40397 getChildContainer: function () {
40398 return this.getEl();
40403 var ret = new Roo.factory(cfg);
40408 if (cfg.xtype.match(/^Form$/)) {
40411 //if (this.footer) {
40412 // el = this.footer.container.insertSibling(false, 'before');
40414 el = this.el.createChild();
40417 this.form = new Roo.form.Form(cfg);
40420 if ( this.form.allItems.length) {
40421 this.form.render(el.dom);
40425 // should only have one of theses..
40426 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40427 // views.. should not be just added - used named prop 'view''
40429 cfg.el = this.el.appendChild(document.createElement("div"));
40432 var ret = new Roo.factory(cfg);
40434 ret.render && ret.render(false, ''); // render blank..
40444 * @class Roo.bootstrap.panel.Grid
40445 * @extends Roo.bootstrap.panel.Content
40447 * Create a new GridPanel.
40448 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40449 * @param {Object} config A the config object
40455 Roo.bootstrap.panel.Grid = function(config)
40459 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40460 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40462 config.el = this.wrapper;
40463 //this.el = this.wrapper;
40465 if (config.container) {
40466 // ctor'ed from a Border/panel.grid
40469 this.wrapper.setStyle("overflow", "hidden");
40470 this.wrapper.addClass('roo-grid-container');
40475 if(config.toolbar){
40476 var tool_el = this.wrapper.createChild();
40477 this.toolbar = Roo.factory(config.toolbar);
40479 if (config.toolbar.items) {
40480 ti = config.toolbar.items ;
40481 delete config.toolbar.items ;
40485 this.toolbar.render(tool_el);
40486 for(var i =0;i < ti.length;i++) {
40487 // Roo.log(['add child', items[i]]);
40488 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40490 this.toolbar.items = nitems;
40492 delete config.toolbar;
40495 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40496 config.grid.scrollBody = true;;
40497 config.grid.monitorWindowResize = false; // turn off autosizing
40498 config.grid.autoHeight = false;
40499 config.grid.autoWidth = false;
40501 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40503 if (config.background) {
40504 // render grid on panel activation (if panel background)
40505 this.on('activate', function(gp) {
40506 if (!gp.grid.rendered) {
40507 gp.grid.render(this.wrapper);
40508 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40513 this.grid.render(this.wrapper);
40514 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40517 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40518 // ??? needed ??? config.el = this.wrapper;
40523 // xtype created footer. - not sure if will work as we normally have to render first..
40524 if (this.footer && !this.footer.el && this.footer.xtype) {
40526 var ctr = this.grid.getView().getFooterPanel(true);
40527 this.footer.dataSource = this.grid.dataSource;
40528 this.footer = Roo.factory(this.footer, Roo);
40529 this.footer.render(ctr);
40539 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40540 getId : function(){
40541 return this.grid.id;
40545 * Returns the grid for this panel
40546 * @return {Roo.bootstrap.Table}
40548 getGrid : function(){
40552 setSize : function(width, height){
40553 if(!this.ignoreResize(width, height)){
40554 var grid = this.grid;
40555 var size = this.adjustForComponents(width, height);
40556 // tfoot is not a footer?
40559 var gridel = grid.getGridEl();
40560 gridel.setSize(size.width, size.height);
40562 var tbd = grid.getGridEl().select('tbody', true).first();
40563 var thd = grid.getGridEl().select('thead',true).first();
40564 var tbf= grid.getGridEl().select('tfoot', true).first();
40567 size.height -= tbf.getHeight();
40570 size.height -= thd.getHeight();
40573 tbd.setSize(size.width, size.height );
40574 // this is for the account management tab -seems to work there.
40575 var thd = grid.getGridEl().select('thead',true).first();
40577 // tbd.setSize(size.width, size.height - thd.getHeight());
40586 beforeSlide : function(){
40587 this.grid.getView().scroller.clip();
40590 afterSlide : function(){
40591 this.grid.getView().scroller.unclip();
40594 destroy : function(){
40595 this.grid.destroy();
40597 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40602 * @class Roo.bootstrap.panel.Nest
40603 * @extends Roo.bootstrap.panel.Content
40605 * Create a new Panel, that can contain a layout.Border.
40608 * @param {Roo.BorderLayout} layout The layout for this panel
40609 * @param {String/Object} config A string to set only the title or a config object
40611 Roo.bootstrap.panel.Nest = function(config)
40613 // construct with only one argument..
40614 /* FIXME - implement nicer consturctors
40615 if (layout.layout) {
40617 layout = config.layout;
40618 delete config.layout;
40620 if (layout.xtype && !layout.getEl) {
40621 // then layout needs constructing..
40622 layout = Roo.factory(layout, Roo);
40626 config.el = config.layout.getEl();
40628 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40630 config.layout.monitorWindowResize = false; // turn off autosizing
40631 this.layout = config.layout;
40632 this.layout.getEl().addClass("roo-layout-nested-layout");
40633 this.layout.parent = this;
40640 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40642 setSize : function(width, height){
40643 if(!this.ignoreResize(width, height)){
40644 var size = this.adjustForComponents(width, height);
40645 var el = this.layout.getEl();
40646 if (size.height < 1) {
40647 el.setWidth(size.width);
40649 el.setSize(size.width, size.height);
40651 var touch = el.dom.offsetWidth;
40652 this.layout.layout();
40653 // ie requires a double layout on the first pass
40654 if(Roo.isIE && !this.initialized){
40655 this.initialized = true;
40656 this.layout.layout();
40661 // activate all subpanels if not currently active..
40663 setActiveState : function(active){
40664 this.active = active;
40665 this.setActiveClass(active);
40668 this.fireEvent("deactivate", this);
40672 this.fireEvent("activate", this);
40673 // not sure if this should happen before or after..
40674 if (!this.layout) {
40675 return; // should not happen..
40678 for (var r in this.layout.regions) {
40679 reg = this.layout.getRegion(r);
40680 if (reg.getActivePanel()) {
40681 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40682 reg.setActivePanel(reg.getActivePanel());
40685 if (!reg.panels.length) {
40688 reg.showPanel(reg.getPanel(0));
40697 * Returns the nested BorderLayout for this panel
40698 * @return {Roo.BorderLayout}
40700 getLayout : function(){
40701 return this.layout;
40705 * Adds a xtype elements to the layout of the nested panel
40709 xtype : 'ContentPanel',
40716 xtype : 'NestedLayoutPanel',
40722 items : [ ... list of content panels or nested layout panels.. ]
40726 * @param {Object} cfg Xtype definition of item to add.
40728 addxtype : function(cfg) {
40729 return this.layout.addxtype(cfg);
40734 * Ext JS Library 1.1.1
40735 * Copyright(c) 2006-2007, Ext JS, LLC.
40737 * Originally Released Under LGPL - original licence link has changed is not relivant.
40740 * <script type="text/javascript">
40743 * @class Roo.TabPanel
40744 * @extends Roo.util.Observable
40745 * A lightweight tab container.
40749 // basic tabs 1, built from existing content
40750 var tabs = new Roo.TabPanel("tabs1");
40751 tabs.addTab("script", "View Script");
40752 tabs.addTab("markup", "View Markup");
40753 tabs.activate("script");
40755 // more advanced tabs, built from javascript
40756 var jtabs = new Roo.TabPanel("jtabs");
40757 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40759 // set up the UpdateManager
40760 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40761 var updater = tab2.getUpdateManager();
40762 updater.setDefaultUrl("ajax1.htm");
40763 tab2.on('activate', updater.refresh, updater, true);
40765 // Use setUrl for Ajax loading
40766 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40767 tab3.setUrl("ajax2.htm", null, true);
40770 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40773 jtabs.activate("jtabs-1");
40776 * Create a new TabPanel.
40777 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40778 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40780 Roo.bootstrap.panel.Tabs = function(config){
40782 * The container element for this TabPanel.
40783 * @type Roo.Element
40785 this.el = Roo.get(config.el);
40788 if(typeof config == "boolean"){
40789 this.tabPosition = config ? "bottom" : "top";
40791 Roo.apply(this, config);
40795 if(this.tabPosition == "bottom"){
40796 // if tabs are at the bottom = create the body first.
40797 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40798 this.el.addClass("roo-tabs-bottom");
40800 // next create the tabs holders
40802 if (this.tabPosition == "west"){
40804 var reg = this.region; // fake it..
40806 if (!reg.mgr.parent) {
40809 reg = reg.mgr.parent.region;
40811 Roo.log("got nest?");
40813 if (reg.mgr.getRegion('west')) {
40814 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40815 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40816 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40817 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40818 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40826 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40827 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40828 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40829 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40834 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40837 // finally - if tabs are at the top, then create the body last..
40838 if(this.tabPosition != "bottom"){
40839 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40840 * @type Roo.Element
40842 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40843 this.el.addClass("roo-tabs-top");
40847 this.bodyEl.setStyle("position", "relative");
40849 this.active = null;
40850 this.activateDelegate = this.activate.createDelegate(this);
40855 * Fires when the active tab changes
40856 * @param {Roo.TabPanel} this
40857 * @param {Roo.TabPanelItem} activePanel The new active tab
40861 * @event beforetabchange
40862 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40863 * @param {Roo.TabPanel} this
40864 * @param {Object} e Set cancel to true on this object to cancel the tab change
40865 * @param {Roo.TabPanelItem} tab The tab being changed to
40867 "beforetabchange" : true
40870 Roo.EventManager.onWindowResize(this.onResize, this);
40871 this.cpad = this.el.getPadding("lr");
40872 this.hiddenCount = 0;
40875 // toolbar on the tabbar support...
40876 if (this.toolbar) {
40877 alert("no toolbar support yet");
40878 this.toolbar = false;
40880 var tcfg = this.toolbar;
40881 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40882 this.toolbar = new Roo.Toolbar(tcfg);
40883 if (Roo.isSafari) {
40884 var tbl = tcfg.container.child('table', true);
40885 tbl.setAttribute('width', '100%');
40893 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40896 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40898 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40900 tabPosition : "top",
40902 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40904 currentTabWidth : 0,
40906 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40910 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40914 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40916 preferredTabWidth : 175,
40918 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40920 resizeTabs : false,
40922 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40924 monitorResize : true,
40926 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40928 toolbar : false, // set by caller..
40930 region : false, /// set by caller
40932 disableTooltips : true, // not used yet...
40935 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40936 * @param {String} id The id of the div to use <b>or create</b>
40937 * @param {String} text The text for the tab
40938 * @param {String} content (optional) Content to put in the TabPanelItem body
40939 * @param {Boolean} closable (optional) True to create a close icon on the tab
40940 * @return {Roo.TabPanelItem} The created TabPanelItem
40942 addTab : function(id, text, content, closable, tpl)
40944 var item = new Roo.bootstrap.panel.TabItem({
40948 closable : closable,
40951 this.addTabItem(item);
40953 item.setContent(content);
40959 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40960 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40961 * @return {Roo.TabPanelItem}
40963 getTab : function(id){
40964 return this.items[id];
40968 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40969 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40971 hideTab : function(id){
40972 var t = this.items[id];
40975 this.hiddenCount++;
40976 this.autoSizeTabs();
40981 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40982 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40984 unhideTab : function(id){
40985 var t = this.items[id];
40987 t.setHidden(false);
40988 this.hiddenCount--;
40989 this.autoSizeTabs();
40994 * Adds an existing {@link Roo.TabPanelItem}.
40995 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40997 addTabItem : function(item)
40999 this.items[item.id] = item;
41000 this.items.push(item);
41001 this.autoSizeTabs();
41002 // if(this.resizeTabs){
41003 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41004 // this.autoSizeTabs();
41006 // item.autoSize();
41011 * Removes a {@link Roo.TabPanelItem}.
41012 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41014 removeTab : function(id){
41015 var items = this.items;
41016 var tab = items[id];
41017 if(!tab) { return; }
41018 var index = items.indexOf(tab);
41019 if(this.active == tab && items.length > 1){
41020 var newTab = this.getNextAvailable(index);
41025 this.stripEl.dom.removeChild(tab.pnode.dom);
41026 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41027 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41029 items.splice(index, 1);
41030 delete this.items[tab.id];
41031 tab.fireEvent("close", tab);
41032 tab.purgeListeners();
41033 this.autoSizeTabs();
41036 getNextAvailable : function(start){
41037 var items = this.items;
41039 // look for a next tab that will slide over to
41040 // replace the one being removed
41041 while(index < items.length){
41042 var item = items[++index];
41043 if(item && !item.isHidden()){
41047 // if one isn't found select the previous tab (on the left)
41050 var item = items[--index];
41051 if(item && !item.isHidden()){
41059 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41060 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41062 disableTab : function(id){
41063 var tab = this.items[id];
41064 if(tab && this.active != tab){
41070 * Enables a {@link Roo.TabPanelItem} that is disabled.
41071 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41073 enableTab : function(id){
41074 var tab = this.items[id];
41079 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41080 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41081 * @return {Roo.TabPanelItem} The TabPanelItem.
41083 activate : function(id)
41085 //Roo.log('activite:' + id);
41087 var tab = this.items[id];
41091 if(tab == this.active || tab.disabled){
41095 this.fireEvent("beforetabchange", this, e, tab);
41096 if(e.cancel !== true && !tab.disabled){
41098 this.active.hide();
41100 this.active = this.items[id];
41101 this.active.show();
41102 this.fireEvent("tabchange", this, this.active);
41108 * Gets the active {@link Roo.TabPanelItem}.
41109 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41111 getActiveTab : function(){
41112 return this.active;
41116 * Updates the tab body element to fit the height of the container element
41117 * for overflow scrolling
41118 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41120 syncHeight : function(targetHeight){
41121 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41122 var bm = this.bodyEl.getMargins();
41123 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41124 this.bodyEl.setHeight(newHeight);
41128 onResize : function(){
41129 if(this.monitorResize){
41130 this.autoSizeTabs();
41135 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41137 beginUpdate : function(){
41138 this.updating = true;
41142 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41144 endUpdate : function(){
41145 this.updating = false;
41146 this.autoSizeTabs();
41150 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41152 autoSizeTabs : function()
41154 var count = this.items.length;
41155 var vcount = count - this.hiddenCount;
41158 this.stripEl.hide();
41160 this.stripEl.show();
41163 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41168 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41169 var availWidth = Math.floor(w / vcount);
41170 var b = this.stripBody;
41171 if(b.getWidth() > w){
41172 var tabs = this.items;
41173 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41174 if(availWidth < this.minTabWidth){
41175 /*if(!this.sleft){ // incomplete scrolling code
41176 this.createScrollButtons();
41179 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41182 if(this.currentTabWidth < this.preferredTabWidth){
41183 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41189 * Returns the number of tabs in this TabPanel.
41192 getCount : function(){
41193 return this.items.length;
41197 * Resizes all the tabs to the passed width
41198 * @param {Number} The new width
41200 setTabWidth : function(width){
41201 this.currentTabWidth = width;
41202 for(var i = 0, len = this.items.length; i < len; i++) {
41203 if(!this.items[i].isHidden()) {
41204 this.items[i].setWidth(width);
41210 * Destroys this TabPanel
41211 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41213 destroy : function(removeEl){
41214 Roo.EventManager.removeResizeListener(this.onResize, this);
41215 for(var i = 0, len = this.items.length; i < len; i++){
41216 this.items[i].purgeListeners();
41218 if(removeEl === true){
41219 this.el.update("");
41224 createStrip : function(container)
41226 var strip = document.createElement("nav");
41227 strip.className = Roo.bootstrap.version == 4 ?
41228 "navbar-light bg-light" :
41229 "navbar navbar-default"; //"x-tabs-wrap";
41230 container.appendChild(strip);
41234 createStripList : function(strip)
41236 // div wrapper for retard IE
41237 // returns the "tr" element.
41238 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41239 //'<div class="x-tabs-strip-wrap">'+
41240 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41241 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41242 return strip.firstChild; //.firstChild.firstChild.firstChild;
41244 createBody : function(container)
41246 var body = document.createElement("div");
41247 Roo.id(body, "tab-body");
41248 //Roo.fly(body).addClass("x-tabs-body");
41249 Roo.fly(body).addClass("tab-content");
41250 container.appendChild(body);
41253 createItemBody :function(bodyEl, id){
41254 var body = Roo.getDom(id);
41256 body = document.createElement("div");
41259 //Roo.fly(body).addClass("x-tabs-item-body");
41260 Roo.fly(body).addClass("tab-pane");
41261 bodyEl.insertBefore(body, bodyEl.firstChild);
41265 createStripElements : function(stripEl, text, closable, tpl)
41267 var td = document.createElement("li"); // was td..
41268 td.className = 'nav-item';
41270 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41273 stripEl.appendChild(td);
41275 td.className = "x-tabs-closable";
41276 if(!this.closeTpl){
41277 this.closeTpl = new Roo.Template(
41278 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41279 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41280 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41283 var el = this.closeTpl.overwrite(td, {"text": text});
41284 var close = el.getElementsByTagName("div")[0];
41285 var inner = el.getElementsByTagName("em")[0];
41286 return {"el": el, "close": close, "inner": inner};
41289 // not sure what this is..
41290 // if(!this.tabTpl){
41291 //this.tabTpl = new Roo.Template(
41292 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41293 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41295 // this.tabTpl = new Roo.Template(
41296 // '<a href="#">' +
41297 // '<span unselectable="on"' +
41298 // (this.disableTooltips ? '' : ' title="{text}"') +
41299 // ' >{text}</span></a>'
41305 var template = tpl || this.tabTpl || false;
41308 template = new Roo.Template(
41309 Roo.bootstrap.version == 4 ?
41311 '<a class="nav-link" href="#" unselectable="on"' +
41312 (this.disableTooltips ? '' : ' title="{text}"') +
41315 '<a class="nav-link" href="#">' +
41316 '<span unselectable="on"' +
41317 (this.disableTooltips ? '' : ' title="{text}"') +
41318 ' >{text}</span></a>'
41323 switch (typeof(template)) {
41327 template = new Roo.Template(template);
41333 var el = template.overwrite(td, {"text": text});
41335 var inner = el.getElementsByTagName("span")[0];
41337 return {"el": el, "inner": inner};
41345 * @class Roo.TabPanelItem
41346 * @extends Roo.util.Observable
41347 * Represents an individual item (tab plus body) in a TabPanel.
41348 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41349 * @param {String} id The id of this TabPanelItem
41350 * @param {String} text The text for the tab of this TabPanelItem
41351 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41353 Roo.bootstrap.panel.TabItem = function(config){
41355 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41356 * @type Roo.TabPanel
41358 this.tabPanel = config.panel;
41360 * The id for this TabPanelItem
41363 this.id = config.id;
41365 this.disabled = false;
41367 this.text = config.text;
41369 this.loaded = false;
41370 this.closable = config.closable;
41373 * The body element for this TabPanelItem.
41374 * @type Roo.Element
41376 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41377 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41378 this.bodyEl.setStyle("display", "block");
41379 this.bodyEl.setStyle("zoom", "1");
41380 //this.hideAction();
41382 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41384 this.el = Roo.get(els.el);
41385 this.inner = Roo.get(els.inner, true);
41386 this.textEl = Roo.bootstrap.version == 4 ?
41387 this.el : Roo.get(this.el.dom.firstChild, true);
41389 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41390 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41393 // this.el.on("mousedown", this.onTabMouseDown, this);
41394 this.el.on("click", this.onTabClick, this);
41396 if(config.closable){
41397 var c = Roo.get(els.close, true);
41398 c.dom.title = this.closeText;
41399 c.addClassOnOver("close-over");
41400 c.on("click", this.closeClick, this);
41406 * Fires when this tab becomes the active tab.
41407 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41408 * @param {Roo.TabPanelItem} this
41412 * @event beforeclose
41413 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41414 * @param {Roo.TabPanelItem} this
41415 * @param {Object} e Set cancel to true on this object to cancel the close.
41417 "beforeclose": true,
41420 * Fires when this tab is closed.
41421 * @param {Roo.TabPanelItem} this
41425 * @event deactivate
41426 * Fires when this tab is no longer the active tab.
41427 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41428 * @param {Roo.TabPanelItem} this
41430 "deactivate" : true
41432 this.hidden = false;
41434 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41437 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41439 purgeListeners : function(){
41440 Roo.util.Observable.prototype.purgeListeners.call(this);
41441 this.el.removeAllListeners();
41444 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41447 this.status_node.addClass("active");
41450 this.tabPanel.stripWrap.repaint();
41452 this.fireEvent("activate", this.tabPanel, this);
41456 * Returns true if this tab is the active tab.
41457 * @return {Boolean}
41459 isActive : function(){
41460 return this.tabPanel.getActiveTab() == this;
41464 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41467 this.status_node.removeClass("active");
41469 this.fireEvent("deactivate", this.tabPanel, this);
41472 hideAction : function(){
41473 this.bodyEl.hide();
41474 this.bodyEl.setStyle("position", "absolute");
41475 this.bodyEl.setLeft("-20000px");
41476 this.bodyEl.setTop("-20000px");
41479 showAction : function(){
41480 this.bodyEl.setStyle("position", "relative");
41481 this.bodyEl.setTop("");
41482 this.bodyEl.setLeft("");
41483 this.bodyEl.show();
41487 * Set the tooltip for the tab.
41488 * @param {String} tooltip The tab's tooltip
41490 setTooltip : function(text){
41491 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41492 this.textEl.dom.qtip = text;
41493 this.textEl.dom.removeAttribute('title');
41495 this.textEl.dom.title = text;
41499 onTabClick : function(e){
41500 e.preventDefault();
41501 this.tabPanel.activate(this.id);
41504 onTabMouseDown : function(e){
41505 e.preventDefault();
41506 this.tabPanel.activate(this.id);
41509 getWidth : function(){
41510 return this.inner.getWidth();
41513 setWidth : function(width){
41514 var iwidth = width - this.linode.getPadding("lr");
41515 this.inner.setWidth(iwidth);
41516 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41517 this.linode.setWidth(width);
41521 * Show or hide the tab
41522 * @param {Boolean} hidden True to hide or false to show.
41524 setHidden : function(hidden){
41525 this.hidden = hidden;
41526 this.linode.setStyle("display", hidden ? "none" : "");
41530 * Returns true if this tab is "hidden"
41531 * @return {Boolean}
41533 isHidden : function(){
41534 return this.hidden;
41538 * Returns the text for this tab
41541 getText : function(){
41545 autoSize : function(){
41546 //this.el.beginMeasure();
41547 this.textEl.setWidth(1);
41549 * #2804 [new] Tabs in Roojs
41550 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41552 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41553 //this.el.endMeasure();
41557 * Sets the text for the tab (Note: this also sets the tooltip text)
41558 * @param {String} text The tab's text and tooltip
41560 setText : function(text){
41562 this.textEl.update(text);
41563 this.setTooltip(text);
41564 //if(!this.tabPanel.resizeTabs){
41565 // this.autoSize();
41569 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41571 activate : function(){
41572 this.tabPanel.activate(this.id);
41576 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41578 disable : function(){
41579 if(this.tabPanel.active != this){
41580 this.disabled = true;
41581 this.status_node.addClass("disabled");
41586 * Enables this TabPanelItem if it was previously disabled.
41588 enable : function(){
41589 this.disabled = false;
41590 this.status_node.removeClass("disabled");
41594 * Sets the content for this TabPanelItem.
41595 * @param {String} content The content
41596 * @param {Boolean} loadScripts true to look for and load scripts
41598 setContent : function(content, loadScripts){
41599 this.bodyEl.update(content, loadScripts);
41603 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41604 * @return {Roo.UpdateManager} The UpdateManager
41606 getUpdateManager : function(){
41607 return this.bodyEl.getUpdateManager();
41611 * Set a URL to be used to load the content for this TabPanelItem.
41612 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41613 * @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)
41614 * @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)
41615 * @return {Roo.UpdateManager} The UpdateManager
41617 setUrl : function(url, params, loadOnce){
41618 if(this.refreshDelegate){
41619 this.un('activate', this.refreshDelegate);
41621 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41622 this.on("activate", this.refreshDelegate);
41623 return this.bodyEl.getUpdateManager();
41627 _handleRefresh : function(url, params, loadOnce){
41628 if(!loadOnce || !this.loaded){
41629 var updater = this.bodyEl.getUpdateManager();
41630 updater.update(url, params, this._setLoaded.createDelegate(this));
41635 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41636 * Will fail silently if the setUrl method has not been called.
41637 * This does not activate the panel, just updates its content.
41639 refresh : function(){
41640 if(this.refreshDelegate){
41641 this.loaded = false;
41642 this.refreshDelegate();
41647 _setLoaded : function(){
41648 this.loaded = true;
41652 closeClick : function(e){
41655 this.fireEvent("beforeclose", this, o);
41656 if(o.cancel !== true){
41657 this.tabPanel.removeTab(this.id);
41661 * The text displayed in the tooltip for the close icon.
41664 closeText : "Close this tab"
41667 * This script refer to:
41668 * Title: International Telephone Input
41669 * Author: Jack O'Connor
41670 * Code version: v12.1.12
41671 * Availability: https://github.com/jackocnr/intl-tel-input.git
41674 Roo.bootstrap.PhoneInputData = function() {
41677 "Afghanistan (افغانستان)",
41682 "Albania (Shqipëri)",
41687 "Algeria (الجزائر)",
41712 "Antigua and Barbuda",
41722 "Armenia (Հայաստան)",
41738 "Austria (Österreich)",
41743 "Azerbaijan (Azərbaycan)",
41753 "Bahrain (البحرين)",
41758 "Bangladesh (বাংলাদেশ)",
41768 "Belarus (Беларусь)",
41773 "Belgium (België)",
41803 "Bosnia and Herzegovina (Босна и Херцеговина)",
41818 "British Indian Ocean Territory",
41823 "British Virgin Islands",
41833 "Bulgaria (България)",
41843 "Burundi (Uburundi)",
41848 "Cambodia (កម្ពុជា)",
41853 "Cameroon (Cameroun)",
41862 ["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"]
41865 "Cape Verde (Kabu Verdi)",
41870 "Caribbean Netherlands",
41881 "Central African Republic (République centrafricaine)",
41901 "Christmas Island",
41907 "Cocos (Keeling) Islands",
41918 "Comoros (جزر القمر)",
41923 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41928 "Congo (Republic) (Congo-Brazzaville)",
41948 "Croatia (Hrvatska)",
41969 "Czech Republic (Česká republika)",
41974 "Denmark (Danmark)",
41989 "Dominican Republic (República Dominicana)",
41993 ["809", "829", "849"]
42011 "Equatorial Guinea (Guinea Ecuatorial)",
42031 "Falkland Islands (Islas Malvinas)",
42036 "Faroe Islands (Føroyar)",
42057 "French Guiana (Guyane française)",
42062 "French Polynesia (Polynésie française)",
42077 "Georgia (საქართველო)",
42082 "Germany (Deutschland)",
42102 "Greenland (Kalaallit Nunaat)",
42139 "Guinea-Bissau (Guiné Bissau)",
42164 "Hungary (Magyarország)",
42169 "Iceland (Ísland)",
42189 "Iraq (العراق)",
42205 "Israel (ישראל)",
42232 "Jordan (الأردن)",
42237 "Kazakhstan (Казахстан)",
42258 "Kuwait (الكويت)",
42263 "Kyrgyzstan (Кыргызстан)",
42273 "Latvia (Latvija)",
42278 "Lebanon (لبنان)",
42293 "Libya (ليبيا)",
42303 "Lithuania (Lietuva)",
42318 "Macedonia (FYROM) (Македонија)",
42323 "Madagascar (Madagasikara)",
42353 "Marshall Islands",
42363 "Mauritania (موريتانيا)",
42368 "Mauritius (Moris)",
42389 "Moldova (Republica Moldova)",
42399 "Mongolia (Монгол)",
42404 "Montenegro (Crna Gora)",
42414 "Morocco (المغرب)",
42420 "Mozambique (Moçambique)",
42425 "Myanmar (Burma) (မြန်မာ)",
42430 "Namibia (Namibië)",
42445 "Netherlands (Nederland)",
42450 "New Caledonia (Nouvelle-Calédonie)",
42485 "North Korea (조선 민주주의 인민 공화국)",
42490 "Northern Mariana Islands",
42506 "Pakistan (پاکستان)",
42516 "Palestine (فلسطين)",
42526 "Papua New Guinea",
42568 "Réunion (La Réunion)",
42574 "Romania (România)",
42590 "Saint Barthélemy",
42601 "Saint Kitts and Nevis",
42611 "Saint Martin (Saint-Martin (partie française))",
42617 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42622 "Saint Vincent and the Grenadines",
42637 "São Tomé and Príncipe (São Tomé e Príncipe)",
42642 "Saudi Arabia (المملكة العربية السعودية)",
42647 "Senegal (Sénégal)",
42677 "Slovakia (Slovensko)",
42682 "Slovenia (Slovenija)",
42692 "Somalia (Soomaaliya)",
42702 "South Korea (대한민국)",
42707 "South Sudan (جنوب السودان)",
42717 "Sri Lanka (ශ්රී ලංකාව)",
42722 "Sudan (السودان)",
42732 "Svalbard and Jan Mayen",
42743 "Sweden (Sverige)",
42748 "Switzerland (Schweiz)",
42753 "Syria (سوريا)",
42798 "Trinidad and Tobago",
42803 "Tunisia (تونس)",
42808 "Turkey (Türkiye)",
42818 "Turks and Caicos Islands",
42828 "U.S. Virgin Islands",
42838 "Ukraine (Україна)",
42843 "United Arab Emirates (الإمارات العربية المتحدة)",
42865 "Uzbekistan (Oʻzbekiston)",
42875 "Vatican City (Città del Vaticano)",
42886 "Vietnam (Việt Nam)",
42891 "Wallis and Futuna (Wallis-et-Futuna)",
42896 "Western Sahara (الصحراء الغربية)",
42902 "Yemen (اليمن)",
42926 * This script refer to:
42927 * Title: International Telephone Input
42928 * Author: Jack O'Connor
42929 * Code version: v12.1.12
42930 * Availability: https://github.com/jackocnr/intl-tel-input.git
42934 * @class Roo.bootstrap.PhoneInput
42935 * @extends Roo.bootstrap.TriggerField
42936 * An input with International dial-code selection
42938 * @cfg {String} defaultDialCode default '+852'
42939 * @cfg {Array} preferedCountries default []
42942 * Create a new PhoneInput.
42943 * @param {Object} config Configuration options
42946 Roo.bootstrap.PhoneInput = function(config) {
42947 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42950 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42952 listWidth: undefined,
42954 selectedClass: 'active',
42956 invalidClass : "has-warning",
42958 validClass: 'has-success',
42960 allowed: '0123456789',
42965 * @cfg {String} defaultDialCode The default dial code when initializing the input
42967 defaultDialCode: '+852',
42970 * @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
42972 preferedCountries: false,
42974 getAutoCreate : function()
42976 var data = Roo.bootstrap.PhoneInputData();
42977 var align = this.labelAlign || this.parentLabelAlign();
42980 this.allCountries = [];
42981 this.dialCodeMapping = [];
42983 for (var i = 0; i < data.length; i++) {
42985 this.allCountries[i] = {
42989 priority: c[3] || 0,
42990 areaCodes: c[4] || null
42992 this.dialCodeMapping[c[2]] = {
42995 priority: c[3] || 0,
42996 areaCodes: c[4] || null
43008 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43009 maxlength: this.max_length,
43010 cls : 'form-control tel-input',
43011 autocomplete: 'new-password'
43014 var hiddenInput = {
43017 cls: 'hidden-tel-input'
43021 hiddenInput.name = this.name;
43024 if (this.disabled) {
43025 input.disabled = true;
43028 var flag_container = {
43045 cls: this.hasFeedback ? 'has-feedback' : '',
43051 cls: 'dial-code-holder',
43058 cls: 'roo-select2-container input-group',
43065 if (this.fieldLabel.length) {
43068 tooltip: 'This field is required'
43074 cls: 'control-label',
43080 html: this.fieldLabel
43083 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43089 if(this.indicatorpos == 'right') {
43090 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43097 if(align == 'left') {
43105 if(this.labelWidth > 12){
43106 label.style = "width: " + this.labelWidth + 'px';
43108 if(this.labelWidth < 13 && this.labelmd == 0){
43109 this.labelmd = this.labelWidth;
43111 if(this.labellg > 0){
43112 label.cls += ' col-lg-' + this.labellg;
43113 input.cls += ' col-lg-' + (12 - this.labellg);
43115 if(this.labelmd > 0){
43116 label.cls += ' col-md-' + this.labelmd;
43117 container.cls += ' col-md-' + (12 - this.labelmd);
43119 if(this.labelsm > 0){
43120 label.cls += ' col-sm-' + this.labelsm;
43121 container.cls += ' col-sm-' + (12 - this.labelsm);
43123 if(this.labelxs > 0){
43124 label.cls += ' col-xs-' + this.labelxs;
43125 container.cls += ' col-xs-' + (12 - this.labelxs);
43135 var settings = this;
43137 ['xs','sm','md','lg'].map(function(size){
43138 if (settings[size]) {
43139 cfg.cls += ' col-' + size + '-' + settings[size];
43143 this.store = new Roo.data.Store({
43144 proxy : new Roo.data.MemoryProxy({}),
43145 reader : new Roo.data.JsonReader({
43156 'name' : 'dialCode',
43160 'name' : 'priority',
43164 'name' : 'areaCodes',
43171 if(!this.preferedCountries) {
43172 this.preferedCountries = [
43179 var p = this.preferedCountries.reverse();
43182 for (var i = 0; i < p.length; i++) {
43183 for (var j = 0; j < this.allCountries.length; j++) {
43184 if(this.allCountries[j].iso2 == p[i]) {
43185 var t = this.allCountries[j];
43186 this.allCountries.splice(j,1);
43187 this.allCountries.unshift(t);
43193 this.store.proxy.data = {
43195 data: this.allCountries
43201 initEvents : function()
43204 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43206 this.indicator = this.indicatorEl();
43207 this.flag = this.flagEl();
43208 this.dialCodeHolder = this.dialCodeHolderEl();
43210 this.trigger = this.el.select('div.flag-box',true).first();
43211 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43216 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43217 _this.list.setWidth(lw);
43220 this.list.on('mouseover', this.onViewOver, this);
43221 this.list.on('mousemove', this.onViewMove, this);
43222 this.inputEl().on("keyup", this.onKeyUp, this);
43223 this.inputEl().on("keypress", this.onKeyPress, this);
43225 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43227 this.view = new Roo.View(this.list, this.tpl, {
43228 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43231 this.view.on('click', this.onViewClick, this);
43232 this.setValue(this.defaultDialCode);
43235 onTriggerClick : function(e)
43237 Roo.log('trigger click');
43242 if(this.isExpanded()){
43244 this.hasFocus = false;
43246 this.store.load({});
43247 this.hasFocus = true;
43252 isExpanded : function()
43254 return this.list.isVisible();
43257 collapse : function()
43259 if(!this.isExpanded()){
43263 Roo.get(document).un('mousedown', this.collapseIf, this);
43264 Roo.get(document).un('mousewheel', this.collapseIf, this);
43265 this.fireEvent('collapse', this);
43269 expand : function()
43273 if(this.isExpanded() || !this.hasFocus){
43277 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43278 this.list.setWidth(lw);
43281 this.restrictHeight();
43283 Roo.get(document).on('mousedown', this.collapseIf, this);
43284 Roo.get(document).on('mousewheel', this.collapseIf, this);
43286 this.fireEvent('expand', this);
43289 restrictHeight : function()
43291 this.list.alignTo(this.inputEl(), this.listAlign);
43292 this.list.alignTo(this.inputEl(), this.listAlign);
43295 onViewOver : function(e, t)
43297 if(this.inKeyMode){
43300 var item = this.view.findItemFromChild(t);
43303 var index = this.view.indexOf(item);
43304 this.select(index, false);
43309 onViewClick : function(view, doFocus, el, e)
43311 var index = this.view.getSelectedIndexes()[0];
43313 var r = this.store.getAt(index);
43316 this.onSelect(r, index);
43318 if(doFocus !== false && !this.blockFocus){
43319 this.inputEl().focus();
43323 onViewMove : function(e, t)
43325 this.inKeyMode = false;
43328 select : function(index, scrollIntoView)
43330 this.selectedIndex = index;
43331 this.view.select(index);
43332 if(scrollIntoView !== false){
43333 var el = this.view.getNode(index);
43335 this.list.scrollChildIntoView(el, false);
43340 createList : function()
43342 this.list = Roo.get(document.body).createChild({
43344 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43345 style: 'display:none'
43348 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43351 collapseIf : function(e)
43353 var in_combo = e.within(this.el);
43354 var in_list = e.within(this.list);
43355 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43357 if (in_combo || in_list || is_list) {
43363 onSelect : function(record, index)
43365 if(this.fireEvent('beforeselect', this, record, index) !== false){
43367 this.setFlagClass(record.data.iso2);
43368 this.setDialCode(record.data.dialCode);
43369 this.hasFocus = false;
43371 this.fireEvent('select', this, record, index);
43375 flagEl : function()
43377 var flag = this.el.select('div.flag',true).first();
43384 dialCodeHolderEl : function()
43386 var d = this.el.select('input.dial-code-holder',true).first();
43393 setDialCode : function(v)
43395 this.dialCodeHolder.dom.value = '+'+v;
43398 setFlagClass : function(n)
43400 this.flag.dom.className = 'flag '+n;
43403 getValue : function()
43405 var v = this.inputEl().getValue();
43406 if(this.dialCodeHolder) {
43407 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43412 setValue : function(v)
43414 var d = this.getDialCode(v);
43416 //invalid dial code
43417 if(v.length == 0 || !d || d.length == 0) {
43419 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43420 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43426 this.setFlagClass(this.dialCodeMapping[d].iso2);
43427 this.setDialCode(d);
43428 this.inputEl().dom.value = v.replace('+'+d,'');
43429 this.hiddenEl().dom.value = this.getValue();
43434 getDialCode : function(v)
43438 if (v.length == 0) {
43439 return this.dialCodeHolder.dom.value;
43443 if (v.charAt(0) != "+") {
43446 var numericChars = "";
43447 for (var i = 1; i < v.length; i++) {
43448 var c = v.charAt(i);
43451 if (this.dialCodeMapping[numericChars]) {
43452 dialCode = v.substr(1, i);
43454 if (numericChars.length == 4) {
43464 this.setValue(this.defaultDialCode);
43468 hiddenEl : function()
43470 return this.el.select('input.hidden-tel-input',true).first();
43473 // after setting val
43474 onKeyUp : function(e){
43475 this.setValue(this.getValue());
43478 onKeyPress : function(e){
43479 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43486 * @class Roo.bootstrap.MoneyField
43487 * @extends Roo.bootstrap.ComboBox
43488 * Bootstrap MoneyField class
43491 * Create a new MoneyField.
43492 * @param {Object} config Configuration options
43495 Roo.bootstrap.MoneyField = function(config) {
43497 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43501 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43504 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43506 allowDecimals : true,
43508 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43510 decimalSeparator : ".",
43512 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43514 decimalPrecision : 0,
43516 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43518 allowNegative : true,
43520 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43524 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43526 minValue : Number.NEGATIVE_INFINITY,
43528 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43530 maxValue : Number.MAX_VALUE,
43532 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43534 minText : "The minimum value for this field is {0}",
43536 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43538 maxText : "The maximum value for this field is {0}",
43540 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43541 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43543 nanText : "{0} is not a valid number",
43545 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43549 * @cfg {String} defaults currency of the MoneyField
43550 * value should be in lkey
43552 defaultCurrency : false,
43554 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43556 thousandsDelimiter : false,
43558 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43569 getAutoCreate : function()
43571 var align = this.labelAlign || this.parentLabelAlign();
43583 cls : 'form-control roo-money-amount-input',
43584 autocomplete: 'new-password'
43587 var hiddenInput = {
43591 cls: 'hidden-number-input'
43594 if(this.max_length) {
43595 input.maxlength = this.max_length;
43599 hiddenInput.name = this.name;
43602 if (this.disabled) {
43603 input.disabled = true;
43606 var clg = 12 - this.inputlg;
43607 var cmd = 12 - this.inputmd;
43608 var csm = 12 - this.inputsm;
43609 var cxs = 12 - this.inputxs;
43613 cls : 'row roo-money-field',
43617 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43621 cls: 'roo-select2-container input-group',
43625 cls : 'form-control roo-money-currency-input',
43626 autocomplete: 'new-password',
43628 name : this.currencyName
43632 cls : 'input-group-addon',
43646 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43650 cls: this.hasFeedback ? 'has-feedback' : '',
43661 if (this.fieldLabel.length) {
43664 tooltip: 'This field is required'
43670 cls: 'control-label',
43676 html: this.fieldLabel
43679 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43685 if(this.indicatorpos == 'right') {
43686 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43693 if(align == 'left') {
43701 if(this.labelWidth > 12){
43702 label.style = "width: " + this.labelWidth + 'px';
43704 if(this.labelWidth < 13 && this.labelmd == 0){
43705 this.labelmd = this.labelWidth;
43707 if(this.labellg > 0){
43708 label.cls += ' col-lg-' + this.labellg;
43709 input.cls += ' col-lg-' + (12 - this.labellg);
43711 if(this.labelmd > 0){
43712 label.cls += ' col-md-' + this.labelmd;
43713 container.cls += ' col-md-' + (12 - this.labelmd);
43715 if(this.labelsm > 0){
43716 label.cls += ' col-sm-' + this.labelsm;
43717 container.cls += ' col-sm-' + (12 - this.labelsm);
43719 if(this.labelxs > 0){
43720 label.cls += ' col-xs-' + this.labelxs;
43721 container.cls += ' col-xs-' + (12 - this.labelxs);
43732 var settings = this;
43734 ['xs','sm','md','lg'].map(function(size){
43735 if (settings[size]) {
43736 cfg.cls += ' col-' + size + '-' + settings[size];
43743 initEvents : function()
43745 this.indicator = this.indicatorEl();
43747 this.initCurrencyEvent();
43749 this.initNumberEvent();
43752 initCurrencyEvent : function()
43755 throw "can not find store for combo";
43758 this.store = Roo.factory(this.store, Roo.data);
43759 this.store.parent = this;
43763 this.triggerEl = this.el.select('.input-group-addon', true).first();
43765 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43770 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43771 _this.list.setWidth(lw);
43774 this.list.on('mouseover', this.onViewOver, this);
43775 this.list.on('mousemove', this.onViewMove, this);
43776 this.list.on('scroll', this.onViewScroll, this);
43779 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43782 this.view = new Roo.View(this.list, this.tpl, {
43783 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43786 this.view.on('click', this.onViewClick, this);
43788 this.store.on('beforeload', this.onBeforeLoad, this);
43789 this.store.on('load', this.onLoad, this);
43790 this.store.on('loadexception', this.onLoadException, this);
43792 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43793 "up" : function(e){
43794 this.inKeyMode = true;
43798 "down" : function(e){
43799 if(!this.isExpanded()){
43800 this.onTriggerClick();
43802 this.inKeyMode = true;
43807 "enter" : function(e){
43810 if(this.fireEvent("specialkey", this, e)){
43811 this.onViewClick(false);
43817 "esc" : function(e){
43821 "tab" : function(e){
43824 if(this.fireEvent("specialkey", this, e)){
43825 this.onViewClick(false);
43833 doRelay : function(foo, bar, hname){
43834 if(hname == 'down' || this.scope.isExpanded()){
43835 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43843 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43847 initNumberEvent : function(e)
43849 this.inputEl().on("keydown" , this.fireKey, this);
43850 this.inputEl().on("focus", this.onFocus, this);
43851 this.inputEl().on("blur", this.onBlur, this);
43853 this.inputEl().relayEvent('keyup', this);
43855 if(this.indicator){
43856 this.indicator.addClass('invisible');
43859 this.originalValue = this.getValue();
43861 if(this.validationEvent == 'keyup'){
43862 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43863 this.inputEl().on('keyup', this.filterValidation, this);
43865 else if(this.validationEvent !== false){
43866 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43869 if(this.selectOnFocus){
43870 this.on("focus", this.preFocus, this);
43873 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43874 this.inputEl().on("keypress", this.filterKeys, this);
43876 this.inputEl().relayEvent('keypress', this);
43879 var allowed = "0123456789";
43881 if(this.allowDecimals){
43882 allowed += this.decimalSeparator;
43885 if(this.allowNegative){
43889 if(this.thousandsDelimiter) {
43893 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43895 var keyPress = function(e){
43897 var k = e.getKey();
43899 var c = e.getCharCode();
43902 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43903 allowed.indexOf(String.fromCharCode(c)) === -1
43909 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43913 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43918 this.inputEl().on("keypress", keyPress, this);
43922 onTriggerClick : function(e)
43929 this.loadNext = false;
43931 if(this.isExpanded()){
43936 this.hasFocus = true;
43938 if(this.triggerAction == 'all') {
43939 this.doQuery(this.allQuery, true);
43943 this.doQuery(this.getRawValue());
43946 getCurrency : function()
43948 var v = this.currencyEl().getValue();
43953 restrictHeight : function()
43955 this.list.alignTo(this.currencyEl(), this.listAlign);
43956 this.list.alignTo(this.currencyEl(), this.listAlign);
43959 onViewClick : function(view, doFocus, el, e)
43961 var index = this.view.getSelectedIndexes()[0];
43963 var r = this.store.getAt(index);
43966 this.onSelect(r, index);
43970 onSelect : function(record, index){
43972 if(this.fireEvent('beforeselect', this, record, index) !== false){
43974 this.setFromCurrencyData(index > -1 ? record.data : false);
43978 this.fireEvent('select', this, record, index);
43982 setFromCurrencyData : function(o)
43986 this.lastCurrency = o;
43988 if (this.currencyField) {
43989 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43991 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43994 this.lastSelectionText = currency;
43996 //setting default currency
43997 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43998 this.setCurrency(this.defaultCurrency);
44002 this.setCurrency(currency);
44005 setFromData : function(o)
44009 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44011 this.setFromCurrencyData(c);
44016 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44018 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44021 this.setValue(value);
44025 setCurrency : function(v)
44027 this.currencyValue = v;
44030 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44035 setValue : function(v)
44037 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44043 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44045 this.inputEl().dom.value = (v == '') ? '' :
44046 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44048 if(!this.allowZero && v === '0') {
44049 this.hiddenEl().dom.value = '';
44050 this.inputEl().dom.value = '';
44057 getRawValue : function()
44059 var v = this.inputEl().getValue();
44064 getValue : function()
44066 return this.fixPrecision(this.parseValue(this.getRawValue()));
44069 parseValue : function(value)
44071 if(this.thousandsDelimiter) {
44073 r = new RegExp(",", "g");
44074 value = value.replace(r, "");
44077 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44078 return isNaN(value) ? '' : value;
44082 fixPrecision : function(value)
44084 if(this.thousandsDelimiter) {
44086 r = new RegExp(",", "g");
44087 value = value.replace(r, "");
44090 var nan = isNaN(value);
44092 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44093 return nan ? '' : value;
44095 return parseFloat(value).toFixed(this.decimalPrecision);
44098 decimalPrecisionFcn : function(v)
44100 return Math.floor(v);
44103 validateValue : function(value)
44105 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44109 var num = this.parseValue(value);
44112 this.markInvalid(String.format(this.nanText, value));
44116 if(num < this.minValue){
44117 this.markInvalid(String.format(this.minText, this.minValue));
44121 if(num > this.maxValue){
44122 this.markInvalid(String.format(this.maxText, this.maxValue));
44129 validate : function()
44131 if(this.disabled || this.allowBlank){
44136 var currency = this.getCurrency();
44138 if(this.validateValue(this.getRawValue()) && currency.length){
44143 this.markInvalid();
44147 getName: function()
44152 beforeBlur : function()
44158 var v = this.parseValue(this.getRawValue());
44165 onBlur : function()
44169 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44170 //this.el.removeClass(this.focusClass);
44173 this.hasFocus = false;
44175 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44179 var v = this.getValue();
44181 if(String(v) !== String(this.startValue)){
44182 this.fireEvent('change', this, v, this.startValue);
44185 this.fireEvent("blur", this);
44188 inputEl : function()
44190 return this.el.select('.roo-money-amount-input', true).first();
44193 currencyEl : function()
44195 return this.el.select('.roo-money-currency-input', true).first();
44198 hiddenEl : function()
44200 return this.el.select('input.hidden-number-input',true).first();
44204 * @class Roo.bootstrap.BezierSignature
44205 * @extends Roo.bootstrap.Component
44206 * Bootstrap BezierSignature class
44207 * This script refer to:
44208 * Title: Signature Pad
44210 * Availability: https://github.com/szimek/signature_pad
44213 * Create a new BezierSignature
44214 * @param {Object} config The config object
44217 Roo.bootstrap.BezierSignature = function(config){
44218 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44224 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44231 mouse_btn_down: true,
44234 * @cfg {int} canvas height
44236 canvas_height: '200px',
44239 * @cfg {float|function} Radius of a single dot.
44244 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44249 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44254 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44259 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44264 * @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.
44266 bg_color: 'rgba(0, 0, 0, 0)',
44269 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44271 dot_color: 'black',
44274 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44276 velocity_filter_weight: 0.7,
44279 * @cfg {function} Callback when stroke begin.
44284 * @cfg {function} Callback when stroke end.
44288 getAutoCreate : function()
44290 var cls = 'roo-signature column';
44293 cls += ' ' + this.cls;
44303 for(var i = 0; i < col_sizes.length; i++) {
44304 if(this[col_sizes[i]]) {
44305 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44315 cls: 'roo-signature-body',
44319 cls: 'roo-signature-body-canvas',
44320 height: this.canvas_height,
44321 width: this.canvas_width
44328 style: 'display: none'
44336 initEvents: function()
44338 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44340 var canvas = this.canvasEl();
44342 // mouse && touch event swapping...
44343 canvas.dom.style.touchAction = 'none';
44344 canvas.dom.style.msTouchAction = 'none';
44346 this.mouse_btn_down = false;
44347 canvas.on('mousedown', this._handleMouseDown, this);
44348 canvas.on('mousemove', this._handleMouseMove, this);
44349 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44351 if (window.PointerEvent) {
44352 canvas.on('pointerdown', this._handleMouseDown, this);
44353 canvas.on('pointermove', this._handleMouseMove, this);
44354 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44357 if ('ontouchstart' in window) {
44358 canvas.on('touchstart', this._handleTouchStart, this);
44359 canvas.on('touchmove', this._handleTouchMove, this);
44360 canvas.on('touchend', this._handleTouchEnd, this);
44363 Roo.EventManager.onWindowResize(this.resize, this, true);
44365 // file input event
44366 this.fileEl().on('change', this.uploadImage, this);
44373 resize: function(){
44375 var canvas = this.canvasEl().dom;
44376 var ctx = this.canvasElCtx();
44377 var img_data = false;
44379 if(canvas.width > 0) {
44380 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44382 // setting canvas width will clean img data
44385 var style = window.getComputedStyle ?
44386 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44388 var padding_left = parseInt(style.paddingLeft) || 0;
44389 var padding_right = parseInt(style.paddingRight) || 0;
44391 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44394 ctx.putImageData(img_data, 0, 0);
44398 _handleMouseDown: function(e)
44400 if (e.browserEvent.which === 1) {
44401 this.mouse_btn_down = true;
44402 this.strokeBegin(e);
44406 _handleMouseMove: function (e)
44408 if (this.mouse_btn_down) {
44409 this.strokeMoveUpdate(e);
44413 _handleMouseUp: function (e)
44415 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44416 this.mouse_btn_down = false;
44421 _handleTouchStart: function (e) {
44423 e.preventDefault();
44424 if (e.browserEvent.targetTouches.length === 1) {
44425 // var touch = e.browserEvent.changedTouches[0];
44426 // this.strokeBegin(touch);
44428 this.strokeBegin(e); // assume e catching the correct xy...
44432 _handleTouchMove: function (e) {
44433 e.preventDefault();
44434 // var touch = event.targetTouches[0];
44435 // _this._strokeMoveUpdate(touch);
44436 this.strokeMoveUpdate(e);
44439 _handleTouchEnd: function (e) {
44440 var wasCanvasTouched = e.target === this.canvasEl().dom;
44441 if (wasCanvasTouched) {
44442 e.preventDefault();
44443 // var touch = event.changedTouches[0];
44444 // _this._strokeEnd(touch);
44449 reset: function () {
44450 this._lastPoints = [];
44451 this._lastVelocity = 0;
44452 this._lastWidth = (this.min_width + this.max_width) / 2;
44453 this.canvasElCtx().fillStyle = this.dot_color;
44456 strokeMoveUpdate: function(e)
44458 this.strokeUpdate(e);
44460 if (this.throttle) {
44461 this.throttleStroke(this.strokeUpdate, this.throttle);
44464 this.strokeUpdate(e);
44468 strokeBegin: function(e)
44470 var newPointGroup = {
44471 color: this.dot_color,
44475 if (typeof this.onBegin === 'function') {
44479 this.curve_data.push(newPointGroup);
44481 this.strokeUpdate(e);
44484 strokeUpdate: function(e)
44486 var rect = this.canvasEl().dom.getBoundingClientRect();
44487 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44488 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44489 var lastPoints = lastPointGroup.points;
44490 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44491 var isLastPointTooClose = lastPoint
44492 ? point.distanceTo(lastPoint) <= this.min_distance
44494 var color = lastPointGroup.color;
44495 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44496 var curve = this.addPoint(point);
44498 this.drawDot({color: color, point: point});
44501 this.drawCurve({color: color, curve: curve});
44511 strokeEnd: function(e)
44513 this.strokeUpdate(e);
44514 if (typeof this.onEnd === 'function') {
44519 addPoint: function (point) {
44520 var _lastPoints = this._lastPoints;
44521 _lastPoints.push(point);
44522 if (_lastPoints.length > 2) {
44523 if (_lastPoints.length === 3) {
44524 _lastPoints.unshift(_lastPoints[0]);
44526 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44527 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44528 _lastPoints.shift();
44534 calculateCurveWidths: function (startPoint, endPoint) {
44535 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44536 (1 - this.velocity_filter_weight) * this._lastVelocity;
44538 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44541 start: this._lastWidth
44544 this._lastVelocity = velocity;
44545 this._lastWidth = newWidth;
44549 drawDot: function (_a) {
44550 var color = _a.color, point = _a.point;
44551 var ctx = this.canvasElCtx();
44552 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44554 this.drawCurveSegment(point.x, point.y, width);
44556 ctx.fillStyle = color;
44560 drawCurve: function (_a) {
44561 var color = _a.color, curve = _a.curve;
44562 var ctx = this.canvasElCtx();
44563 var widthDelta = curve.endWidth - curve.startWidth;
44564 var drawSteps = Math.floor(curve.length()) * 2;
44566 ctx.fillStyle = color;
44567 for (var i = 0; i < drawSteps; i += 1) {
44568 var t = i / drawSteps;
44574 var x = uuu * curve.startPoint.x;
44575 x += 3 * uu * t * curve.control1.x;
44576 x += 3 * u * tt * curve.control2.x;
44577 x += ttt * curve.endPoint.x;
44578 var y = uuu * curve.startPoint.y;
44579 y += 3 * uu * t * curve.control1.y;
44580 y += 3 * u * tt * curve.control2.y;
44581 y += ttt * curve.endPoint.y;
44582 var width = curve.startWidth + ttt * widthDelta;
44583 this.drawCurveSegment(x, y, width);
44589 drawCurveSegment: function (x, y, width) {
44590 var ctx = this.canvasElCtx();
44592 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44593 this.is_empty = false;
44598 var ctx = this.canvasElCtx();
44599 var canvas = this.canvasEl().dom;
44600 ctx.fillStyle = this.bg_color;
44601 ctx.clearRect(0, 0, canvas.width, canvas.height);
44602 ctx.fillRect(0, 0, canvas.width, canvas.height);
44603 this.curve_data = [];
44605 this.is_empty = true;
44610 return this.el.select('input',true).first();
44613 canvasEl: function()
44615 return this.el.select('canvas',true).first();
44618 canvasElCtx: function()
44620 return this.el.select('canvas',true).first().dom.getContext('2d');
44623 getImage: function(type)
44625 if(this.is_empty) {
44630 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44633 drawFromImage: function(img_src)
44635 var img = new Image();
44637 img.onload = function(){
44638 this.canvasElCtx().drawImage(img, 0, 0);
44643 this.is_empty = false;
44646 selectImage: function()
44648 this.fileEl().dom.click();
44651 uploadImage: function(e)
44653 var reader = new FileReader();
44655 reader.onload = function(e){
44656 var img = new Image();
44657 img.onload = function(){
44659 this.canvasElCtx().drawImage(img, 0, 0);
44661 img.src = e.target.result;
44664 reader.readAsDataURL(e.target.files[0]);
44667 // Bezier Point Constructor
44668 Point: (function () {
44669 function Point(x, y, time) {
44672 this.time = time || Date.now();
44674 Point.prototype.distanceTo = function (start) {
44675 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44677 Point.prototype.equals = function (other) {
44678 return this.x === other.x && this.y === other.y && this.time === other.time;
44680 Point.prototype.velocityFrom = function (start) {
44681 return this.time !== start.time
44682 ? this.distanceTo(start) / (this.time - start.time)
44689 // Bezier Constructor
44690 Bezier: (function () {
44691 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44692 this.startPoint = startPoint;
44693 this.control2 = control2;
44694 this.control1 = control1;
44695 this.endPoint = endPoint;
44696 this.startWidth = startWidth;
44697 this.endWidth = endWidth;
44699 Bezier.fromPoints = function (points, widths, scope) {
44700 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44701 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44702 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44704 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44705 var dx1 = s1.x - s2.x;
44706 var dy1 = s1.y - s2.y;
44707 var dx2 = s2.x - s3.x;
44708 var dy2 = s2.y - s3.y;
44709 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44710 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44711 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44712 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44713 var dxm = m1.x - m2.x;
44714 var dym = m1.y - m2.y;
44715 var k = l2 / (l1 + l2);
44716 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44717 var tx = s2.x - cm.x;
44718 var ty = s2.y - cm.y;
44720 c1: new scope.Point(m1.x + tx, m1.y + ty),
44721 c2: new scope.Point(m2.x + tx, m2.y + ty)
44724 Bezier.prototype.length = function () {
44729 for (var i = 0; i <= steps; i += 1) {
44731 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44732 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44734 var xdiff = cx - px;
44735 var ydiff = cy - py;
44736 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44743 Bezier.prototype.point = function (t, start, c1, c2, end) {
44744 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44745 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44746 + (3.0 * c2 * (1.0 - t) * t * t)
44747 + (end * t * t * t);
44752 throttleStroke: function(fn, wait) {
44753 if (wait === void 0) { wait = 250; }
44755 var timeout = null;
44759 var later = function () {
44760 previous = Date.now();
44762 result = fn.apply(storedContext, storedArgs);
44764 storedContext = null;
44768 return function wrapper() {
44770 for (var _i = 0; _i < arguments.length; _i++) {
44771 args[_i] = arguments[_i];
44773 var now = Date.now();
44774 var remaining = wait - (now - previous);
44775 storedContext = this;
44777 if (remaining <= 0 || remaining > wait) {
44779 clearTimeout(timeout);
44783 result = fn.apply(storedContext, storedArgs);
44785 storedContext = null;
44789 else if (!timeout) {
44790 timeout = window.setTimeout(later, remaining);