2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3592 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3593 if (this.registerMenu && this.type != 'treeview') {
3594 Roo.bootstrap.MenuMgr.register(this);
3601 * Fires before this menu is displayed (return false to block)
3602 * @param {Roo.menu.Menu} this
3607 * Fires before this menu is hidden (return false to block)
3608 * @param {Roo.menu.Menu} this
3613 * Fires after this menu is displayed
3614 * @param {Roo.menu.Menu} this
3619 * Fires after this menu is hidden
3620 * @param {Roo.menu.Menu} this
3625 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3626 * @param {Roo.menu.Menu} this
3627 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628 * @param {Roo.EventObject} e
3633 * Fires when the mouse is hovering over this menu
3634 * @param {Roo.menu.Menu} this
3635 * @param {Roo.EventObject} e
3636 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3641 * Fires when the mouse exits this menu
3642 * @param {Roo.menu.Menu} this
3643 * @param {Roo.EventObject} e
3644 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3649 * Fires when a menu item contained in this menu is clicked
3650 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3651 * @param {Roo.EventObject} e
3655 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3658 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3662 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3665 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3667 registerMenu : true,
3669 menuItems :false, // stores the menu items..
3679 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3681 hideTrigger : false,
3686 getChildContainer : function() {
3690 getAutoCreate : function(){
3692 //if (['right'].indexOf(this.align)!==-1) {
3693 // cfg.cn[1].cls += ' pull-right'
3698 cls : 'dropdown-menu shadow' ,
3699 style : 'z-index:1000'
3703 if (this.type === 'submenu') {
3704 cfg.cls = 'submenu active';
3706 if (this.type === 'treeview') {
3707 cfg.cls = 'treeview-menu';
3712 initEvents : function() {
3714 // Roo.log("ADD event");
3715 // Roo.log(this.triggerEl.dom);
3717 this.triggerEl.on('click', this.onTriggerClick, this);
3719 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3721 if (!this.hideTrigger) {
3722 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3723 // dropdown toggle on the 'a' in BS4?
3724 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3726 this.triggerEl.addClass('dropdown-toggle');
3730 this.el.on('touchstart' , this.onTouch, this);
3732 this.el.on('click' , this.onClick, this);
3734 this.el.on("mouseover", this.onMouseOver, this);
3735 this.el.on("mouseout", this.onMouseOut, this);
3739 findTargetItem : function(e)
3741 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3745 //Roo.log(t); Roo.log(t.id);
3747 //Roo.log(this.menuitems);
3748 return this.menuitems.get(t.id);
3750 //return this.items.get(t.menuItemId);
3756 onTouch : function(e)
3758 Roo.log("menu.onTouch");
3759 //e.stopEvent(); this make the user popdown broken
3763 onClick : function(e)
3765 Roo.log("menu.onClick");
3767 var t = this.findTargetItem(e);
3768 if(!t || t.isContainer){
3773 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3774 if(t == this.activeItem && t.shouldDeactivate(e)){
3775 this.activeItem.deactivate();
3776 delete this.activeItem;
3780 this.setActiveItem(t, true);
3788 Roo.log('pass click event');
3792 this.fireEvent("click", this, t, e);
3796 if(!t.href.length || t.href == '#'){
3797 (function() { _this.hide(); }).defer(100);
3802 onMouseOver : function(e){
3803 var t = this.findTargetItem(e);
3806 // if(t.canActivate && !t.disabled){
3807 // this.setActiveItem(t, true);
3811 this.fireEvent("mouseover", this, e, t);
3813 isVisible : function(){
3814 return !this.hidden;
3816 onMouseOut : function(e){
3817 var t = this.findTargetItem(e);
3820 // if(t == this.activeItem && t.shouldDeactivate(e)){
3821 // this.activeItem.deactivate();
3822 // delete this.activeItem;
3825 this.fireEvent("mouseout", this, e, t);
3830 * Displays this menu relative to another element
3831 * @param {String/HTMLElement/Roo.Element} element The element to align to
3832 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3833 * the element (defaults to this.defaultAlign)
3834 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3836 show : function(el, pos, parentMenu)
3838 if (false === this.fireEvent("beforeshow", this)) {
3839 Roo.log("show canceled");
3842 this.parentMenu = parentMenu;
3846 this.el.addClass('show'); // show otherwise we do not know how big we are..
3848 var xy = this.el.getAlignToXY(el, pos);
3850 // bl-tl << left align below
3851 // tl-bl << left align
3853 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3854 // if it goes to far to the right.. -> align left.
3855 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3858 // was left align - go right?
3859 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3862 // goes down the bottom
3863 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3865 var a = this.align.replace('?', '').split('-');
3866 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3870 this.showAt( xy , parentMenu, false);
3873 * Displays this menu at a specific xy position
3874 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3875 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877 showAt : function(xy, parentMenu, /* private: */_e){
3878 this.parentMenu = parentMenu;
3883 this.fireEvent("beforeshow", this);
3884 //xy = this.el.adjustForConstraints(xy);
3888 this.hideMenuItems();
3889 this.hidden = false;
3890 this.triggerEl.addClass('open');
3891 this.el.addClass('show');
3895 // reassign x when hitting right
3897 // reassign y when hitting bottom
3899 // but the list may align on trigger left or trigger top... should it be a properity?
3901 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3906 this.fireEvent("show", this);
3912 this.doFocus.defer(50, this);
3916 doFocus : function(){
3918 this.focusEl.focus();
3923 * Hides this menu and optionally all parent menus
3924 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3926 hide : function(deep)
3928 if (false === this.fireEvent("beforehide", this)) {
3929 Roo.log("hide canceled");
3932 this.hideMenuItems();
3933 if(this.el && this.isVisible()){
3935 if(this.activeItem){
3936 this.activeItem.deactivate();
3937 this.activeItem = null;
3939 this.triggerEl.removeClass('open');;
3940 this.el.removeClass('show');
3942 this.fireEvent("hide", this);
3944 if(deep === true && this.parentMenu){
3945 this.parentMenu.hide(true);
3949 onTriggerClick : function(e)
3951 Roo.log('trigger click');
3953 var target = e.getTarget();
3955 Roo.log(target.nodeName.toLowerCase());
3957 if(target.nodeName.toLowerCase() === 'i'){
3963 onTriggerPress : function(e)
3965 Roo.log('trigger press');
3966 //Roo.log(e.getTarget());
3967 // Roo.log(this.triggerEl.dom);
3969 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3970 var pel = Roo.get(e.getTarget());
3971 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3972 Roo.log('is treeview or dropdown?');
3976 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3980 if (this.isVisible()) {
3986 this.show(this.triggerEl, this.align, false);
3989 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3996 hideMenuItems : function()
3998 Roo.log("hide Menu Items");
4003 this.el.select('.open',true).each(function(aa) {
4005 aa.removeClass('open');
4009 addxtypeChild : function (tree, cntr) {
4010 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4012 this.menuitems.add(comp);
4024 this.getEl().dom.innerHTML = '';
4025 this.menuitems.clear();
4039 * @class Roo.bootstrap.MenuItem
4040 * @extends Roo.bootstrap.Component
4041 * Bootstrap MenuItem class
4042 * @cfg {String} html the menu label
4043 * @cfg {String} href the link
4044 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4045 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4046 * @cfg {Boolean} active used on sidebars to highlight active itesm
4047 * @cfg {String} fa favicon to show on left of menu item.
4048 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4052 * Create a new MenuItem
4053 * @param {Object} config The config object
4057 Roo.bootstrap.MenuItem = function(config){
4058 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4063 * The raw click event for the entire grid.
4064 * @param {Roo.bootstrap.MenuItem} this
4065 * @param {Roo.EventObject} e
4071 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4075 preventDefault: false,
4076 isContainer : false,
4080 getAutoCreate : function(){
4082 if(this.isContainer){
4085 cls: 'dropdown-menu-item '
4095 cls : 'dropdown-item',
4100 if (this.fa !== false) {
4103 cls : 'fa fa-' + this.fa
4112 cls: 'dropdown-menu-item',
4115 if (this.parent().type == 'treeview') {
4116 cfg.cls = 'treeview-menu';
4119 cfg.cls += ' active';
4124 anc.href = this.href || cfg.cn[0].href ;
4125 ctag.html = this.html || cfg.cn[0].html ;
4129 initEvents: function()
4131 if (this.parent().type == 'treeview') {
4132 this.el.select('a').on('click', this.onClick, this);
4136 this.menu.parentType = this.xtype;
4137 this.menu.triggerEl = this.el;
4138 this.menu = this.addxtype(Roo.apply({}, this.menu));
4142 onClick : function(e)
4144 Roo.log('item on click ');
4146 if(this.preventDefault){
4149 //this.parent().hideMenuItems();
4151 this.fireEvent('click', this, e);
4170 * @class Roo.bootstrap.MenuSeparator
4171 * @extends Roo.bootstrap.Component
4172 * Bootstrap MenuSeparator class
4175 * Create a new MenuItem
4176 * @param {Object} config The config object
4180 Roo.bootstrap.MenuSeparator = function(config){
4181 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4184 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4186 getAutoCreate : function(){
4205 * @class Roo.bootstrap.Modal
4206 * @extends Roo.bootstrap.Component
4207 * Bootstrap Modal class
4208 * @cfg {String} title Title of dialog
4209 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4210 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4211 * @cfg {Boolean} specificTitle default false
4212 * @cfg {Array} buttons Array of buttons or standard button set..
4213 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4214 * @cfg {Boolean} animate default true
4215 * @cfg {Boolean} allow_close default true
4216 * @cfg {Boolean} fitwindow default false
4217 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4218 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4219 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4220 * @cfg {String} size (sm|lg|xl) default empty
4221 * @cfg {Number} max_width set the max width of modal
4222 * @cfg {Boolean} editableTitle can the title be edited
4227 * Create a new Modal Dialog
4228 * @param {Object} config The config object
4231 Roo.bootstrap.Modal = function(config){
4232 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4237 * The raw btnclick event for the button
4238 * @param {Roo.EventObject} e
4243 * Fire when dialog resize
4244 * @param {Roo.bootstrap.Modal} this
4245 * @param {Roo.EventObject} e
4249 * @event titlechanged
4250 * Fire when the editable title has been changed
4251 * @param {Roo.bootstrap.Modal} this
4252 * @param {Roo.EventObject} value
4254 "titlechanged" : true
4257 this.buttons = this.buttons || [];
4260 this.tmpl = Roo.factory(this.tmpl);
4265 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4267 title : 'test dialog',
4277 specificTitle: false,
4279 buttonPosition: 'right',
4301 editableTitle : false,
4303 onRender : function(ct, position)
4305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4308 var cfg = Roo.apply({}, this.getAutoCreate());
4311 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4313 //if (!cfg.name.length) {
4317 cfg.cls += ' ' + this.cls;
4320 cfg.style = this.style;
4322 this.el = Roo.get(document.body).createChild(cfg, position);
4324 //var type = this.el.dom.type;
4327 if(this.tabIndex !== undefined){
4328 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4331 this.dialogEl = this.el.select('.modal-dialog',true).first();
4332 this.bodyEl = this.el.select('.modal-body',true).first();
4333 this.closeEl = this.el.select('.modal-header .close', true).first();
4334 this.headerEl = this.el.select('.modal-header',true).first();
4335 this.titleEl = this.el.select('.modal-title',true).first();
4336 this.footerEl = this.el.select('.modal-footer',true).first();
4338 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4340 //this.el.addClass("x-dlg-modal");
4342 if (this.buttons.length) {
4343 Roo.each(this.buttons, function(bb) {
4344 var b = Roo.apply({}, bb);
4345 b.xns = b.xns || Roo.bootstrap;
4346 b.xtype = b.xtype || 'Button';
4347 if (typeof(b.listeners) == 'undefined') {
4348 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4351 var btn = Roo.factory(b);
4353 btn.render(this.getButtonContainer());
4357 // render the children.
4360 if(typeof(this.items) != 'undefined'){
4361 var items = this.items;
4364 for(var i =0;i < items.length;i++) {
4365 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4369 this.items = nitems;
4371 // where are these used - they used to be body/close/footer
4375 //this.el.addClass([this.fieldClass, this.cls]);
4379 getAutoCreate : function()
4381 // we will default to modal-body-overflow - might need to remove or make optional later.
4383 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4384 html : this.html || ''
4389 cls : 'modal-title',
4393 if(this.specificTitle){ // WTF is this?
4398 if (this.allow_close && Roo.bootstrap.version == 3) {
4408 if (this.editableTitle) {
4410 cls: 'form-control roo-editable-title d-none',
4416 if (this.allow_close && Roo.bootstrap.version == 4) {
4426 if(this.size.length){
4427 size = 'modal-' + this.size;
4430 var footer = Roo.bootstrap.version == 3 ?
4432 cls : 'modal-footer',
4436 cls: 'btn-' + this.buttonPosition
4441 { // BS4 uses mr-auto on left buttons....
4442 cls : 'modal-footer'
4453 cls: "modal-dialog " + size,
4456 cls : "modal-content",
4459 cls : 'modal-header',
4474 modal.cls += ' fade';
4480 getChildContainer : function() {
4485 getButtonContainer : function() {
4487 return Roo.bootstrap.version == 4 ?
4488 this.el.select('.modal-footer',true).first()
4489 : this.el.select('.modal-footer div',true).first();
4492 initEvents : function()
4494 if (this.allow_close) {
4495 this.closeEl.on('click', this.hide, this);
4497 Roo.EventManager.onWindowResize(this.resize, this, true);
4498 if (this.editableTitle) {
4499 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4500 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4501 this.headerEditEl.on('keyup', function(e) {
4502 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4503 this.toggleHeaderInput(false)
4506 this.headerEditEl.on('blur', function(e) {
4507 this.toggleHeaderInput(false)
4516 this.maskEl.setSize(
4517 Roo.lib.Dom.getViewWidth(true),
4518 Roo.lib.Dom.getViewHeight(true)
4521 if (this.fitwindow) {
4523 this.dialogEl.setStyle( { 'max-width' : '100%' });
4525 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4526 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4531 if(this.max_width !== 0) {
4533 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4536 this.setSize(w, this.height);
4540 if(this.max_height) {
4541 this.setSize(w,Math.min(
4543 Roo.lib.Dom.getViewportHeight(true) - 60
4549 if(!this.fit_content) {
4550 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4554 this.setSize(w, Math.min(
4556 this.headerEl.getHeight() +
4557 this.footerEl.getHeight() +
4558 this.getChildHeight(this.bodyEl.dom.childNodes),
4559 Roo.lib.Dom.getViewportHeight(true) - 60)
4565 setSize : function(w,h)
4576 if (!this.rendered) {
4579 this.toggleHeaderInput(false);
4580 //this.el.setStyle('display', 'block');
4581 this.el.removeClass('hideing');
4582 this.el.dom.style.display='block';
4584 Roo.get(document.body).addClass('modal-open');
4586 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4589 this.el.addClass('show');
4590 this.el.addClass('in');
4593 this.el.addClass('show');
4594 this.el.addClass('in');
4597 // not sure how we can show data in here..
4599 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4602 Roo.get(document.body).addClass("x-body-masked");
4604 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4605 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4606 this.maskEl.dom.style.display = 'block';
4607 this.maskEl.addClass('show');
4612 this.fireEvent('show', this);
4614 // set zindex here - otherwise it appears to be ignored...
4615 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4618 this.items.forEach( function(e) {
4619 e.layout ? e.layout() : false;
4627 if(this.fireEvent("beforehide", this) !== false){
4629 this.maskEl.removeClass('show');
4631 this.maskEl.dom.style.display = '';
4632 Roo.get(document.body).removeClass("x-body-masked");
4633 this.el.removeClass('in');
4634 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4636 if(this.animate){ // why
4637 this.el.addClass('hideing');
4638 this.el.removeClass('show');
4640 if (!this.el.hasClass('hideing')) {
4641 return; // it's been shown again...
4644 this.el.dom.style.display='';
4646 Roo.get(document.body).removeClass('modal-open');
4647 this.el.removeClass('hideing');
4651 this.el.removeClass('show');
4652 this.el.dom.style.display='';
4653 Roo.get(document.body).removeClass('modal-open');
4656 this.fireEvent('hide', this);
4659 isVisible : function()
4662 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4666 addButton : function(str, cb)
4670 var b = Roo.apply({}, { html : str } );
4671 b.xns = b.xns || Roo.bootstrap;
4672 b.xtype = b.xtype || 'Button';
4673 if (typeof(b.listeners) == 'undefined') {
4674 b.listeners = { click : cb.createDelegate(this) };
4677 var btn = Roo.factory(b);
4679 btn.render(this.getButtonContainer());
4685 setDefaultButton : function(btn)
4687 //this.el.select('.modal-footer').()
4690 resizeTo: function(w,h)
4692 this.dialogEl.setWidth(w);
4694 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4696 this.bodyEl.setHeight(h - diff);
4698 this.fireEvent('resize', this);
4701 setContentSize : function(w, h)
4705 onButtonClick: function(btn,e)
4708 this.fireEvent('btnclick', btn.name, e);
4711 * Set the title of the Dialog
4712 * @param {String} str new Title
4714 setTitle: function(str) {
4715 this.titleEl.dom.innerHTML = str;
4719 * Set the body of the Dialog
4720 * @param {String} str new Title
4722 setBody: function(str) {
4723 this.bodyEl.dom.innerHTML = str;
4726 * Set the body of the Dialog using the template
4727 * @param {Obj} data - apply this data to the template and replace the body contents.
4729 applyBody: function(obj)
4732 Roo.log("Error - using apply Body without a template");
4735 this.tmpl.overwrite(this.bodyEl, obj);
4738 getChildHeight : function(child_nodes)
4742 child_nodes.length == 0
4747 var child_height = 0;
4749 for(var i = 0; i < child_nodes.length; i++) {
4752 * for modal with tabs...
4753 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4755 var layout_childs = child_nodes[i].childNodes;
4757 for(var j = 0; j < layout_childs.length; j++) {
4759 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4761 var layout_body_childs = layout_childs[j].childNodes;
4763 for(var k = 0; k < layout_body_childs.length; k++) {
4765 if(layout_body_childs[k].classList.contains('navbar')) {
4766 child_height += layout_body_childs[k].offsetHeight;
4770 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4772 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4774 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4776 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4777 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4792 child_height += child_nodes[i].offsetHeight;
4793 // Roo.log(child_nodes[i].offsetHeight);
4796 return child_height;
4798 toggleHeaderInput : function(is_edit)
4800 if (!this.editableTitle) {
4801 return; // not editable.
4803 if (is_edit && this.is_header_editing) {
4804 return; // already editing..
4808 this.headerEditEl.dom.value = this.title;
4809 this.headerEditEl.removeClass('d-none');
4810 this.headerEditEl.dom.focus();
4811 this.titleEl.addClass('d-none');
4813 this.is_header_editing = true;
4816 // flip back to not editing.
4817 this.title = this.headerEditEl.dom.value;
4818 this.headerEditEl.addClass('d-none');
4819 this.titleEl.removeClass('d-none');
4820 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4821 this.is_header_editing = false;
4822 this.fireEvent('titlechanged', this, this.title);
4831 Roo.apply(Roo.bootstrap.Modal, {
4833 * Button config that displays a single OK button
4842 * Button config that displays Yes and No buttons
4858 * Button config that displays OK and Cancel buttons
4873 * Button config that displays Yes, No and Cancel buttons
4898 * messagebox - can be used as a replace
4902 * @class Roo.MessageBox
4903 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4907 Roo.Msg.alert('Status', 'Changes saved successfully.');
4909 // Prompt for user data:
4910 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4912 // process text value...
4916 // Show a dialog using config options:
4918 title:'Save Changes?',
4919 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4920 buttons: Roo.Msg.YESNOCANCEL,
4927 Roo.bootstrap.MessageBox = function(){
4928 var dlg, opt, mask, waitTimer;
4929 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4930 var buttons, activeTextEl, bwidth;
4934 var handleButton = function(button){
4936 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4940 var handleHide = function(){
4942 dlg.el.removeClass(opt.cls);
4945 // Roo.TaskMgr.stop(waitTimer);
4946 // waitTimer = null;
4951 var updateButtons = function(b){
4954 buttons["ok"].hide();
4955 buttons["cancel"].hide();
4956 buttons["yes"].hide();
4957 buttons["no"].hide();
4958 dlg.footerEl.hide();
4962 dlg.footerEl.show();
4963 for(var k in buttons){
4964 if(typeof buttons[k] != "function"){
4967 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4968 width += buttons[k].el.getWidth()+15;
4978 var handleEsc = function(d, k, e){
4979 if(opt && opt.closable !== false){
4989 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4990 * @return {Roo.BasicDialog} The BasicDialog element
4992 getDialog : function(){
4994 dlg = new Roo.bootstrap.Modal( {
4997 //constraintoviewport:false,
4999 //collapsible : false,
5004 //buttonAlign:"center",
5005 closeClick : function(){
5006 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5009 handleButton("cancel");
5014 dlg.on("hide", handleHide);
5016 //dlg.addKeyListener(27, handleEsc);
5018 this.buttons = buttons;
5019 var bt = this.buttonText;
5020 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5021 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5022 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5023 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5025 bodyEl = dlg.bodyEl.createChild({
5027 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5028 '<textarea class="roo-mb-textarea"></textarea>' +
5029 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5031 msgEl = bodyEl.dom.firstChild;
5032 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5033 textboxEl.enableDisplayMode();
5034 textboxEl.addKeyListener([10,13], function(){
5035 if(dlg.isVisible() && opt && opt.buttons){
5038 }else if(opt.buttons.yes){
5039 handleButton("yes");
5043 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5044 textareaEl.enableDisplayMode();
5045 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5046 progressEl.enableDisplayMode();
5048 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5049 var pf = progressEl.dom.firstChild;
5051 pp = Roo.get(pf.firstChild);
5052 pp.setHeight(pf.offsetHeight);
5060 * Updates the message box body text
5061 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5062 * the XHTML-compliant non-breaking space character '&#160;')
5063 * @return {Roo.MessageBox} This message box
5065 updateText : function(text)
5067 if(!dlg.isVisible() && !opt.width){
5068 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5069 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5071 msgEl.innerHTML = text || ' ';
5073 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5074 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5076 Math.min(opt.width || cw , this.maxWidth),
5077 Math.max(opt.minWidth || this.minWidth, bwidth)
5080 activeTextEl.setWidth(w);
5082 if(dlg.isVisible()){
5083 dlg.fixedcenter = false;
5085 // to big, make it scroll. = But as usual stupid IE does not support
5088 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5089 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5090 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5092 bodyEl.dom.style.height = '';
5093 bodyEl.dom.style.overflowY = '';
5096 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5098 bodyEl.dom.style.overflowX = '';
5101 dlg.setContentSize(w, bodyEl.getHeight());
5102 if(dlg.isVisible()){
5103 dlg.fixedcenter = true;
5109 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5110 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5111 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5112 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5113 * @return {Roo.MessageBox} This message box
5115 updateProgress : function(value, text){
5117 this.updateText(text);
5120 if (pp) { // weird bug on my firefox - for some reason this is not defined
5121 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5122 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5128 * Returns true if the message box is currently displayed
5129 * @return {Boolean} True if the message box is visible, else false
5131 isVisible : function(){
5132 return dlg && dlg.isVisible();
5136 * Hides the message box if it is displayed
5139 if(this.isVisible()){
5145 * Displays a new message box, or reinitializes an existing message box, based on the config options
5146 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5147 * The following config object properties are supported:
5149 Property Type Description
5150 ---------- --------------- ------------------------------------------------------------------------------------
5151 animEl String/Element An id or Element from which the message box should animate as it opens and
5152 closes (defaults to undefined)
5153 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5154 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5155 closable Boolean False to hide the top-right close button (defaults to true). Note that
5156 progress and wait dialogs will ignore this property and always hide the
5157 close button as they can only be closed programmatically.
5158 cls String A custom CSS class to apply to the message box element
5159 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5160 displayed (defaults to 75)
5161 fn Function A callback function to execute after closing the dialog. The arguments to the
5162 function will be btn (the name of the button that was clicked, if applicable,
5163 e.g. "ok"), and text (the value of the active text field, if applicable).
5164 Progress and wait dialogs will ignore this option since they do not respond to
5165 user actions and can only be closed programmatically, so any required function
5166 should be called by the same code after it closes the dialog.
5167 icon String A CSS class that provides a background image to be used as an icon for
5168 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5169 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5170 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5171 modal Boolean False to allow user interaction with the page while the message box is
5172 displayed (defaults to true)
5173 msg String A string that will replace the existing message box body text (defaults
5174 to the XHTML-compliant non-breaking space character ' ')
5175 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5176 progress Boolean True to display a progress bar (defaults to false)
5177 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5178 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5179 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5180 title String The title text
5181 value String The string value to set into the active textbox element if displayed
5182 wait Boolean True to display a progress bar (defaults to false)
5183 width Number The width of the dialog in pixels
5190 msg: 'Please enter your address:',
5192 buttons: Roo.MessageBox.OKCANCEL,
5195 animEl: 'addAddressBtn'
5198 * @param {Object} config Configuration options
5199 * @return {Roo.MessageBox} This message box
5201 show : function(options)
5204 // this causes nightmares if you show one dialog after another
5205 // especially on callbacks..
5207 if(this.isVisible()){
5210 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5211 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5212 Roo.log("New Dialog Message:" + options.msg )
5213 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5214 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5217 var d = this.getDialog();
5219 d.setTitle(opt.title || " ");
5220 d.closeEl.setDisplayed(opt.closable !== false);
5221 activeTextEl = textboxEl;
5222 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5227 textareaEl.setHeight(typeof opt.multiline == "number" ?
5228 opt.multiline : this.defaultTextHeight);
5229 activeTextEl = textareaEl;
5238 progressEl.setDisplayed(opt.progress === true);
5240 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5242 this.updateProgress(0);
5243 activeTextEl.dom.value = opt.value || "";
5245 dlg.setDefaultButton(activeTextEl);
5247 var bs = opt.buttons;
5251 }else if(bs && bs.yes){
5252 db = buttons["yes"];
5254 dlg.setDefaultButton(db);
5256 bwidth = updateButtons(opt.buttons);
5257 this.updateText(opt.msg);
5259 d.el.addClass(opt.cls);
5261 d.proxyDrag = opt.proxyDrag === true;
5262 d.modal = opt.modal !== false;
5263 d.mask = opt.modal !== false ? mask : false;
5265 // force it to the end of the z-index stack so it gets a cursor in FF
5266 document.body.appendChild(dlg.el.dom);
5267 d.animateTarget = null;
5268 d.show(options.animEl);
5274 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5275 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5276 * and closing the message box when the process is complete.
5277 * @param {String} title The title bar text
5278 * @param {String} msg The message box body text
5279 * @return {Roo.MessageBox} This message box
5281 progress : function(title, msg){
5288 minWidth: this.minProgressWidth,
5295 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5296 * If a callback function is passed it will be called after the user clicks the button, and the
5297 * id of the button that was clicked will be passed as the only parameter to the callback
5298 * (could also be the top-right close button).
5299 * @param {String} title The title bar text
5300 * @param {String} msg The message box body text
5301 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302 * @param {Object} scope (optional) The scope of the callback function
5303 * @return {Roo.MessageBox} This message box
5305 alert : function(title, msg, fn, scope)
5320 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5321 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5322 * You are responsible for closing the message box when the process is complete.
5323 * @param {String} msg The message box body text
5324 * @param {String} title (optional) The title bar text
5325 * @return {Roo.MessageBox} This message box
5327 wait : function(msg, title){
5338 waitTimer = Roo.TaskMgr.start({
5340 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5348 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5349 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5350 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5351 * @param {String} title The title bar text
5352 * @param {String} msg The message box body text
5353 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354 * @param {Object} scope (optional) The scope of the callback function
5355 * @return {Roo.MessageBox} This message box
5357 confirm : function(title, msg, fn, scope){
5361 buttons: this.YESNO,
5370 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5371 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5372 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5373 * (could also be the top-right close button) and the text that was entered will be passed as the two
5374 * parameters to the callback.
5375 * @param {String} title The title bar text
5376 * @param {String} msg The message box body text
5377 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378 * @param {Object} scope (optional) The scope of the callback function
5379 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5380 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5381 * @return {Roo.MessageBox} This message box
5383 prompt : function(title, msg, fn, scope, multiline){
5387 buttons: this.OKCANCEL,
5392 multiline: multiline,
5399 * Button config that displays a single OK button
5404 * Button config that displays Yes and No buttons
5407 YESNO : {yes:true, no:true},
5409 * Button config that displays OK and Cancel buttons
5412 OKCANCEL : {ok:true, cancel:true},
5414 * Button config that displays Yes, No and Cancel buttons
5417 YESNOCANCEL : {yes:true, no:true, cancel:true},
5420 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5423 defaultTextHeight : 75,
5425 * The maximum width in pixels of the message box (defaults to 600)
5430 * The minimum width in pixels of the message box (defaults to 100)
5435 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5436 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5439 minProgressWidth : 250,
5441 * An object containing the default button text strings that can be overriden for localized language support.
5442 * Supported properties are: ok, cancel, yes and no.
5443 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5456 * Shorthand for {@link Roo.MessageBox}
5458 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5459 Roo.Msg = Roo.Msg || Roo.MessageBox;
5468 * @class Roo.bootstrap.Navbar
5469 * @extends Roo.bootstrap.Component
5470 * Bootstrap Navbar class
5473 * Create a new Navbar
5474 * @param {Object} config The config object
5478 Roo.bootstrap.Navbar = function(config){
5479 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5483 * @event beforetoggle
5484 * Fire before toggle the menu
5485 * @param {Roo.EventObject} e
5487 "beforetoggle" : true
5491 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5500 getAutoCreate : function(){
5503 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5507 initEvents :function ()
5509 //Roo.log(this.el.select('.navbar-toggle',true));
5510 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5517 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5519 var size = this.el.getSize();
5520 this.maskEl.setSize(size.width, size.height);
5521 this.maskEl.enableDisplayMode("block");
5530 getChildContainer : function()
5532 if (this.el && this.el.select('.collapse').getCount()) {
5533 return this.el.select('.collapse',true).first();
5548 onToggle : function()
5551 if(this.fireEvent('beforetoggle', this) === false){
5554 var ce = this.el.select('.navbar-collapse',true).first();
5556 if (!ce.hasClass('show')) {
5566 * Expand the navbar pulldown
5568 expand : function ()
5571 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (ce.hasClass('collapsing')) {
5575 ce.dom.style.height = '';
5577 ce.addClass('in'); // old...
5578 ce.removeClass('collapse');
5579 ce.addClass('show');
5580 var h = ce.getHeight();
5582 ce.removeClass('show');
5583 // at this point we should be able to see it..
5584 ce.addClass('collapsing');
5586 ce.setHeight(0); // resize it ...
5587 ce.on('transitionend', function() {
5588 //Roo.log('done transition');
5589 ce.removeClass('collapsing');
5590 ce.addClass('show');
5591 ce.removeClass('collapse');
5593 ce.dom.style.height = '';
5594 }, this, { single: true} );
5596 ce.dom.scrollTop = 0;
5599 * Collapse the navbar pulldown
5601 collapse : function()
5603 var ce = this.el.select('.navbar-collapse',true).first();
5605 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5606 // it's collapsed or collapsing..
5609 ce.removeClass('in'); // old...
5610 ce.setHeight(ce.getHeight());
5611 ce.removeClass('show');
5612 ce.addClass('collapsing');
5614 ce.on('transitionend', function() {
5615 ce.dom.style.height = '';
5616 ce.removeClass('collapsing');
5617 ce.addClass('collapse');
5618 }, this, { single: true} );
5638 * @class Roo.bootstrap.NavSimplebar
5639 * @extends Roo.bootstrap.Navbar
5640 * Bootstrap Sidebar class
5642 * @cfg {Boolean} inverse is inverted color
5644 * @cfg {String} type (nav | pills | tabs)
5645 * @cfg {Boolean} arrangement stacked | justified
5646 * @cfg {String} align (left | right) alignment
5648 * @cfg {Boolean} main (true|false) main nav bar? default false
5649 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5651 * @cfg {String} tag (header|footer|nav|div) default is nav
5653 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5657 * Create a new Sidebar
5658 * @param {Object} config The config object
5662 Roo.bootstrap.NavSimplebar = function(config){
5663 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5666 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5682 getAutoCreate : function(){
5686 tag : this.tag || 'div',
5687 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5689 if (['light','white'].indexOf(this.weight) > -1) {
5690 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5692 cfg.cls += ' bg-' + this.weight;
5695 cfg.cls += ' navbar-inverse';
5699 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5701 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5710 cls: 'nav nav-' + this.xtype,
5716 this.type = this.type || 'nav';
5717 if (['tabs','pills'].indexOf(this.type) != -1) {
5718 cfg.cn[0].cls += ' nav-' + this.type
5722 if (this.type!=='nav') {
5723 Roo.log('nav type must be nav/tabs/pills')
5725 cfg.cn[0].cls += ' navbar-nav'
5731 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5732 cfg.cn[0].cls += ' nav-' + this.arrangement;
5736 if (this.align === 'right') {
5737 cfg.cn[0].cls += ' navbar-right';
5762 * navbar-expand-md fixed-top
5766 * @class Roo.bootstrap.NavHeaderbar
5767 * @extends Roo.bootstrap.NavSimplebar
5768 * Bootstrap Sidebar class
5770 * @cfg {String} brand what is brand
5771 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5772 * @cfg {String} brand_href href of the brand
5773 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5774 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5775 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5776 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5779 * Create a new Sidebar
5780 * @param {Object} config The config object
5784 Roo.bootstrap.NavHeaderbar = function(config){
5785 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5796 desktopCenter : false,
5799 getAutoCreate : function(){
5802 tag: this.nav || 'nav',
5803 cls: 'navbar navbar-expand-md',
5809 if (this.desktopCenter) {
5810 cn.push({cls : 'container', cn : []});
5818 cls: 'navbar-toggle navbar-toggler',
5819 'data-toggle': 'collapse',
5824 html: 'Toggle navigation'
5828 cls: 'icon-bar navbar-toggler-icon'
5841 cn.push( Roo.bootstrap.version == 4 ? btn : {
5843 cls: 'navbar-header',
5852 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5856 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5858 if (['light','white'].indexOf(this.weight) > -1) {
5859 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5861 cfg.cls += ' bg-' + this.weight;
5864 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5865 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5867 // tag can override this..
5869 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5872 if (this.brand !== '') {
5873 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5874 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5876 href: this.brand_href ? this.brand_href : '#',
5877 cls: 'navbar-brand',
5885 cfg.cls += ' main-nav';
5893 getHeaderChildContainer : function()
5895 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5896 return this.el.select('.navbar-header',true).first();
5899 return this.getChildContainer();
5902 getChildContainer : function()
5905 return this.el.select('.roo-navbar-collapse',true).first();
5910 initEvents : function()
5912 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5914 if (this.autohide) {
5919 Roo.get(document).on('scroll',function(e) {
5920 var ns = Roo.get(document).getScroll().top;
5921 var os = prevScroll;
5925 ft.removeClass('slideDown');
5926 ft.addClass('slideUp');
5929 ft.removeClass('slideUp');
5930 ft.addClass('slideDown');
5951 * @class Roo.bootstrap.NavSidebar
5952 * @extends Roo.bootstrap.Navbar
5953 * Bootstrap Sidebar class
5956 * Create a new Sidebar
5957 * @param {Object} config The config object
5961 Roo.bootstrap.NavSidebar = function(config){
5962 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5965 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5967 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5969 getAutoCreate : function(){
5974 cls: 'sidebar sidebar-nav'
5996 * @class Roo.bootstrap.NavGroup
5997 * @extends Roo.bootstrap.Component
5998 * Bootstrap NavGroup class
5999 * @cfg {String} align (left|right)
6000 * @cfg {Boolean} inverse
6001 * @cfg {String} type (nav|pills|tab) default nav
6002 * @cfg {String} navId - reference Id for navbar.
6003 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6006 * Create a new nav group
6007 * @param {Object} config The config object
6010 Roo.bootstrap.NavGroup = function(config){
6011 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6014 Roo.bootstrap.NavGroup.register(this);
6018 * Fires when the active item changes
6019 * @param {Roo.bootstrap.NavGroup} this
6020 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6021 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6028 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6040 getAutoCreate : function()
6042 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6048 if (Roo.bootstrap.version == 4) {
6049 if (['tabs','pills'].indexOf(this.type) != -1) {
6050 cfg.cls += ' nav-' + this.type;
6052 // trying to remove so header bar can right align top?
6053 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6054 // do not use on header bar...
6055 cfg.cls += ' navbar-nav';
6060 if (['tabs','pills'].indexOf(this.type) != -1) {
6061 cfg.cls += ' nav-' + this.type
6063 if (this.type !== 'nav') {
6064 Roo.log('nav type must be nav/tabs/pills')
6066 cfg.cls += ' navbar-nav'
6070 if (this.parent() && this.parent().sidebar) {
6073 cls: 'dashboard-menu sidebar-menu'
6079 if (this.form === true) {
6082 cls: 'navbar-form form-inline'
6084 //nav navbar-right ml-md-auto
6085 if (this.align === 'right') {
6086 cfg.cls += ' navbar-right ml-md-auto';
6088 cfg.cls += ' navbar-left';
6092 if (this.align === 'right') {
6093 cfg.cls += ' navbar-right ml-md-auto';
6095 cfg.cls += ' mr-auto';
6099 cfg.cls += ' navbar-inverse';
6107 * sets the active Navigation item
6108 * @param {Roo.bootstrap.NavItem} the new current navitem
6110 setActiveItem : function(item)
6113 Roo.each(this.navItems, function(v){
6118 v.setActive(false, true);
6125 item.setActive(true, true);
6126 this.fireEvent('changed', this, item, prev);
6131 * gets the active Navigation item
6132 * @return {Roo.bootstrap.NavItem} the current navitem
6134 getActive : function()
6138 Roo.each(this.navItems, function(v){
6149 indexOfNav : function()
6153 Roo.each(this.navItems, function(v,i){
6164 * adds a Navigation item
6165 * @param {Roo.bootstrap.NavItem} the navitem to add
6167 addItem : function(cfg)
6169 if (this.form && Roo.bootstrap.version == 4) {
6172 var cn = new Roo.bootstrap.NavItem(cfg);
6174 cn.parentId = this.id;
6175 cn.onRender(this.el, null);
6179 * register a Navigation item
6180 * @param {Roo.bootstrap.NavItem} the navitem to add
6182 register : function(item)
6184 this.navItems.push( item);
6185 item.navId = this.navId;
6190 * clear all the Navigation item
6193 clearAll : function()
6196 this.el.dom.innerHTML = '';
6199 getNavItem: function(tabId)
6202 Roo.each(this.navItems, function(e) {
6203 if (e.tabId == tabId) {
6213 setActiveNext : function()
6215 var i = this.indexOfNav(this.getActive());
6216 if (i > this.navItems.length) {
6219 this.setActiveItem(this.navItems[i+1]);
6221 setActivePrev : function()
6223 var i = this.indexOfNav(this.getActive());
6227 this.setActiveItem(this.navItems[i-1]);
6229 clearWasActive : function(except) {
6230 Roo.each(this.navItems, function(e) {
6231 if (e.tabId != except.tabId && e.was_active) {
6232 e.was_active = false;
6239 getWasActive : function ()
6242 Roo.each(this.navItems, function(e) {
6257 Roo.apply(Roo.bootstrap.NavGroup, {
6261 * register a Navigation Group
6262 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6264 register : function(navgrp)
6266 this.groups[navgrp.navId] = navgrp;
6270 * fetch a Navigation Group based on the navigation ID
6271 * @param {string} the navgroup to add
6272 * @returns {Roo.bootstrap.NavGroup} the navgroup
6274 get: function(navId) {
6275 if (typeof(this.groups[navId]) == 'undefined') {
6277 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6279 return this.groups[navId] ;
6294 * @class Roo.bootstrap.NavItem
6295 * @extends Roo.bootstrap.Component
6296 * Bootstrap Navbar.NavItem class
6297 * @cfg {String} href link to
6298 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6299 * @cfg {Boolean} button_outline show and outlined button
6300 * @cfg {String} html content of button
6301 * @cfg {String} badge text inside badge
6302 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6303 * @cfg {String} glyphicon DEPRICATED - use fa
6304 * @cfg {String} icon DEPRICATED - use fa
6305 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6306 * @cfg {Boolean} active Is item active
6307 * @cfg {Boolean} disabled Is item disabled
6308 * @cfg {String} linkcls Link Class
6309 * @cfg {Boolean} preventDefault (true | false) default false
6310 * @cfg {String} tabId the tab that this item activates.
6311 * @cfg {String} tagtype (a|span) render as a href or span?
6312 * @cfg {Boolean} animateRef (true|false) link to element default false
6315 * Create a new Navbar Item
6316 * @param {Object} config The config object
6318 Roo.bootstrap.NavItem = function(config){
6319 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6324 * The raw click event for the entire grid.
6325 * @param {Roo.EventObject} e
6330 * Fires when the active item active state changes
6331 * @param {Roo.bootstrap.NavItem} this
6332 * @param {boolean} state the new state
6338 * Fires when scroll to element
6339 * @param {Roo.bootstrap.NavItem} this
6340 * @param {Object} options
6341 * @param {Roo.EventObject} e
6349 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6358 preventDefault : false,
6366 button_outline : false,
6370 getAutoCreate : function(){
6377 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6380 cfg.cls += ' active' ;
6382 if (this.disabled) {
6383 cfg.cls += ' disabled';
6387 if (this.button_weight.length) {
6388 cfg.tag = this.href ? 'a' : 'button';
6389 cfg.html = this.html || '';
6390 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6392 cfg.href = this.href;
6395 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6397 cfg.cls += " nav-html";
6400 // menu .. should add dropdown-menu class - so no need for carat..
6402 if (this.badge !== '') {
6404 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6409 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6413 href : this.href || "#",
6414 html: this.html || '',
6418 if (this.tagtype == 'a') {
6419 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6424 } else if (this.fa) {
6425 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6426 } else if(this.glyphicon) {
6427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6429 cfg.cn[0].cls += " nav-html";
6433 cfg.cn[0].html += " <span class='caret'></span>";
6437 if (this.badge !== '') {
6438 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6446 onRender : function(ct, position)
6448 // Roo.log("Call onRender: " + this.xtype);
6449 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6453 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6454 this.navLink = this.el.select('.nav-link',true).first();
6455 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6460 initEvents: function()
6462 if (typeof (this.menu) != 'undefined') {
6463 this.menu.parentType = this.xtype;
6464 this.menu.triggerEl = this.el;
6465 this.menu = this.addxtype(Roo.apply({}, this.menu));
6468 this.el.on('click', this.onClick, this);
6470 //if(this.tagtype == 'span'){
6471 // this.el.select('span',true).on('click', this.onClick, this);
6474 // at this point parent should be available..
6475 this.parent().register(this);
6478 onClick : function(e)
6480 if (e.getTarget('.dropdown-menu-item')) {
6481 // did you click on a menu itemm.... - then don't trigger onclick..
6486 this.preventDefault ||
6489 Roo.log("NavItem - prevent Default?");
6493 if (this.disabled) {
6497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6498 if (tg && tg.transition) {
6499 Roo.log("waiting for the transitionend");
6505 //Roo.log("fire event clicked");
6506 if(this.fireEvent('click', this, e) === false){
6510 if(this.tagtype == 'span'){
6514 //Roo.log(this.href);
6515 var ael = this.el.select('a',true).first();
6518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6521 return; // ignore... - it's a 'hash' to another page.
6523 Roo.log("NavItem - prevent Default?");
6525 this.scrollToElement(e);
6529 var p = this.parent();
6531 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6532 if (typeof(p.setActiveItem) !== 'undefined') {
6533 p.setActiveItem(this);
6537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6539 // remove the collapsed menu expand...
6540 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6544 isActive: function () {
6547 setActive : function(state, fire, is_was_active)
6549 if (this.active && !state && this.navId) {
6550 this.was_active = true;
6551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6553 nv.clearWasActive(this);
6557 this.active = state;
6560 this.el.removeClass('active');
6561 this.navLink ? this.navLink.removeClass('active') : false;
6562 } else if (!this.el.hasClass('active')) {
6564 this.el.addClass('active');
6565 if (Roo.bootstrap.version == 4 && this.navLink ) {
6566 this.navLink.addClass('active');
6571 this.fireEvent('changed', this, state);
6574 // show a panel if it's registered and related..
6576 if (!this.navId || !this.tabId || !state || is_was_active) {
6580 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6584 var pan = tg.getPanelByName(this.tabId);
6588 // if we can not flip to new panel - go back to old nav highlight..
6589 if (false == tg.showPanel(pan)) {
6590 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6592 var onav = nv.getWasActive();
6594 onav.setActive(true, false, true);
6603 // this should not be here...
6604 setDisabled : function(state)
6606 this.disabled = state;
6608 this.el.removeClass('disabled');
6609 } else if (!this.el.hasClass('disabled')) {
6610 this.el.addClass('disabled');
6616 * Fetch the element to display the tooltip on.
6617 * @return {Roo.Element} defaults to this.el
6619 tooltipEl : function()
6621 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6624 scrollToElement : function(e)
6626 var c = document.body;
6629 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6631 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6632 c = document.documentElement;
6635 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6641 var o = target.calcOffsetsTo(c);
6648 this.fireEvent('scrollto', this, options, e);
6650 Roo.get(c).scrollTo('top', options.value, true);
6655 * Set the HTML (text content) of the item
6656 * @param {string} html content for the nav item
6658 setHtml : function(html)
6661 this.htmlEl.dom.innerHTML = html;
6673 * <span> icon </span>
6674 * <span> text </span>
6675 * <span>badge </span>
6679 * @class Roo.bootstrap.NavSidebarItem
6680 * @extends Roo.bootstrap.NavItem
6681 * Bootstrap Navbar.NavSidebarItem class
6682 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6683 * {Boolean} open is the menu open
6684 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6685 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6686 * {String} buttonSize (sm|md|lg)the extra classes for the button
6687 * {Boolean} showArrow show arrow next to the text (default true)
6689 * Create a new Navbar Button
6690 * @param {Object} config The config object
6692 Roo.bootstrap.NavSidebarItem = function(config){
6693 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6698 * The raw click event for the entire grid.
6699 * @param {Roo.EventObject} e
6704 * Fires when the active item active state changes
6705 * @param {Roo.bootstrap.NavSidebarItem} this
6706 * @param {boolean} state the new state
6714 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6716 badgeWeight : 'default',
6722 buttonWeight : 'default',
6728 getAutoCreate : function(){
6733 href : this.href || '#',
6739 if(this.buttonView){
6742 href : this.href || '#',
6743 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6756 cfg.cls += ' active';
6759 if (this.disabled) {
6760 cfg.cls += ' disabled';
6763 cfg.cls += ' open x-open';
6766 if (this.glyphicon || this.icon) {
6767 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6768 a.cn.push({ tag : 'i', cls : c }) ;
6771 if(!this.buttonView){
6774 html : this.html || ''
6781 if (this.badge !== '') {
6782 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6788 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6791 a.cls += ' dropdown-toggle treeview' ;
6797 initEvents : function()
6799 if (typeof (this.menu) != 'undefined') {
6800 this.menu.parentType = this.xtype;
6801 this.menu.triggerEl = this.el;
6802 this.menu = this.addxtype(Roo.apply({}, this.menu));
6805 this.el.on('click', this.onClick, this);
6807 if(this.badge !== ''){
6808 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6813 onClick : function(e)
6820 if(this.preventDefault){
6824 this.fireEvent('click', this, e);
6827 disable : function()
6829 this.setDisabled(true);
6834 this.setDisabled(false);
6837 setDisabled : function(state)
6839 if(this.disabled == state){
6843 this.disabled = state;
6846 this.el.addClass('disabled');
6850 this.el.removeClass('disabled');
6855 setActive : function(state)
6857 if(this.active == state){
6861 this.active = state;
6864 this.el.addClass('active');
6868 this.el.removeClass('active');
6873 isActive: function ()
6878 setBadge : function(str)
6884 this.badgeEl.dom.innerHTML = str;
6899 Roo.namespace('Roo.bootstrap.breadcrumb');
6903 * @class Roo.bootstrap.breadcrumb.Nav
6904 * @extends Roo.bootstrap.Component
6905 * Bootstrap Breadcrumb Nav Class
6907 * @children Roo.bootstrap.breadcrumb.Item
6910 * Create a new breadcrumb.Nav
6911 * @param {Object} config The config object
6915 Roo.bootstrap.breadcrumb.Nav = function(config){
6916 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6921 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6923 getAutoCreate : function()
6940 initEvents: function()
6942 this.olEl = this.el.select('ol',true).first();
6944 getChildContainer : function()
6960 * @class Roo.bootstrap.breadcrumb.Nav
6961 * @extends Roo.bootstrap.Component
6962 * Bootstrap Breadcrumb Nav Class
6964 * @children Roo.bootstrap.breadcrumb.Component
6965 * @cfg {String} html the content of the link.
6966 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6967 * @cfg {Boolean} active is it active
6971 * Create a new breadcrumb.Nav
6972 * @param {Object} config The config object
6975 Roo.bootstrap.breadcrumb.Item = function(config){
6976 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6981 * The img click event for the img.
6982 * @param {Roo.EventObject} e
6989 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6994 getAutoCreate : function()
6999 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7001 if (this.href !== false) {
7008 cfg.html = this.html;
7014 initEvents: function()
7017 this.el.select('a', true).first().on('click',this.onClick, this)
7021 onClick : function(e)
7024 this.fireEvent('click',this, e);
7037 * @class Roo.bootstrap.Row
7038 * @extends Roo.bootstrap.Component
7039 * Bootstrap Row class (contains columns...)
7043 * @param {Object} config The config object
7046 Roo.bootstrap.Row = function(config){
7047 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7050 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7052 getAutoCreate : function(){
7071 * @class Roo.bootstrap.Pagination
7072 * @extends Roo.bootstrap.Component
7073 * Bootstrap Pagination class
7074 * @cfg {String} size xs | sm | md | lg
7075 * @cfg {Boolean} inverse false | true
7078 * Create a new Pagination
7079 * @param {Object} config The config object
7082 Roo.bootstrap.Pagination = function(config){
7083 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7086 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7092 getAutoCreate : function(){
7098 cfg.cls += ' inverse';
7104 cfg.cls += " " + this.cls;
7122 * @class Roo.bootstrap.PaginationItem
7123 * @extends Roo.bootstrap.Component
7124 * Bootstrap PaginationItem class
7125 * @cfg {String} html text
7126 * @cfg {String} href the link
7127 * @cfg {Boolean} preventDefault (true | false) default true
7128 * @cfg {Boolean} active (true | false) default false
7129 * @cfg {Boolean} disabled default false
7133 * Create a new PaginationItem
7134 * @param {Object} config The config object
7138 Roo.bootstrap.PaginationItem = function(config){
7139 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7144 * The raw click event for the entire grid.
7145 * @param {Roo.EventObject} e
7151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7155 preventDefault: true,
7160 getAutoCreate : function(){
7166 href : this.href ? this.href : '#',
7167 html : this.html ? this.html : ''
7177 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7181 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7187 initEvents: function() {
7189 this.el.on('click', this.onClick, this);
7192 onClick : function(e)
7194 Roo.log('PaginationItem on click ');
7195 if(this.preventDefault){
7203 this.fireEvent('click', this, e);
7219 * @class Roo.bootstrap.Slider
7220 * @extends Roo.bootstrap.Component
7221 * Bootstrap Slider class
7224 * Create a new Slider
7225 * @param {Object} config The config object
7228 Roo.bootstrap.Slider = function(config){
7229 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7232 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7234 getAutoCreate : function(){
7238 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7242 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7254 * Ext JS Library 1.1.1
7255 * Copyright(c) 2006-2007, Ext JS, LLC.
7257 * Originally Released Under LGPL - original licence link has changed is not relivant.
7260 * <script type="text/javascript">
7265 * @class Roo.grid.ColumnModel
7266 * @extends Roo.util.Observable
7267 * This is the default implementation of a ColumnModel used by the Grid. It defines
7268 * the columns in the grid.
7271 var colModel = new Roo.grid.ColumnModel([
7272 {header: "Ticker", width: 60, sortable: true, locked: true},
7273 {header: "Company Name", width: 150, sortable: true},
7274 {header: "Market Cap.", width: 100, sortable: true},
7275 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7276 {header: "Employees", width: 100, sortable: true, resizable: false}
7281 * The config options listed for this class are options which may appear in each
7282 * individual column definition.
7283 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7285 * @param {Object} config An Array of column config objects. See this class's
7286 * config objects for details.
7288 Roo.grid.ColumnModel = function(config){
7290 * The config passed into the constructor
7292 this.config = config;
7295 // if no id, create one
7296 // if the column does not have a dataIndex mapping,
7297 // map it to the order it is in the config
7298 for(var i = 0, len = config.length; i < len; i++){
7300 if(typeof c.dataIndex == "undefined"){
7303 if(typeof c.renderer == "string"){
7304 c.renderer = Roo.util.Format[c.renderer];
7306 if(typeof c.id == "undefined"){
7309 if(c.editor && c.editor.xtype){
7310 c.editor = Roo.factory(c.editor, Roo.grid);
7312 if(c.editor && c.editor.isFormField){
7313 c.editor = new Roo.grid.GridEditor(c.editor);
7315 this.lookup[c.id] = c;
7319 * The width of columns which have no width specified (defaults to 100)
7322 this.defaultWidth = 100;
7325 * Default sortable of columns which have no sortable specified (defaults to false)
7328 this.defaultSortable = false;
7332 * @event widthchange
7333 * Fires when the width of a column changes.
7334 * @param {ColumnModel} this
7335 * @param {Number} columnIndex The column index
7336 * @param {Number} newWidth The new width
7338 "widthchange": true,
7340 * @event headerchange
7341 * Fires when the text of a header changes.
7342 * @param {ColumnModel} this
7343 * @param {Number} columnIndex The column index
7344 * @param {Number} newText The new header text
7346 "headerchange": true,
7348 * @event hiddenchange
7349 * Fires when a column is hidden or "unhidden".
7350 * @param {ColumnModel} this
7351 * @param {Number} columnIndex The column index
7352 * @param {Boolean} hidden true if hidden, false otherwise
7354 "hiddenchange": true,
7356 * @event columnmoved
7357 * Fires when a column is moved.
7358 * @param {ColumnModel} this
7359 * @param {Number} oldIndex
7360 * @param {Number} newIndex
7362 "columnmoved" : true,
7364 * @event columlockchange
7365 * Fires when a column's locked state is changed
7366 * @param {ColumnModel} this
7367 * @param {Number} colIndex
7368 * @param {Boolean} locked true if locked
7370 "columnlockchange" : true
7372 Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376 * @cfg {String} header The header text to display in the Grid view.
7379 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7380 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7381 * specified, the column's index is used as an index into the Record's data Array.
7384 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7385 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7388 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7389 * Defaults to the value of the {@link #defaultSortable} property.
7390 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7393 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7396 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7399 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7402 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7405 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7406 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7407 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7408 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7411 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7414 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7417 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7420 * @cfg {String} cursor (Optional)
7423 * @cfg {String} tooltip (Optional)
7426 * @cfg {Number} xs (Optional)
7429 * @cfg {Number} sm (Optional)
7432 * @cfg {Number} md (Optional)
7435 * @cfg {Number} lg (Optional)
7438 * Returns the id of the column at the specified index.
7439 * @param {Number} index The column index
7440 * @return {String} the id
7442 getColumnId : function(index){
7443 return this.config[index].id;
7447 * Returns the column for a specified id.
7448 * @param {String} id The column id
7449 * @return {Object} the column
7451 getColumnById : function(id){
7452 return this.lookup[id];
7457 * Returns the column for a specified dataIndex.
7458 * @param {String} dataIndex The column dataIndex
7459 * @return {Object|Boolean} the column or false if not found
7461 getColumnByDataIndex: function(dataIndex){
7462 var index = this.findColumnIndex(dataIndex);
7463 return index > -1 ? this.config[index] : false;
7467 * Returns the index for a specified column id.
7468 * @param {String} id The column id
7469 * @return {Number} the index, or -1 if not found
7471 getIndexById : function(id){
7472 for(var i = 0, len = this.config.length; i < len; i++){
7473 if(this.config[i].id == id){
7481 * Returns the index for a specified column dataIndex.
7482 * @param {String} dataIndex The column dataIndex
7483 * @return {Number} the index, or -1 if not found
7486 findColumnIndex : function(dataIndex){
7487 for(var i = 0, len = this.config.length; i < len; i++){
7488 if(this.config[i].dataIndex == dataIndex){
7496 moveColumn : function(oldIndex, newIndex){
7497 var c = this.config[oldIndex];
7498 this.config.splice(oldIndex, 1);
7499 this.config.splice(newIndex, 0, c);
7500 this.dataMap = null;
7501 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7504 isLocked : function(colIndex){
7505 return this.config[colIndex].locked === true;
7508 setLocked : function(colIndex, value, suppressEvent){
7509 if(this.isLocked(colIndex) == value){
7512 this.config[colIndex].locked = value;
7514 this.fireEvent("columnlockchange", this, colIndex, value);
7518 getTotalLockedWidth : function(){
7520 for(var i = 0; i < this.config.length; i++){
7521 if(this.isLocked(i) && !this.isHidden(i)){
7522 this.totalWidth += this.getColumnWidth(i);
7528 getLockedCount : function(){
7529 for(var i = 0, len = this.config.length; i < len; i++){
7530 if(!this.isLocked(i)){
7535 return this.config.length;
7539 * Returns the number of columns.
7542 getColumnCount : function(visibleOnly){
7543 if(visibleOnly === true){
7545 for(var i = 0, len = this.config.length; i < len; i++){
7546 if(!this.isHidden(i)){
7552 return this.config.length;
7556 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7557 * @param {Function} fn
7558 * @param {Object} scope (optional)
7559 * @return {Array} result
7561 getColumnsBy : function(fn, scope){
7563 for(var i = 0, len = this.config.length; i < len; i++){
7564 var c = this.config[i];
7565 if(fn.call(scope||this, c, i) === true){
7573 * Returns true if the specified column is sortable.
7574 * @param {Number} col The column index
7577 isSortable : function(col){
7578 if(typeof this.config[col].sortable == "undefined"){
7579 return this.defaultSortable;
7581 return this.config[col].sortable;
7585 * Returns the rendering (formatting) function defined for the column.
7586 * @param {Number} col The column index.
7587 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589 getRenderer : function(col){
7590 if(!this.config[col].renderer){
7591 return Roo.grid.ColumnModel.defaultRenderer;
7593 return this.config[col].renderer;
7597 * Sets the rendering (formatting) function for a column.
7598 * @param {Number} col The column index
7599 * @param {Function} fn The function to use to process the cell's raw data
7600 * to return HTML markup for the grid view. The render function is called with
7601 * the following parameters:<ul>
7602 * <li>Data value.</li>
7603 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7604 * <li>css A CSS style string to apply to the table cell.</li>
7605 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7606 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7607 * <li>Row index</li>
7608 * <li>Column index</li>
7609 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611 setRenderer : function(col, fn){
7612 this.config[col].renderer = fn;
7616 * Returns the width for the specified column.
7617 * @param {Number} col The column index
7620 getColumnWidth : function(col){
7621 return this.config[col].width * 1 || this.defaultWidth;
7625 * Sets the width for a column.
7626 * @param {Number} col The column index
7627 * @param {Number} width The new width
7629 setColumnWidth : function(col, width, suppressEvent){
7630 this.config[col].width = width;
7631 this.totalWidth = null;
7633 this.fireEvent("widthchange", this, col, width);
7638 * Returns the total width of all columns.
7639 * @param {Boolean} includeHidden True to include hidden column widths
7642 getTotalWidth : function(includeHidden){
7643 if(!this.totalWidth){
7644 this.totalWidth = 0;
7645 for(var i = 0, len = this.config.length; i < len; i++){
7646 if(includeHidden || !this.isHidden(i)){
7647 this.totalWidth += this.getColumnWidth(i);
7651 return this.totalWidth;
7655 * Returns the header for the specified column.
7656 * @param {Number} col The column index
7659 getColumnHeader : function(col){
7660 return this.config[col].header;
7664 * Sets the header for a column.
7665 * @param {Number} col The column index
7666 * @param {String} header The new header
7668 setColumnHeader : function(col, header){
7669 this.config[col].header = header;
7670 this.fireEvent("headerchange", this, col, header);
7674 * Returns the tooltip for the specified column.
7675 * @param {Number} col The column index
7678 getColumnTooltip : function(col){
7679 return this.config[col].tooltip;
7682 * Sets the tooltip for a column.
7683 * @param {Number} col The column index
7684 * @param {String} tooltip The new tooltip
7686 setColumnTooltip : function(col, tooltip){
7687 this.config[col].tooltip = tooltip;
7691 * Returns the dataIndex for the specified column.
7692 * @param {Number} col The column index
7695 getDataIndex : function(col){
7696 return this.config[col].dataIndex;
7700 * Sets the dataIndex for a column.
7701 * @param {Number} col The column index
7702 * @param {Number} dataIndex The new dataIndex
7704 setDataIndex : function(col, dataIndex){
7705 this.config[col].dataIndex = dataIndex;
7711 * Returns true if the cell is editable.
7712 * @param {Number} colIndex The column index
7713 * @param {Number} rowIndex The row index - this is nto actually used..?
7716 isCellEditable : function(colIndex, rowIndex){
7717 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7721 * Returns the editor defined for the cell/column.
7722 * return false or null to disable editing.
7723 * @param {Number} colIndex The column index
7724 * @param {Number} rowIndex The row index
7727 getCellEditor : function(colIndex, rowIndex){
7728 return this.config[colIndex].editor;
7732 * Sets if a column is editable.
7733 * @param {Number} col The column index
7734 * @param {Boolean} editable True if the column is editable
7736 setEditable : function(col, editable){
7737 this.config[col].editable = editable;
7742 * Returns true if the column is hidden.
7743 * @param {Number} colIndex The column index
7746 isHidden : function(colIndex){
7747 return this.config[colIndex].hidden;
7752 * Returns true if the column width cannot be changed
7754 isFixed : function(colIndex){
7755 return this.config[colIndex].fixed;
7759 * Returns true if the column can be resized
7762 isResizable : function(colIndex){
7763 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7766 * Sets if a column is hidden.
7767 * @param {Number} colIndex The column index
7768 * @param {Boolean} hidden True if the column is hidden
7770 setHidden : function(colIndex, hidden){
7771 this.config[colIndex].hidden = hidden;
7772 this.totalWidth = null;
7773 this.fireEvent("hiddenchange", this, colIndex, hidden);
7777 * Sets the editor for a column.
7778 * @param {Number} col The column index
7779 * @param {Object} editor The editor object
7781 setEditor : function(col, editor){
7782 this.config[col].editor = editor;
7786 Roo.grid.ColumnModel.defaultRenderer = function(value)
7788 if(typeof value == "object") {
7791 if(typeof value == "string" && value.length < 1){
7795 return String.format("{0}", value);
7798 // Alias for backwards compatibility
7799 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7802 * Ext JS Library 1.1.1
7803 * Copyright(c) 2006-2007, Ext JS, LLC.
7805 * Originally Released Under LGPL - original licence link has changed is not relivant.
7808 * <script type="text/javascript">
7812 * @class Roo.LoadMask
7813 * A simple utility class for generically masking elements while loading data. If the element being masked has
7814 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7815 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7816 * element's UpdateManager load indicator and will be destroyed after the initial load.
7818 * Create a new LoadMask
7819 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7820 * @param {Object} config The config object
7822 Roo.LoadMask = function(el, config){
7823 this.el = Roo.get(el);
7824 Roo.apply(this, config);
7826 this.store.on('beforeload', this.onBeforeLoad, this);
7827 this.store.on('load', this.onLoad, this);
7828 this.store.on('loadexception', this.onLoadException, this);
7829 this.removeMask = false;
7831 var um = this.el.getUpdateManager();
7832 um.showLoadIndicator = false; // disable the default indicator
7833 um.on('beforeupdate', this.onBeforeLoad, this);
7834 um.on('update', this.onLoad, this);
7835 um.on('failure', this.onLoad, this);
7836 this.removeMask = true;
7840 Roo.LoadMask.prototype = {
7842 * @cfg {Boolean} removeMask
7843 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7844 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7848 * The text to display in a centered loading message box (defaults to 'Loading...')
7852 * @cfg {String} msgCls
7853 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7855 msgCls : 'x-mask-loading',
7858 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7864 * Disables the mask to prevent it from being displayed
7866 disable : function(){
7867 this.disabled = true;
7871 * Enables the mask so that it can be displayed
7873 enable : function(){
7874 this.disabled = false;
7877 onLoadException : function()
7881 if (typeof(arguments[3]) != 'undefined') {
7882 Roo.MessageBox.alert("Error loading",arguments[3]);
7886 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7887 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7894 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7899 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7903 onBeforeLoad : function(){
7905 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7910 destroy : function(){
7912 this.store.un('beforeload', this.onBeforeLoad, this);
7913 this.store.un('load', this.onLoad, this);
7914 this.store.un('loadexception', this.onLoadException, this);
7916 var um = this.el.getUpdateManager();
7917 um.un('beforeupdate', this.onBeforeLoad, this);
7918 um.un('update', this.onLoad, this);
7919 um.un('failure', this.onLoad, this);
7930 * @class Roo.bootstrap.Table
7931 * @extends Roo.bootstrap.Component
7932 * Bootstrap Table class
7933 * @cfg {String} cls table class
7934 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7935 * @cfg {String} bgcolor Specifies the background color for a table
7936 * @cfg {Number} border Specifies whether the table cells should have borders or not
7937 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7938 * @cfg {Number} cellspacing Specifies the space between cells
7939 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7940 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7941 * @cfg {String} sortable Specifies that the table should be sortable
7942 * @cfg {String} summary Specifies a summary of the content of a table
7943 * @cfg {Number} width Specifies the width of a table
7944 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7946 * @cfg {boolean} striped Should the rows be alternative striped
7947 * @cfg {boolean} bordered Add borders to the table
7948 * @cfg {boolean} hover Add hover highlighting
7949 * @cfg {boolean} condensed Format condensed
7950 * @cfg {boolean} responsive Format condensed
7951 * @cfg {Boolean} loadMask (true|false) default false
7952 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7953 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7954 * @cfg {Boolean} rowSelection (true|false) default false
7955 * @cfg {Boolean} cellSelection (true|false) default false
7956 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7957 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7958 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7959 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7963 * Create a new Table
7964 * @param {Object} config The config object
7967 Roo.bootstrap.Table = function(config){
7968 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7973 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7974 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7975 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7976 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7978 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7980 this.sm.grid = this;
7981 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7982 this.sm = this.selModel;
7983 this.sm.xmodule = this.xmodule || false;
7986 if (this.cm && typeof(this.cm.config) == 'undefined') {
7987 this.colModel = new Roo.grid.ColumnModel(this.cm);
7988 this.cm = this.colModel;
7989 this.cm.xmodule = this.xmodule || false;
7992 this.store= Roo.factory(this.store, Roo.data);
7993 this.ds = this.store;
7994 this.ds.xmodule = this.xmodule || false;
7997 if (this.footer && this.store) {
7998 this.footer.dataSource = this.ds;
7999 this.footer = Roo.factory(this.footer);
8006 * Fires when a cell is clicked
8007 * @param {Roo.bootstrap.Table} this
8008 * @param {Roo.Element} el
8009 * @param {Number} rowIndex
8010 * @param {Number} columnIndex
8011 * @param {Roo.EventObject} e
8015 * @event celldblclick
8016 * Fires when a cell is double clicked
8017 * @param {Roo.bootstrap.Table} this
8018 * @param {Roo.Element} el
8019 * @param {Number} rowIndex
8020 * @param {Number} columnIndex
8021 * @param {Roo.EventObject} e
8023 "celldblclick" : true,
8026 * Fires when a row is clicked
8027 * @param {Roo.bootstrap.Table} this
8028 * @param {Roo.Element} el
8029 * @param {Number} rowIndex
8030 * @param {Roo.EventObject} e
8034 * @event rowdblclick
8035 * Fires when a row is double clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Roo.EventObject} e
8041 "rowdblclick" : true,
8044 * Fires when a mouseover occur
8045 * @param {Roo.bootstrap.Table} this
8046 * @param {Roo.Element} el
8047 * @param {Number} rowIndex
8048 * @param {Number} columnIndex
8049 * @param {Roo.EventObject} e
8054 * Fires when a mouseout occur
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Roo.Element} el
8057 * @param {Number} rowIndex
8058 * @param {Number} columnIndex
8059 * @param {Roo.EventObject} e
8064 * Fires when a row is rendered, so you can change add a style to it.
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8070 * @event rowsrendered
8071 * Fires when all the rows have been rendered
8072 * @param {Roo.bootstrap.Table} this
8074 'rowsrendered' : true,
8076 * @event contextmenu
8077 * The raw contextmenu event for the entire grid.
8078 * @param {Roo.EventObject} e
8080 "contextmenu" : true,
8082 * @event rowcontextmenu
8083 * Fires when a row is right clicked
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Number} rowIndex
8086 * @param {Roo.EventObject} e
8088 "rowcontextmenu" : true,
8090 * @event cellcontextmenu
8091 * Fires when a cell is right clicked
8092 * @param {Roo.bootstrap.Table} this
8093 * @param {Number} rowIndex
8094 * @param {Number} cellIndex
8095 * @param {Roo.EventObject} e
8097 "cellcontextmenu" : true,
8099 * @event headercontextmenu
8100 * Fires when a header is right clicked
8101 * @param {Roo.bootstrap.Table} this
8102 * @param {Number} columnIndex
8103 * @param {Roo.EventObject} e
8105 "headercontextmenu" : true
8109 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8135 rowSelection : false,
8136 cellSelection : false,
8139 // Roo.Element - the tbody
8141 // Roo.Element - thead element
8144 container: false, // used by gridpanel...
8150 auto_hide_footer : false,
8152 getAutoCreate : function()
8154 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8161 if (this.scrollBody) {
8162 cfg.cls += ' table-body-fixed';
8165 cfg.cls += ' table-striped';
8169 cfg.cls += ' table-hover';
8171 if (this.bordered) {
8172 cfg.cls += ' table-bordered';
8174 if (this.condensed) {
8175 cfg.cls += ' table-condensed';
8177 if (this.responsive) {
8178 cfg.cls += ' table-responsive';
8182 cfg.cls+= ' ' +this.cls;
8185 // this lot should be simplifed...
8198 ].forEach(function(k) {
8206 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8209 if(this.store || this.cm){
8210 if(this.headerShow){
8211 cfg.cn.push(this.renderHeader());
8214 cfg.cn.push(this.renderBody());
8216 if(this.footerShow){
8217 cfg.cn.push(this.renderFooter());
8219 // where does this come from?
8220 //cfg.cls+= ' TableGrid';
8223 return { cn : [ cfg ] };
8226 initEvents : function()
8228 if(!this.store || !this.cm){
8231 if (this.selModel) {
8232 this.selModel.initEvents();
8236 //Roo.log('initEvents with ds!!!!');
8238 this.mainBody = this.el.select('tbody', true).first();
8239 this.mainHead = this.el.select('thead', true).first();
8240 this.mainFoot = this.el.select('tfoot', true).first();
8246 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8247 e.on('click', _this.sort, _this);
8250 this.mainBody.on("click", this.onClick, this);
8251 this.mainBody.on("dblclick", this.onDblClick, this);
8253 // why is this done????? = it breaks dialogs??
8254 //this.parent().el.setStyle('position', 'relative');
8258 this.footer.parentId = this.id;
8259 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8262 this.el.select('tfoot tr td').first().addClass('hide');
8267 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8270 this.store.on('load', this.onLoad, this);
8271 this.store.on('beforeload', this.onBeforeLoad, this);
8272 this.store.on('update', this.onUpdate, this);
8273 this.store.on('add', this.onAdd, this);
8274 this.store.on("clear", this.clear, this);
8276 this.el.on("contextmenu", this.onContextMenu, this);
8278 this.mainBody.on('scroll', this.onBodyScroll, this);
8280 this.cm.on("headerchange", this.onHeaderChange, this);
8282 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8286 onContextMenu : function(e, t)
8288 this.processEvent("contextmenu", e);
8291 processEvent : function(name, e)
8293 if (name != 'touchstart' ) {
8294 this.fireEvent(name, e);
8297 var t = e.getTarget();
8299 var cell = Roo.get(t);
8305 if(cell.findParent('tfoot', false, true)){
8309 if(cell.findParent('thead', false, true)){
8311 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8312 cell = Roo.get(t).findParent('th', false, true);
8314 Roo.log("failed to find th in thead?");
8315 Roo.log(e.getTarget());
8320 var cellIndex = cell.dom.cellIndex;
8322 var ename = name == 'touchstart' ? 'click' : name;
8323 this.fireEvent("header" + ename, this, cellIndex, e);
8328 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8329 cell = Roo.get(t).findParent('td', false, true);
8331 Roo.log("failed to find th in tbody?");
8332 Roo.log(e.getTarget());
8337 var row = cell.findParent('tr', false, true);
8338 var cellIndex = cell.dom.cellIndex;
8339 var rowIndex = row.dom.rowIndex - 1;
8343 this.fireEvent("row" + name, this, rowIndex, e);
8347 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8353 onMouseover : function(e, el)
8355 var cell = Roo.get(el);
8361 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362 cell = cell.findParent('td', false, true);
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1; // start from 0
8369 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8373 onMouseout : function(e, el)
8375 var cell = Roo.get(el);
8381 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8382 cell = cell.findParent('td', false, true);
8385 var row = cell.findParent('tr', false, true);
8386 var cellIndex = cell.dom.cellIndex;
8387 var rowIndex = row.dom.rowIndex - 1; // start from 0
8389 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8393 onClick : function(e, el)
8395 var cell = Roo.get(el);
8397 if(!cell || (!this.cellSelection && !this.rowSelection)){
8401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402 cell = cell.findParent('td', false, true);
8405 if(!cell || typeof(cell) == 'undefined'){
8409 var row = cell.findParent('tr', false, true);
8411 if(!row || typeof(row) == 'undefined'){
8415 var cellIndex = cell.dom.cellIndex;
8416 var rowIndex = this.getRowIndex(row);
8418 // why??? - should these not be based on SelectionModel?
8419 if(this.cellSelection){
8420 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8423 if(this.rowSelection){
8424 this.fireEvent('rowclick', this, row, rowIndex, e);
8430 onDblClick : function(e,el)
8432 var cell = Roo.get(el);
8434 if(!cell || (!this.cellSelection && !this.rowSelection)){
8438 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8439 cell = cell.findParent('td', false, true);
8442 if(!cell || typeof(cell) == 'undefined'){
8446 var row = cell.findParent('tr', false, true);
8448 if(!row || typeof(row) == 'undefined'){
8452 var cellIndex = cell.dom.cellIndex;
8453 var rowIndex = this.getRowIndex(row);
8455 if(this.cellSelection){
8456 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8459 if(this.rowSelection){
8460 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8464 sort : function(e,el)
8466 var col = Roo.get(el);
8468 if(!col.hasClass('sortable')){
8472 var sort = col.attr('sort');
8475 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8479 this.store.sortInfo = {field : sort, direction : dir};
8482 Roo.log("calling footer first");
8483 this.footer.onClick('first');
8486 this.store.load({ params : { start : 0 } });
8490 renderHeader : function()
8498 this.totalWidth = 0;
8500 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8502 var config = cm.config[i];
8506 cls : 'x-hcol-' + i,
8508 html: cm.getColumnHeader(i)
8513 if(typeof(config.sortable) != 'undefined' && config.sortable){
8515 c.html = '<i class="glyphicon"></i>' + c.html;
8518 // could use BS4 hidden-..-down
8520 if(typeof(config.lgHeader) != 'undefined'){
8521 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8524 if(typeof(config.mdHeader) != 'undefined'){
8525 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8528 if(typeof(config.smHeader) != 'undefined'){
8529 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8532 if(typeof(config.xsHeader) != 'undefined'){
8533 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8540 if(typeof(config.tooltip) != 'undefined'){
8541 c.tooltip = config.tooltip;
8544 if(typeof(config.colspan) != 'undefined'){
8545 c.colspan = config.colspan;
8548 if(typeof(config.hidden) != 'undefined' && config.hidden){
8549 c.style += ' display:none;';
8552 if(typeof(config.dataIndex) != 'undefined'){
8553 c.sort = config.dataIndex;
8558 if(typeof(config.align) != 'undefined' && config.align.length){
8559 c.style += ' text-align:' + config.align + ';';
8562 if(typeof(config.width) != 'undefined'){
8563 c.style += ' width:' + config.width + 'px;';
8564 this.totalWidth += config.width;
8566 this.totalWidth += 100; // assume minimum of 100 per column?
8569 if(typeof(config.cls) != 'undefined'){
8570 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8573 ['xs','sm','md','lg'].map(function(size){
8575 if(typeof(config[size]) == 'undefined'){
8579 if (!config[size]) { // 0 = hidden
8580 // BS 4 '0' is treated as hide that column and below.
8581 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8585 c.cls += ' col-' + size + '-' + config[size] + (
8586 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8598 renderBody : function()
8608 colspan : this.cm.getColumnCount()
8618 renderFooter : function()
8628 colspan : this.cm.getColumnCount()
8642 // Roo.log('ds onload');
8647 var ds = this.store;
8649 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8650 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8651 if (_this.store.sortInfo) {
8653 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8654 e.select('i', true).addClass(['glyphicon-arrow-up']);
8657 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8658 e.select('i', true).addClass(['glyphicon-arrow-down']);
8663 var tbody = this.mainBody;
8665 if(ds.getCount() > 0){
8666 ds.data.each(function(d,rowIndex){
8667 var row = this.renderRow(cm, ds, rowIndex);
8669 tbody.createChild(row);
8673 if(row.cellObjects.length){
8674 Roo.each(row.cellObjects, function(r){
8675 _this.renderCellObject(r);
8682 var tfoot = this.el.select('tfoot', true).first();
8684 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8686 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8688 var total = this.ds.getTotalCount();
8690 if(this.footer.pageSize < total){
8691 this.mainFoot.show();
8695 Roo.each(this.el.select('tbody td', true).elements, function(e){
8696 e.on('mouseover', _this.onMouseover, _this);
8699 Roo.each(this.el.select('tbody td', true).elements, function(e){
8700 e.on('mouseout', _this.onMouseout, _this);
8702 this.fireEvent('rowsrendered', this);
8708 onUpdate : function(ds,record)
8710 this.refreshRow(record);
8714 onRemove : function(ds, record, index, isUpdate){
8715 if(isUpdate !== true){
8716 this.fireEvent("beforerowremoved", this, index, record);
8718 var bt = this.mainBody.dom;
8720 var rows = this.el.select('tbody > tr', true).elements;
8722 if(typeof(rows[index]) != 'undefined'){
8723 bt.removeChild(rows[index].dom);
8726 // if(bt.rows[index]){
8727 // bt.removeChild(bt.rows[index]);
8730 if(isUpdate !== true){
8731 //this.stripeRows(index);
8732 //this.syncRowHeights(index, index);
8734 this.fireEvent("rowremoved", this, index, record);
8738 onAdd : function(ds, records, rowIndex)
8740 //Roo.log('on Add called');
8741 // - note this does not handle multiple adding very well..
8742 var bt = this.mainBody.dom;
8743 for (var i =0 ; i < records.length;i++) {
8744 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8745 //Roo.log(records[i]);
8746 //Roo.log(this.store.getAt(rowIndex+i));
8747 this.insertRow(this.store, rowIndex + i, false);
8754 refreshRow : function(record){
8755 var ds = this.store, index;
8756 if(typeof record == 'number'){
8758 record = ds.getAt(index);
8760 index = ds.indexOf(record);
8762 return; // should not happen - but seems to
8765 this.insertRow(ds, index, true);
8767 this.onRemove(ds, record, index+1, true);
8769 //this.syncRowHeights(index, index);
8771 this.fireEvent("rowupdated", this, index, record);
8774 insertRow : function(dm, rowIndex, isUpdate){
8777 this.fireEvent("beforerowsinserted", this, rowIndex);
8779 //var s = this.getScrollState();
8780 var row = this.renderRow(this.cm, this.store, rowIndex);
8781 // insert before rowIndex..
8782 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8786 if(row.cellObjects.length){
8787 Roo.each(row.cellObjects, function(r){
8788 _this.renderCellObject(r);
8793 this.fireEvent("rowsinserted", this, rowIndex);
8794 //this.syncRowHeights(firstRow, lastRow);
8795 //this.stripeRows(firstRow);
8802 getRowDom : function(rowIndex)
8804 var rows = this.el.select('tbody > tr', true).elements;
8806 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8809 // returns the object tree for a tr..
8812 renderRow : function(cm, ds, rowIndex)
8814 var d = ds.getAt(rowIndex);
8818 cls : 'x-row-' + rowIndex,
8822 var cellObjects = [];
8824 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8825 var config = cm.config[i];
8827 var renderer = cm.getRenderer(i);
8831 if(typeof(renderer) !== 'undefined'){
8832 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8834 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8835 // and are rendered into the cells after the row is rendered - using the id for the element.
8837 if(typeof(value) === 'object'){
8847 rowIndex : rowIndex,
8852 this.fireEvent('rowclass', this, rowcfg);
8856 cls : rowcfg.rowClass + ' x-col-' + i,
8858 html: (typeof(value) === 'object') ? '' : value
8865 if(typeof(config.colspan) != 'undefined'){
8866 td.colspan = config.colspan;
8869 if(typeof(config.hidden) != 'undefined' && config.hidden){
8870 td.style += ' display:none;';
8873 if(typeof(config.align) != 'undefined' && config.align.length){
8874 td.style += ' text-align:' + config.align + ';';
8876 if(typeof(config.valign) != 'undefined' && config.valign.length){
8877 td.style += ' vertical-align:' + config.valign + ';';
8880 if(typeof(config.width) != 'undefined'){
8881 td.style += ' width:' + config.width + 'px;';
8884 if(typeof(config.cursor) != 'undefined'){
8885 td.style += ' cursor:' + config.cursor + ';';
8888 if(typeof(config.cls) != 'undefined'){
8889 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8892 ['xs','sm','md','lg'].map(function(size){
8894 if(typeof(config[size]) == 'undefined'){
8900 if (!config[size]) { // 0 = hidden
8901 // BS 4 '0' is treated as hide that column and below.
8902 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8906 td.cls += ' col-' + size + '-' + config[size] + (
8907 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8917 row.cellObjects = cellObjects;
8925 onBeforeLoad : function()
8934 this.el.select('tbody', true).first().dom.innerHTML = '';
8937 * Show or hide a row.
8938 * @param {Number} rowIndex to show or hide
8939 * @param {Boolean} state hide
8941 setRowVisibility : function(rowIndex, state)
8943 var bt = this.mainBody.dom;
8945 var rows = this.el.select('tbody > tr', true).elements;
8947 if(typeof(rows[rowIndex]) == 'undefined'){
8950 rows[rowIndex].dom.style.display = state ? '' : 'none';
8954 getSelectionModel : function(){
8956 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8958 return this.selModel;
8961 * Render the Roo.bootstrap object from renderder
8963 renderCellObject : function(r)
8967 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8969 var t = r.cfg.render(r.container);
8972 Roo.each(r.cfg.cn, function(c){
8974 container: t.getChildContainer(),
8977 _this.renderCellObject(child);
8982 getRowIndex : function(row)
8986 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8997 * Returns the grid's underlying element = used by panel.Grid
8998 * @return {Element} The element
9000 getGridEl : function(){
9004 * Forces a resize - used by panel.Grid
9005 * @return {Element} The element
9007 autoSize : function()
9009 //var ctr = Roo.get(this.container.dom.parentElement);
9010 var ctr = Roo.get(this.el.dom);
9012 var thd = this.getGridEl().select('thead',true).first();
9013 var tbd = this.getGridEl().select('tbody', true).first();
9014 var tfd = this.getGridEl().select('tfoot', true).first();
9016 var cw = ctr.getWidth();
9017 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9021 tbd.setWidth(ctr.getWidth());
9022 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9023 // this needs fixing for various usage - currently only hydra job advers I think..
9025 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9027 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9030 cw = Math.max(cw, this.totalWidth);
9031 this.getGridEl().select('tbody tr',true).setWidth(cw);
9033 // resize 'expandable coloumn?
9035 return; // we doe not have a view in this design..
9038 onBodyScroll: function()
9040 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9042 this.mainHead.setStyle({
9043 'position' : 'relative',
9044 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9050 var scrollHeight = this.mainBody.dom.scrollHeight;
9052 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9054 var height = this.mainBody.getHeight();
9056 if(scrollHeight - height == scrollTop) {
9058 var total = this.ds.getTotalCount();
9060 if(this.footer.cursor + this.footer.pageSize < total){
9062 this.footer.ds.load({
9064 start : this.footer.cursor + this.footer.pageSize,
9065 limit : this.footer.pageSize
9075 onHeaderChange : function()
9077 var header = this.renderHeader();
9078 var table = this.el.select('table', true).first();
9080 this.mainHead.remove();
9081 this.mainHead = table.createChild(header, this.mainBody, false);
9084 onHiddenChange : function(colModel, colIndex, hidden)
9086 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9087 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9089 this.CSS.updateRule(thSelector, "display", "");
9090 this.CSS.updateRule(tdSelector, "display", "");
9093 this.CSS.updateRule(thSelector, "display", "none");
9094 this.CSS.updateRule(tdSelector, "display", "none");
9097 this.onHeaderChange();
9101 setColumnWidth: function(col_index, width)
9103 // width = "md-2 xs-2..."
9104 if(!this.colModel.config[col_index]) {
9108 var w = width.split(" ");
9110 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9112 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9115 for(var j = 0; j < w.length; j++) {
9121 var size_cls = w[j].split("-");
9123 if(!Number.isInteger(size_cls[1] * 1)) {
9127 if(!this.colModel.config[col_index][size_cls[0]]) {
9131 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9135 h_row[0].classList.replace(
9136 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9137 "col-"+size_cls[0]+"-"+size_cls[1]
9140 for(var i = 0; i < rows.length; i++) {
9142 var size_cls = w[j].split("-");
9144 if(!Number.isInteger(size_cls[1] * 1)) {
9148 if(!this.colModel.config[col_index][size_cls[0]]) {
9152 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9156 rows[i].classList.replace(
9157 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9158 "col-"+size_cls[0]+"-"+size_cls[1]
9162 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9177 * @class Roo.bootstrap.TableCell
9178 * @extends Roo.bootstrap.Component
9179 * Bootstrap TableCell class
9180 * @cfg {String} html cell contain text
9181 * @cfg {String} cls cell class
9182 * @cfg {String} tag cell tag (td|th) default td
9183 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9184 * @cfg {String} align Aligns the content in a cell
9185 * @cfg {String} axis Categorizes cells
9186 * @cfg {String} bgcolor Specifies the background color of a cell
9187 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9188 * @cfg {Number} colspan Specifies the number of columns a cell should span
9189 * @cfg {String} headers Specifies one or more header cells a cell is related to
9190 * @cfg {Number} height Sets the height of a cell
9191 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9192 * @cfg {Number} rowspan Sets the number of rows a cell should span
9193 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9194 * @cfg {String} valign Vertical aligns the content in a cell
9195 * @cfg {Number} width Specifies the width of a cell
9198 * Create a new TableCell
9199 * @param {Object} config The config object
9202 Roo.bootstrap.TableCell = function(config){
9203 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9226 getAutoCreate : function(){
9227 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9247 cfg.align=this.align
9253 cfg.bgcolor=this.bgcolor
9256 cfg.charoff=this.charoff
9259 cfg.colspan=this.colspan
9262 cfg.headers=this.headers
9265 cfg.height=this.height
9268 cfg.nowrap=this.nowrap
9271 cfg.rowspan=this.rowspan
9274 cfg.scope=this.scope
9277 cfg.valign=this.valign
9280 cfg.width=this.width
9299 * @class Roo.bootstrap.TableRow
9300 * @extends Roo.bootstrap.Component
9301 * Bootstrap TableRow class
9302 * @cfg {String} cls row class
9303 * @cfg {String} align Aligns the content in a table row
9304 * @cfg {String} bgcolor Specifies a background color for a table row
9305 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9306 * @cfg {String} valign Vertical aligns the content in a table row
9309 * Create a new TableRow
9310 * @param {Object} config The config object
9313 Roo.bootstrap.TableRow = function(config){
9314 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9325 getAutoCreate : function(){
9326 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9336 cfg.align = this.align;
9339 cfg.bgcolor = this.bgcolor;
9342 cfg.charoff = this.charoff;
9345 cfg.valign = this.valign;
9363 * @class Roo.bootstrap.TableBody
9364 * @extends Roo.bootstrap.Component
9365 * Bootstrap TableBody class
9366 * @cfg {String} cls element class
9367 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9368 * @cfg {String} align Aligns the content inside the element
9369 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9370 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9373 * Create a new TableBody
9374 * @param {Object} config The config object
9377 Roo.bootstrap.TableBody = function(config){
9378 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9389 getAutoCreate : function(){
9390 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9404 cfg.align = this.align;
9407 cfg.charoff = this.charoff;
9410 cfg.valign = this.valign;
9417 // initEvents : function()
9424 // this.store = Roo.factory(this.store, Roo.data);
9425 // this.store.on('load', this.onLoad, this);
9427 // this.store.load();
9431 // onLoad: function ()
9433 // this.fireEvent('load', this);
9443 * Ext JS Library 1.1.1
9444 * Copyright(c) 2006-2007, Ext JS, LLC.
9446 * Originally Released Under LGPL - original licence link has changed is not relivant.
9449 * <script type="text/javascript">
9452 // as we use this in bootstrap.
9453 Roo.namespace('Roo.form');
9455 * @class Roo.form.Action
9456 * Internal Class used to handle form actions
9458 * @param {Roo.form.BasicForm} el The form element or its id
9459 * @param {Object} config Configuration options
9464 // define the action interface
9465 Roo.form.Action = function(form, options){
9467 this.options = options || {};
9470 * Client Validation Failed
9473 Roo.form.Action.CLIENT_INVALID = 'client';
9475 * Server Validation Failed
9478 Roo.form.Action.SERVER_INVALID = 'server';
9480 * Connect to Server Failed
9483 Roo.form.Action.CONNECT_FAILURE = 'connect';
9485 * Reading Data from Server Failed
9488 Roo.form.Action.LOAD_FAILURE = 'load';
9490 Roo.form.Action.prototype = {
9492 failureType : undefined,
9493 response : undefined,
9497 run : function(options){
9502 success : function(response){
9507 handleResponse : function(response){
9511 // default connection failure
9512 failure : function(response){
9514 this.response = response;
9515 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9516 this.form.afterAction(this, false);
9519 processResponse : function(response){
9520 this.response = response;
9521 if(!response.responseText){
9524 this.result = this.handleResponse(response);
9528 // utility functions used internally
9529 getUrl : function(appendParams){
9530 var url = this.options.url || this.form.url || this.form.el.dom.action;
9532 var p = this.getParams();
9534 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9540 getMethod : function(){
9541 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9544 getParams : function(){
9545 var bp = this.form.baseParams;
9546 var p = this.options.params;
9548 if(typeof p == "object"){
9549 p = Roo.urlEncode(Roo.applyIf(p, bp));
9550 }else if(typeof p == 'string' && bp){
9551 p += '&' + Roo.urlEncode(bp);
9554 p = Roo.urlEncode(bp);
9559 createCallback : function(){
9561 success: this.success,
9562 failure: this.failure,
9564 timeout: (this.form.timeout*1000),
9565 upload: this.form.fileUpload ? this.success : undefined
9570 Roo.form.Action.Submit = function(form, options){
9571 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9577 haveProgress : false,
9578 uploadComplete : false,
9580 // uploadProgress indicator.
9581 uploadProgress : function()
9583 if (!this.form.progressUrl) {
9587 if (!this.haveProgress) {
9588 Roo.MessageBox.progress("Uploading", "Uploading");
9590 if (this.uploadComplete) {
9591 Roo.MessageBox.hide();
9595 this.haveProgress = true;
9597 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9599 var c = new Roo.data.Connection();
9601 url : this.form.progressUrl,
9606 success : function(req){
9607 //console.log(data);
9611 rdata = Roo.decode(req.responseText)
9613 Roo.log("Invalid data from server..");
9617 if (!rdata || !rdata.success) {
9619 Roo.MessageBox.alert(Roo.encode(rdata));
9622 var data = rdata.data;
9624 if (this.uploadComplete) {
9625 Roo.MessageBox.hide();
9630 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9631 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9634 this.uploadProgress.defer(2000,this);
9637 failure: function(data) {
9638 Roo.log('progress url failed ');
9649 // run get Values on the form, so it syncs any secondary forms.
9650 this.form.getValues();
9652 var o = this.options;
9653 var method = this.getMethod();
9654 var isPost = method == 'POST';
9655 if(o.clientValidation === false || this.form.isValid()){
9657 if (this.form.progressUrl) {
9658 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9659 (new Date() * 1) + '' + Math.random());
9664 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9665 form:this.form.el.dom,
9666 url:this.getUrl(!isPost),
9668 params:isPost ? this.getParams() : null,
9669 isUpload: this.form.fileUpload,
9670 formData : this.form.formData
9673 this.uploadProgress();
9675 }else if (o.clientValidation !== false){ // client validation failed
9676 this.failureType = Roo.form.Action.CLIENT_INVALID;
9677 this.form.afterAction(this, false);
9681 success : function(response)
9683 this.uploadComplete= true;
9684 if (this.haveProgress) {
9685 Roo.MessageBox.hide();
9689 var result = this.processResponse(response);
9690 if(result === true || result.success){
9691 this.form.afterAction(this, true);
9695 this.form.markInvalid(result.errors);
9696 this.failureType = Roo.form.Action.SERVER_INVALID;
9698 this.form.afterAction(this, false);
9700 failure : function(response)
9702 this.uploadComplete= true;
9703 if (this.haveProgress) {
9704 Roo.MessageBox.hide();
9707 this.response = response;
9708 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9709 this.form.afterAction(this, false);
9712 handleResponse : function(response){
9713 if(this.form.errorReader){
9714 var rs = this.form.errorReader.read(response);
9717 for(var i = 0, len = rs.records.length; i < len; i++) {
9718 var r = rs.records[i];
9722 if(errors.length < 1){
9726 success : rs.success,
9732 ret = Roo.decode(response.responseText);
9736 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9746 Roo.form.Action.Load = function(form, options){
9747 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9748 this.reader = this.form.reader;
9751 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9756 Roo.Ajax.request(Roo.apply(
9757 this.createCallback(), {
9758 method:this.getMethod(),
9759 url:this.getUrl(false),
9760 params:this.getParams()
9764 success : function(response){
9766 var result = this.processResponse(response);
9767 if(result === true || !result.success || !result.data){
9768 this.failureType = Roo.form.Action.LOAD_FAILURE;
9769 this.form.afterAction(this, false);
9772 this.form.clearInvalid();
9773 this.form.setValues(result.data);
9774 this.form.afterAction(this, true);
9777 handleResponse : function(response){
9778 if(this.form.reader){
9779 var rs = this.form.reader.read(response);
9780 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9782 success : rs.success,
9786 return Roo.decode(response.responseText);
9790 Roo.form.Action.ACTION_TYPES = {
9791 'load' : Roo.form.Action.Load,
9792 'submit' : Roo.form.Action.Submit
9801 * @class Roo.bootstrap.Form
9802 * @extends Roo.bootstrap.Component
9803 * Bootstrap Form class
9804 * @cfg {String} method GET | POST (default POST)
9805 * @cfg {String} labelAlign top | left (default top)
9806 * @cfg {String} align left | right - for navbars
9807 * @cfg {Boolean} loadMask load mask when submit (default true)
9812 * @param {Object} config The config object
9816 Roo.bootstrap.Form = function(config){
9818 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9820 Roo.bootstrap.Form.popover.apply();
9824 * @event clientvalidation
9825 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9826 * @param {Form} this
9827 * @param {Boolean} valid true if the form has passed client-side validation
9829 clientvalidation: true,
9831 * @event beforeaction
9832 * Fires before any action is performed. Return false to cancel the action.
9833 * @param {Form} this
9834 * @param {Action} action The action to be performed
9838 * @event actionfailed
9839 * Fires when an action fails.
9840 * @param {Form} this
9841 * @param {Action} action The action that failed
9843 actionfailed : true,
9845 * @event actioncomplete
9846 * Fires when an action is completed.
9847 * @param {Form} this
9848 * @param {Action} action The action that completed
9850 actioncomplete : true
9854 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9857 * @cfg {String} method
9858 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9863 * The URL to use for form actions if one isn't supplied in the action options.
9866 * @cfg {Boolean} fileUpload
9867 * Set to true if this form is a file upload.
9871 * @cfg {Object} baseParams
9872 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9876 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9880 * @cfg {Sting} align (left|right) for navbar forms
9885 activeAction : null,
9888 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9889 * element by passing it or its id or mask the form itself by passing in true.
9892 waitMsgTarget : false,
9897 * @cfg {Boolean} errorMask (true|false) default false
9902 * @cfg {Number} maskOffset Default 100
9907 * @cfg {Boolean} maskBody
9911 getAutoCreate : function(){
9915 method : this.method || 'POST',
9916 id : this.id || Roo.id(),
9919 if (this.parent().xtype.match(/^Nav/)) {
9920 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9924 if (this.labelAlign == 'left' ) {
9925 cfg.cls += ' form-horizontal';
9931 initEvents : function()
9933 this.el.on('submit', this.onSubmit, this);
9934 // this was added as random key presses on the form where triggering form submit.
9935 this.el.on('keypress', function(e) {
9936 if (e.getCharCode() != 13) {
9939 // we might need to allow it for textareas.. and some other items.
9940 // check e.getTarget().
9942 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9946 Roo.log("keypress blocked");
9954 onSubmit : function(e){
9959 * Returns true if client-side validation on the form is successful.
9962 isValid : function(){
9963 var items = this.getItems();
9967 items.each(function(f){
9973 Roo.log('invalid field: ' + f.name);
9977 if(!target && f.el.isVisible(true)){
9983 if(this.errorMask && !valid){
9984 Roo.bootstrap.Form.popover.mask(this, target);
9991 * Returns true if any fields in this form have changed since their original load.
9994 isDirty : function(){
9996 var items = this.getItems();
9997 items.each(function(f){
10007 * Performs a predefined action (submit or load) or custom actions you define on this form.
10008 * @param {String} actionName The name of the action type
10009 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10010 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10011 * accept other config options):
10013 Property Type Description
10014 ---------------- --------------- ----------------------------------------------------------------------------------
10015 url String The url for the action (defaults to the form's url)
10016 method String The form method to use (defaults to the form's method, or POST if not defined)
10017 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10018 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10019 validate the form on the client (defaults to false)
10021 * @return {BasicForm} this
10023 doAction : function(action, options){
10024 if(typeof action == 'string'){
10025 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10027 if(this.fireEvent('beforeaction', this, action) !== false){
10028 this.beforeAction(action);
10029 action.run.defer(100, action);
10035 beforeAction : function(action){
10036 var o = action.options;
10041 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10046 // not really supported yet.. ??
10048 //if(this.waitMsgTarget === true){
10049 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10050 //}else if(this.waitMsgTarget){
10051 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10052 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10054 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10060 afterAction : function(action, success){
10061 this.activeAction = null;
10062 var o = action.options;
10067 Roo.get(document.body).unmask();
10073 //if(this.waitMsgTarget === true){
10074 // this.el.unmask();
10075 //}else if(this.waitMsgTarget){
10076 // this.waitMsgTarget.unmask();
10078 // Roo.MessageBox.updateProgress(1);
10079 // Roo.MessageBox.hide();
10086 Roo.callback(o.success, o.scope, [this, action]);
10087 this.fireEvent('actioncomplete', this, action);
10091 // failure condition..
10092 // we have a scenario where updates need confirming.
10093 // eg. if a locking scenario exists..
10094 // we look for { errors : { needs_confirm : true }} in the response.
10096 (typeof(action.result) != 'undefined') &&
10097 (typeof(action.result.errors) != 'undefined') &&
10098 (typeof(action.result.errors.needs_confirm) != 'undefined')
10101 Roo.log("not supported yet");
10104 Roo.MessageBox.confirm(
10105 "Change requires confirmation",
10106 action.result.errorMsg,
10111 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10121 Roo.callback(o.failure, o.scope, [this, action]);
10122 // show an error message if no failed handler is set..
10123 if (!this.hasListener('actionfailed')) {
10124 Roo.log("need to add dialog support");
10126 Roo.MessageBox.alert("Error",
10127 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10128 action.result.errorMsg :
10129 "Saving Failed, please check your entries or try again"
10134 this.fireEvent('actionfailed', this, action);
10139 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10140 * @param {String} id The value to search for
10143 findField : function(id){
10144 var items = this.getItems();
10145 var field = items.get(id);
10147 items.each(function(f){
10148 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10155 return field || null;
10158 * Mark fields in this form invalid in bulk.
10159 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10160 * @return {BasicForm} this
10162 markInvalid : function(errors){
10163 if(errors instanceof Array){
10164 for(var i = 0, len = errors.length; i < len; i++){
10165 var fieldError = errors[i];
10166 var f = this.findField(fieldError.id);
10168 f.markInvalid(fieldError.msg);
10174 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10175 field.markInvalid(errors[id]);
10179 //Roo.each(this.childForms || [], function (f) {
10180 // f.markInvalid(errors);
10187 * Set values for fields in this form in bulk.
10188 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10189 * @return {BasicForm} this
10191 setValues : function(values){
10192 if(values instanceof Array){ // array of objects
10193 for(var i = 0, len = values.length; i < len; i++){
10195 var f = this.findField(v.id);
10197 f.setValue(v.value);
10198 if(this.trackResetOnLoad){
10199 f.originalValue = f.getValue();
10203 }else{ // object hash
10206 if(typeof values[id] != 'function' && (field = this.findField(id))){
10208 if (field.setFromData &&
10209 field.valueField &&
10210 field.displayField &&
10211 // combos' with local stores can
10212 // be queried via setValue()
10213 // to set their value..
10214 (field.store && !field.store.isLocal)
10218 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10219 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10220 field.setFromData(sd);
10222 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10224 field.setFromData(values);
10227 field.setValue(values[id]);
10231 if(this.trackResetOnLoad){
10232 field.originalValue = field.getValue();
10238 //Roo.each(this.childForms || [], function (f) {
10239 // f.setValues(values);
10246 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10247 * they are returned as an array.
10248 * @param {Boolean} asString
10251 getValues : function(asString){
10252 //if (this.childForms) {
10253 // copy values from the child forms
10254 // Roo.each(this.childForms, function (f) {
10255 // this.setValues(f.getValues());
10261 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10262 if(asString === true){
10265 return Roo.urlDecode(fs);
10269 * Returns the fields in this form as an object with key/value pairs.
10270 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10273 getFieldValues : function(with_hidden)
10275 var items = this.getItems();
10277 items.each(function(f){
10279 if (!f.getName()) {
10283 var v = f.getValue();
10285 if (f.inputType =='radio') {
10286 if (typeof(ret[f.getName()]) == 'undefined') {
10287 ret[f.getName()] = ''; // empty..
10290 if (!f.el.dom.checked) {
10294 v = f.el.dom.value;
10298 if(f.xtype == 'MoneyField'){
10299 ret[f.currencyName] = f.getCurrency();
10302 // not sure if this supported any more..
10303 if ((typeof(v) == 'object') && f.getRawValue) {
10304 v = f.getRawValue() ; // dates..
10306 // combo boxes where name != hiddenName...
10307 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10308 ret[f.name] = f.getRawValue();
10310 ret[f.getName()] = v;
10317 * Clears all invalid messages in this form.
10318 * @return {BasicForm} this
10320 clearInvalid : function(){
10321 var items = this.getItems();
10323 items.each(function(f){
10331 * Resets this form.
10332 * @return {BasicForm} this
10334 reset : function(){
10335 var items = this.getItems();
10336 items.each(function(f){
10340 Roo.each(this.childForms || [], function (f) {
10348 getItems : function()
10350 var r=new Roo.util.MixedCollection(false, function(o){
10351 return o.id || (o.id = Roo.id());
10353 var iter = function(el) {
10360 Roo.each(el.items,function(e) {
10369 hideFields : function(items)
10371 Roo.each(items, function(i){
10373 var f = this.findField(i);
10384 showFields : function(items)
10386 Roo.each(items, function(i){
10388 var f = this.findField(i);
10401 Roo.apply(Roo.bootstrap.Form, {
10417 intervalID : false,
10423 if(this.isApplied){
10428 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10429 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10430 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10431 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10434 this.maskEl.top.enableDisplayMode("block");
10435 this.maskEl.left.enableDisplayMode("block");
10436 this.maskEl.bottom.enableDisplayMode("block");
10437 this.maskEl.right.enableDisplayMode("block");
10439 this.toolTip = new Roo.bootstrap.Tooltip({
10440 cls : 'roo-form-error-popover',
10442 'left' : ['r-l', [-2,0], 'right'],
10443 'right' : ['l-r', [2,0], 'left'],
10444 'bottom' : ['tl-bl', [0,2], 'top'],
10445 'top' : [ 'bl-tl', [0,-2], 'bottom']
10449 this.toolTip.render(Roo.get(document.body));
10451 this.toolTip.el.enableDisplayMode("block");
10453 Roo.get(document.body).on('click', function(){
10457 Roo.get(document.body).on('touchstart', function(){
10461 this.isApplied = true
10464 mask : function(form, target)
10468 this.target = target;
10470 if(!this.form.errorMask || !target.el){
10474 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10476 Roo.log(scrollable);
10478 var ot = this.target.el.calcOffsetsTo(scrollable);
10480 var scrollTo = ot[1] - this.form.maskOffset;
10482 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10484 scrollable.scrollTo('top', scrollTo);
10486 var box = this.target.el.getBox();
10488 var zIndex = Roo.bootstrap.Modal.zIndex++;
10491 this.maskEl.top.setStyle('position', 'absolute');
10492 this.maskEl.top.setStyle('z-index', zIndex);
10493 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10494 this.maskEl.top.setLeft(0);
10495 this.maskEl.top.setTop(0);
10496 this.maskEl.top.show();
10498 this.maskEl.left.setStyle('position', 'absolute');
10499 this.maskEl.left.setStyle('z-index', zIndex);
10500 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10501 this.maskEl.left.setLeft(0);
10502 this.maskEl.left.setTop(box.y - this.padding);
10503 this.maskEl.left.show();
10505 this.maskEl.bottom.setStyle('position', 'absolute');
10506 this.maskEl.bottom.setStyle('z-index', zIndex);
10507 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10508 this.maskEl.bottom.setLeft(0);
10509 this.maskEl.bottom.setTop(box.bottom + this.padding);
10510 this.maskEl.bottom.show();
10512 this.maskEl.right.setStyle('position', 'absolute');
10513 this.maskEl.right.setStyle('z-index', zIndex);
10514 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10515 this.maskEl.right.setLeft(box.right + this.padding);
10516 this.maskEl.right.setTop(box.y - this.padding);
10517 this.maskEl.right.show();
10519 this.toolTip.bindEl = this.target.el;
10521 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10523 var tip = this.target.blankText;
10525 if(this.target.getValue() !== '' ) {
10527 if (this.target.invalidText.length) {
10528 tip = this.target.invalidText;
10529 } else if (this.target.regexText.length){
10530 tip = this.target.regexText;
10534 this.toolTip.show(tip);
10536 this.intervalID = window.setInterval(function() {
10537 Roo.bootstrap.Form.popover.unmask();
10540 window.onwheel = function(){ return false;};
10542 (function(){ this.isMasked = true; }).defer(500, this);
10546 unmask : function()
10548 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10552 this.maskEl.top.setStyle('position', 'absolute');
10553 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10554 this.maskEl.top.hide();
10556 this.maskEl.left.setStyle('position', 'absolute');
10557 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10558 this.maskEl.left.hide();
10560 this.maskEl.bottom.setStyle('position', 'absolute');
10561 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10562 this.maskEl.bottom.hide();
10564 this.maskEl.right.setStyle('position', 'absolute');
10565 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10566 this.maskEl.right.hide();
10568 this.toolTip.hide();
10570 this.toolTip.el.hide();
10572 window.onwheel = function(){ return true;};
10574 if(this.intervalID){
10575 window.clearInterval(this.intervalID);
10576 this.intervalID = false;
10579 this.isMasked = false;
10589 * Ext JS Library 1.1.1
10590 * Copyright(c) 2006-2007, Ext JS, LLC.
10592 * Originally Released Under LGPL - original licence link has changed is not relivant.
10595 * <script type="text/javascript">
10598 * @class Roo.form.VTypes
10599 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10602 Roo.form.VTypes = function(){
10603 // closure these in so they are only created once.
10604 var alpha = /^[a-zA-Z_]+$/;
10605 var alphanum = /^[a-zA-Z0-9_]+$/;
10606 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10607 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10609 // All these messages and functions are configurable
10612 * The function used to validate email addresses
10613 * @param {String} value The email address
10615 'email' : function(v){
10616 return email.test(v);
10619 * The error text to display when the email validation function returns false
10622 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10624 * The keystroke filter mask to be applied on email input
10627 'emailMask' : /[a-z0-9_\.\-@]/i,
10630 * The function used to validate URLs
10631 * @param {String} value The URL
10633 'url' : function(v){
10634 return url.test(v);
10637 * The error text to display when the url validation function returns false
10640 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10643 * The function used to validate alpha values
10644 * @param {String} value The value
10646 'alpha' : function(v){
10647 return alpha.test(v);
10650 * The error text to display when the alpha validation function returns false
10653 'alphaText' : 'This field should only contain letters and _',
10655 * The keystroke filter mask to be applied on alpha input
10658 'alphaMask' : /[a-z_]/i,
10661 * The function used to validate alphanumeric values
10662 * @param {String} value The value
10664 'alphanum' : function(v){
10665 return alphanum.test(v);
10668 * The error text to display when the alphanumeric validation function returns false
10671 'alphanumText' : 'This field should only contain letters, numbers and _',
10673 * The keystroke filter mask to be applied on alphanumeric input
10676 'alphanumMask' : /[a-z0-9_]/i
10686 * @class Roo.bootstrap.Input
10687 * @extends Roo.bootstrap.Component
10688 * Bootstrap Input class
10689 * @cfg {Boolean} disabled is it disabled
10690 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10691 * @cfg {String} name name of the input
10692 * @cfg {string} fieldLabel - the label associated
10693 * @cfg {string} placeholder - placeholder to put in text.
10694 * @cfg {string} before - input group add on before
10695 * @cfg {string} after - input group add on after
10696 * @cfg {string} size - (lg|sm) or leave empty..
10697 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10698 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10699 * @cfg {Number} md colspan out of 12 for computer-sized screens
10700 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10701 * @cfg {string} value default value of the input
10702 * @cfg {Number} labelWidth set the width of label
10703 * @cfg {Number} labellg set the width of label (1-12)
10704 * @cfg {Number} labelmd set the width of label (1-12)
10705 * @cfg {Number} labelsm set the width of label (1-12)
10706 * @cfg {Number} labelxs set the width of label (1-12)
10707 * @cfg {String} labelAlign (top|left)
10708 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10709 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10710 * @cfg {String} indicatorpos (left|right) default left
10711 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10712 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10713 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10715 * @cfg {String} align (left|center|right) Default left
10716 * @cfg {Boolean} forceFeedback (true|false) Default false
10719 * Create a new Input
10720 * @param {Object} config The config object
10723 Roo.bootstrap.Input = function(config){
10725 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10730 * Fires when this field receives input focus.
10731 * @param {Roo.form.Field} this
10736 * Fires when this field loses input focus.
10737 * @param {Roo.form.Field} this
10741 * @event specialkey
10742 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10743 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10744 * @param {Roo.form.Field} this
10745 * @param {Roo.EventObject} e The event object
10750 * Fires just before the field blurs if the field value has changed.
10751 * @param {Roo.form.Field} this
10752 * @param {Mixed} newValue The new value
10753 * @param {Mixed} oldValue The original value
10758 * Fires after the field has been marked as invalid.
10759 * @param {Roo.form.Field} this
10760 * @param {String} msg The validation message
10765 * Fires after the field has been validated with no errors.
10766 * @param {Roo.form.Field} this
10771 * Fires after the key up
10772 * @param {Roo.form.Field} this
10773 * @param {Roo.EventObject} e The event Object
10778 * Fires after the user pastes into input
10779 * @param {Roo.form.Field} this
10780 * @param {Roo.EventObject} e The event Object
10786 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10788 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10789 automatic validation (defaults to "keyup").
10791 validationEvent : "keyup",
10793 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10795 validateOnBlur : true,
10797 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10799 validationDelay : 250,
10801 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10803 focusClass : "x-form-focus", // not needed???
10807 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10809 invalidClass : "has-warning",
10812 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10814 validClass : "has-success",
10817 * @cfg {Boolean} hasFeedback (true|false) default true
10819 hasFeedback : true,
10822 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10824 invalidFeedbackClass : "glyphicon-warning-sign",
10827 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10829 validFeedbackClass : "glyphicon-ok",
10832 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10834 selectOnFocus : false,
10837 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10841 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10846 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10848 disableKeyFilter : false,
10851 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10855 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10859 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10861 blankText : "Please complete this mandatory field",
10864 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10868 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10870 maxLength : Number.MAX_VALUE,
10872 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10874 minLengthText : "The minimum length for this field is {0}",
10876 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10878 maxLengthText : "The maximum length for this field is {0}",
10882 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10883 * If available, this function will be called only after the basic validators all return true, and will be passed the
10884 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10888 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10889 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10890 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10894 * @cfg {String} regexText -- Depricated - use Invalid Text
10899 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10905 autocomplete: false,
10909 inputType : 'text',
10912 placeholder: false,
10917 preventMark: false,
10918 isFormField : true,
10921 labelAlign : false,
10924 formatedValue : false,
10925 forceFeedback : false,
10927 indicatorpos : 'left',
10937 parentLabelAlign : function()
10940 while (parent.parent()) {
10941 parent = parent.parent();
10942 if (typeof(parent.labelAlign) !='undefined') {
10943 return parent.labelAlign;
10950 getAutoCreate : function()
10952 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10958 if(this.inputType != 'hidden'){
10959 cfg.cls = 'form-group' //input-group
10965 type : this.inputType,
10966 value : this.value,
10967 cls : 'form-control',
10968 placeholder : this.placeholder || '',
10969 autocomplete : this.autocomplete || 'new-password'
10971 if (this.inputType == 'file') {
10972 input.style = 'overflow:hidden'; // why not in CSS?
10975 if(this.capture.length){
10976 input.capture = this.capture;
10979 if(this.accept.length){
10980 input.accept = this.accept + "/*";
10984 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10987 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10988 input.maxLength = this.maxLength;
10991 if (this.disabled) {
10992 input.disabled=true;
10995 if (this.readOnly) {
10996 input.readonly=true;
11000 input.name = this.name;
11004 input.cls += ' input-' + this.size;
11008 ['xs','sm','md','lg'].map(function(size){
11009 if (settings[size]) {
11010 cfg.cls += ' col-' + size + '-' + settings[size];
11014 var inputblock = input;
11018 cls: 'glyphicon form-control-feedback'
11021 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11024 cls : 'has-feedback',
11032 if (this.before || this.after) {
11035 cls : 'input-group',
11039 if (this.before && typeof(this.before) == 'string') {
11041 inputblock.cn.push({
11043 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11047 if (this.before && typeof(this.before) == 'object') {
11048 this.before = Roo.factory(this.before);
11050 inputblock.cn.push({
11052 cls : 'roo-input-before input-group-prepend input-group-' +
11053 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11057 inputblock.cn.push(input);
11059 if (this.after && typeof(this.after) == 'string') {
11060 inputblock.cn.push({
11062 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11066 if (this.after && typeof(this.after) == 'object') {
11067 this.after = Roo.factory(this.after);
11069 inputblock.cn.push({
11071 cls : 'roo-input-after input-group-append input-group-' +
11072 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11076 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077 inputblock.cls += ' has-feedback';
11078 inputblock.cn.push(feedback);
11083 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11084 tooltip : 'This field is required'
11086 if (this.allowBlank ) {
11087 indicator.style = this.allowBlank ? ' display:none' : '';
11089 if (align ==='left' && this.fieldLabel.length) {
11091 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11098 cls : 'control-label col-form-label',
11099 html : this.fieldLabel
11110 var labelCfg = cfg.cn[1];
11111 var contentCfg = cfg.cn[2];
11113 if(this.indicatorpos == 'right'){
11118 cls : 'control-label col-form-label',
11122 html : this.fieldLabel
11136 labelCfg = cfg.cn[0];
11137 contentCfg = cfg.cn[1];
11141 if(this.labelWidth > 12){
11142 labelCfg.style = "width: " + this.labelWidth + 'px';
11145 if(this.labelWidth < 13 && this.labelmd == 0){
11146 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11149 if(this.labellg > 0){
11150 labelCfg.cls += ' col-lg-' + this.labellg;
11151 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11154 if(this.labelmd > 0){
11155 labelCfg.cls += ' col-md-' + this.labelmd;
11156 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11159 if(this.labelsm > 0){
11160 labelCfg.cls += ' col-sm-' + this.labelsm;
11161 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11164 if(this.labelxs > 0){
11165 labelCfg.cls += ' col-xs-' + this.labelxs;
11166 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11170 } else if ( this.fieldLabel.length) {
11177 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11178 tooltip : 'This field is required',
11179 style : this.allowBlank ? ' display:none' : ''
11183 //cls : 'input-group-addon',
11184 html : this.fieldLabel
11192 if(this.indicatorpos == 'right'){
11197 //cls : 'input-group-addon',
11198 html : this.fieldLabel
11203 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11204 tooltip : 'This field is required',
11205 style : this.allowBlank ? ' display:none' : ''
11225 if (this.parentType === 'Navbar' && this.parent().bar) {
11226 cfg.cls += ' navbar-form';
11229 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11230 // on BS4 we do this only if not form
11231 cfg.cls += ' navbar-form';
11239 * return the real input element.
11241 inputEl: function ()
11243 return this.el.select('input.form-control',true).first();
11246 tooltipEl : function()
11248 return this.inputEl();
11251 indicatorEl : function()
11253 if (Roo.bootstrap.version == 4) {
11254 return false; // not enabled in v4 yet.
11257 var indicator = this.el.select('i.roo-required-indicator',true).first();
11267 setDisabled : function(v)
11269 var i = this.inputEl().dom;
11271 i.removeAttribute('disabled');
11275 i.setAttribute('disabled','true');
11277 initEvents : function()
11280 this.inputEl().on("keydown" , this.fireKey, this);
11281 this.inputEl().on("focus", this.onFocus, this);
11282 this.inputEl().on("blur", this.onBlur, this);
11284 this.inputEl().relayEvent('keyup', this);
11285 this.inputEl().relayEvent('paste', this);
11287 this.indicator = this.indicatorEl();
11289 if(this.indicator){
11290 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11293 // reference to original value for reset
11294 this.originalValue = this.getValue();
11295 //Roo.form.TextField.superclass.initEvents.call(this);
11296 if(this.validationEvent == 'keyup'){
11297 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11298 this.inputEl().on('keyup', this.filterValidation, this);
11300 else if(this.validationEvent !== false){
11301 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11304 if(this.selectOnFocus){
11305 this.on("focus", this.preFocus, this);
11308 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11309 this.inputEl().on("keypress", this.filterKeys, this);
11311 this.inputEl().relayEvent('keypress', this);
11314 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11315 this.el.on("click", this.autoSize, this);
11318 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11319 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11322 if (typeof(this.before) == 'object') {
11323 this.before.render(this.el.select('.roo-input-before',true).first());
11325 if (typeof(this.after) == 'object') {
11326 this.after.render(this.el.select('.roo-input-after',true).first());
11329 this.inputEl().on('change', this.onChange, this);
11332 filterValidation : function(e){
11333 if(!e.isNavKeyPress()){
11334 this.validationTask.delay(this.validationDelay);
11338 * Validates the field value
11339 * @return {Boolean} True if the value is valid, else false
11341 validate : function(){
11342 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11343 if(this.disabled || this.validateValue(this.getRawValue())){
11348 this.markInvalid();
11354 * Validates a value according to the field's validation rules and marks the field as invalid
11355 * if the validation fails
11356 * @param {Mixed} value The value to validate
11357 * @return {Boolean} True if the value is valid, else false
11359 validateValue : function(value)
11361 if(this.getVisibilityEl().hasClass('hidden')){
11365 if(value.length < 1) { // if it's blank
11366 if(this.allowBlank){
11372 if(value.length < this.minLength){
11375 if(value.length > this.maxLength){
11379 var vt = Roo.form.VTypes;
11380 if(!vt[this.vtype](value, this)){
11384 if(typeof this.validator == "function"){
11385 var msg = this.validator(value);
11389 if (typeof(msg) == 'string') {
11390 this.invalidText = msg;
11394 if(this.regex && !this.regex.test(value)){
11402 fireKey : function(e){
11403 //Roo.log('field ' + e.getKey());
11404 if(e.isNavKeyPress()){
11405 this.fireEvent("specialkey", this, e);
11408 focus : function (selectText){
11410 this.inputEl().focus();
11411 if(selectText === true){
11412 this.inputEl().dom.select();
11418 onFocus : function(){
11419 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11420 // this.el.addClass(this.focusClass);
11422 if(!this.hasFocus){
11423 this.hasFocus = true;
11424 this.startValue = this.getValue();
11425 this.fireEvent("focus", this);
11429 beforeBlur : Roo.emptyFn,
11433 onBlur : function(){
11435 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436 //this.el.removeClass(this.focusClass);
11438 this.hasFocus = false;
11439 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11442 var v = this.getValue();
11443 if(String(v) !== String(this.startValue)){
11444 this.fireEvent('change', this, v, this.startValue);
11446 this.fireEvent("blur", this);
11449 onChange : function(e)
11451 var v = this.getValue();
11452 if(String(v) !== String(this.startValue)){
11453 this.fireEvent('change', this, v, this.startValue);
11459 * Resets the current field value to the originally loaded value and clears any validation messages
11461 reset : function(){
11462 this.setValue(this.originalValue);
11466 * Returns the name of the field
11467 * @return {Mixed} name The name field
11469 getName: function(){
11473 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11474 * @return {Mixed} value The field value
11476 getValue : function(){
11478 var v = this.inputEl().getValue();
11483 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11484 * @return {Mixed} value The field value
11486 getRawValue : function(){
11487 var v = this.inputEl().getValue();
11493 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11494 * @param {Mixed} value The value to set
11496 setRawValue : function(v){
11497 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11500 selectText : function(start, end){
11501 var v = this.getRawValue();
11503 start = start === undefined ? 0 : start;
11504 end = end === undefined ? v.length : end;
11505 var d = this.inputEl().dom;
11506 if(d.setSelectionRange){
11507 d.setSelectionRange(start, end);
11508 }else if(d.createTextRange){
11509 var range = d.createTextRange();
11510 range.moveStart("character", start);
11511 range.moveEnd("character", v.length-end);
11518 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11519 * @param {Mixed} value The value to set
11521 setValue : function(v){
11524 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11530 processValue : function(value){
11531 if(this.stripCharsRe){
11532 var newValue = value.replace(this.stripCharsRe, '');
11533 if(newValue !== value){
11534 this.setRawValue(newValue);
11541 preFocus : function(){
11543 if(this.selectOnFocus){
11544 this.inputEl().dom.select();
11547 filterKeys : function(e){
11548 var k = e.getKey();
11549 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11552 var c = e.getCharCode(), cc = String.fromCharCode(c);
11553 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11556 if(!this.maskRe.test(cc)){
11561 * Clear any invalid styles/messages for this field
11563 clearInvalid : function(){
11565 if(!this.el || this.preventMark){ // not rendered
11570 this.el.removeClass([this.invalidClass, 'is-invalid']);
11572 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11574 var feedback = this.el.select('.form-control-feedback', true).first();
11577 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11582 if(this.indicator){
11583 this.indicator.removeClass('visible');
11584 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11587 this.fireEvent('valid', this);
11591 * Mark this field as valid
11593 markValid : function()
11595 if(!this.el || this.preventMark){ // not rendered...
11599 this.el.removeClass([this.invalidClass, this.validClass]);
11600 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11602 var feedback = this.el.select('.form-control-feedback', true).first();
11605 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11608 if(this.indicator){
11609 this.indicator.removeClass('visible');
11610 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11618 if(this.allowBlank && !this.getRawValue().length){
11621 if (Roo.bootstrap.version == 3) {
11622 this.el.addClass(this.validClass);
11624 this.inputEl().addClass('is-valid');
11627 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11638 this.fireEvent('valid', this);
11642 * Mark this field as invalid
11643 * @param {String} msg The validation message
11645 markInvalid : function(msg)
11647 if(!this.el || this.preventMark){ // not rendered
11651 this.el.removeClass([this.invalidClass, this.validClass]);
11652 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11654 var feedback = this.el.select('.form-control-feedback', true).first();
11657 this.el.select('.form-control-feedback', true).first().removeClass(
11658 [this.invalidFeedbackClass, this.validFeedbackClass]);
11665 if(this.allowBlank && !this.getRawValue().length){
11669 if(this.indicator){
11670 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11671 this.indicator.addClass('visible');
11673 if (Roo.bootstrap.version == 3) {
11674 this.el.addClass(this.invalidClass);
11676 this.inputEl().addClass('is-invalid');
11681 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11683 var feedback = this.el.select('.form-control-feedback', true).first();
11686 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11688 if(this.getValue().length || this.forceFeedback){
11689 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11696 this.fireEvent('invalid', this, msg);
11699 SafariOnKeyDown : function(event)
11701 // this is a workaround for a password hang bug on chrome/ webkit.
11702 if (this.inputEl().dom.type != 'password') {
11706 var isSelectAll = false;
11708 if(this.inputEl().dom.selectionEnd > 0){
11709 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11711 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11712 event.preventDefault();
11717 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11719 event.preventDefault();
11720 // this is very hacky as keydown always get's upper case.
11722 var cc = String.fromCharCode(event.getCharCode());
11723 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11727 adjustWidth : function(tag, w){
11728 tag = tag.toLowerCase();
11729 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11730 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11731 if(tag == 'input'){
11734 if(tag == 'textarea'){
11737 }else if(Roo.isOpera){
11738 if(tag == 'input'){
11741 if(tag == 'textarea'){
11749 setFieldLabel : function(v)
11751 if(!this.rendered){
11755 if(this.indicatorEl()){
11756 var ar = this.el.select('label > span',true);
11758 if (ar.elements.length) {
11759 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11760 this.fieldLabel = v;
11764 var br = this.el.select('label',true);
11766 if(br.elements.length) {
11767 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11768 this.fieldLabel = v;
11772 Roo.log('Cannot Found any of label > span || label in input');
11776 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11777 this.fieldLabel = v;
11792 * @class Roo.bootstrap.TextArea
11793 * @extends Roo.bootstrap.Input
11794 * Bootstrap TextArea class
11795 * @cfg {Number} cols Specifies the visible width of a text area
11796 * @cfg {Number} rows Specifies the visible number of lines in a text area
11797 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11798 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11799 * @cfg {string} html text
11802 * Create a new TextArea
11803 * @param {Object} config The config object
11806 Roo.bootstrap.TextArea = function(config){
11807 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11811 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11821 getAutoCreate : function(){
11823 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11829 if(this.inputType != 'hidden'){
11830 cfg.cls = 'form-group' //input-group
11838 value : this.value || '',
11839 html: this.html || '',
11840 cls : 'form-control',
11841 placeholder : this.placeholder || ''
11845 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11846 input.maxLength = this.maxLength;
11850 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11854 input.cols = this.cols;
11857 if (this.readOnly) {
11858 input.readonly = true;
11862 input.name = this.name;
11866 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11870 ['xs','sm','md','lg'].map(function(size){
11871 if (settings[size]) {
11872 cfg.cls += ' col-' + size + '-' + settings[size];
11876 var inputblock = input;
11878 if(this.hasFeedback && !this.allowBlank){
11882 cls: 'glyphicon form-control-feedback'
11886 cls : 'has-feedback',
11895 if (this.before || this.after) {
11898 cls : 'input-group',
11902 inputblock.cn.push({
11904 cls : 'input-group-addon',
11909 inputblock.cn.push(input);
11911 if(this.hasFeedback && !this.allowBlank){
11912 inputblock.cls += ' has-feedback';
11913 inputblock.cn.push(feedback);
11917 inputblock.cn.push({
11919 cls : 'input-group-addon',
11926 if (align ==='left' && this.fieldLabel.length) {
11931 cls : 'control-label',
11932 html : this.fieldLabel
11943 if(this.labelWidth > 12){
11944 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11947 if(this.labelWidth < 13 && this.labelmd == 0){
11948 this.labelmd = this.labelWidth;
11951 if(this.labellg > 0){
11952 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11953 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11956 if(this.labelmd > 0){
11957 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11958 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11961 if(this.labelsm > 0){
11962 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11963 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11966 if(this.labelxs > 0){
11967 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11968 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11971 } else if ( this.fieldLabel.length) {
11976 //cls : 'input-group-addon',
11977 html : this.fieldLabel
11995 if (this.disabled) {
11996 input.disabled=true;
12003 * return the real textarea element.
12005 inputEl: function ()
12007 return this.el.select('textarea.form-control',true).first();
12011 * Clear any invalid styles/messages for this field
12013 clearInvalid : function()
12016 if(!this.el || this.preventMark){ // not rendered
12020 var label = this.el.select('label', true).first();
12021 var icon = this.el.select('i.fa-star', true).first();
12026 this.el.removeClass( this.validClass);
12027 this.inputEl().removeClass('is-invalid');
12029 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12031 var feedback = this.el.select('.form-control-feedback', true).first();
12034 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12039 this.fireEvent('valid', this);
12043 * Mark this field as valid
12045 markValid : function()
12047 if(!this.el || this.preventMark){ // not rendered
12051 this.el.removeClass([this.invalidClass, this.validClass]);
12052 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12054 var feedback = this.el.select('.form-control-feedback', true).first();
12057 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12060 if(this.disabled || this.allowBlank){
12064 var label = this.el.select('label', true).first();
12065 var icon = this.el.select('i.fa-star', true).first();
12070 if (Roo.bootstrap.version == 3) {
12071 this.el.addClass(this.validClass);
12073 this.inputEl().addClass('is-valid');
12077 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12079 var feedback = this.el.select('.form-control-feedback', true).first();
12082 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12083 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12088 this.fireEvent('valid', this);
12092 * Mark this field as invalid
12093 * @param {String} msg The validation message
12095 markInvalid : function(msg)
12097 if(!this.el || this.preventMark){ // not rendered
12101 this.el.removeClass([this.invalidClass, this.validClass]);
12102 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12104 var feedback = this.el.select('.form-control-feedback', true).first();
12107 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110 if(this.disabled || this.allowBlank){
12114 var label = this.el.select('label', true).first();
12115 var icon = this.el.select('i.fa-star', true).first();
12117 if(!this.getValue().length && label && !icon){
12118 this.el.createChild({
12120 cls : 'text-danger fa fa-lg fa-star',
12121 tooltip : 'This field is required',
12122 style : 'margin-right:5px;'
12126 if (Roo.bootstrap.version == 3) {
12127 this.el.addClass(this.invalidClass);
12129 this.inputEl().addClass('is-invalid');
12132 // fixme ... this may be depricated need to test..
12133 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12135 var feedback = this.el.select('.form-control-feedback', true).first();
12138 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12140 if(this.getValue().length || this.forceFeedback){
12141 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12148 this.fireEvent('invalid', this, msg);
12156 * trigger field - base class for combo..
12161 * @class Roo.bootstrap.TriggerField
12162 * @extends Roo.bootstrap.Input
12163 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12164 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12165 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12166 * for which you can provide a custom implementation. For example:
12168 var trigger = new Roo.bootstrap.TriggerField();
12169 trigger.onTriggerClick = myTriggerFn;
12170 trigger.applyTo('my-field');
12173 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12174 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12175 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12176 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12177 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12180 * Create a new TriggerField.
12181 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12182 * to the base TextField)
12184 Roo.bootstrap.TriggerField = function(config){
12185 this.mimicing = false;
12186 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12189 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12191 * @cfg {String} triggerClass A CSS class to apply to the trigger
12194 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12199 * @cfg {Boolean} removable (true|false) special filter default false
12203 /** @cfg {Boolean} grow @hide */
12204 /** @cfg {Number} growMin @hide */
12205 /** @cfg {Number} growMax @hide */
12211 autoSize: Roo.emptyFn,
12215 deferHeight : true,
12218 actionMode : 'wrap',
12223 getAutoCreate : function(){
12225 var align = this.labelAlign || this.parentLabelAlign();
12230 cls: 'form-group' //input-group
12237 type : this.inputType,
12238 cls : 'form-control',
12239 autocomplete: 'new-password',
12240 placeholder : this.placeholder || ''
12244 input.name = this.name;
12247 input.cls += ' input-' + this.size;
12250 if (this.disabled) {
12251 input.disabled=true;
12254 var inputblock = input;
12256 if(this.hasFeedback && !this.allowBlank){
12260 cls: 'glyphicon form-control-feedback'
12263 if(this.removable && !this.editable ){
12265 cls : 'has-feedback',
12271 cls : 'roo-combo-removable-btn close'
12278 cls : 'has-feedback',
12287 if(this.removable && !this.editable ){
12289 cls : 'roo-removable',
12295 cls : 'roo-combo-removable-btn close'
12302 if (this.before || this.after) {
12305 cls : 'input-group',
12309 inputblock.cn.push({
12311 cls : 'input-group-addon input-group-prepend input-group-text',
12316 inputblock.cn.push(input);
12318 if(this.hasFeedback && !this.allowBlank){
12319 inputblock.cls += ' has-feedback';
12320 inputblock.cn.push(feedback);
12324 inputblock.cn.push({
12326 cls : 'input-group-addon input-group-append input-group-text',
12335 var ibwrap = inputblock;
12340 cls: 'roo-select2-choices',
12344 cls: 'roo-select2-search-field',
12356 cls: 'roo-select2-container input-group',
12361 cls: 'form-hidden-field'
12367 if(!this.multiple && this.showToggleBtn){
12373 if (this.caret != false) {
12376 cls: 'fa fa-' + this.caret
12383 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12385 Roo.bootstrap.version == 3 ? caret : '',
12388 cls: 'combobox-clear',
12402 combobox.cls += ' roo-select2-container-multi';
12406 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12407 tooltip : 'This field is required'
12409 if (Roo.bootstrap.version == 4) {
12412 style : 'display:none'
12417 if (align ==='left' && this.fieldLabel.length) {
12419 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12426 cls : 'control-label',
12427 html : this.fieldLabel
12439 var labelCfg = cfg.cn[1];
12440 var contentCfg = cfg.cn[2];
12442 if(this.indicatorpos == 'right'){
12447 cls : 'control-label',
12451 html : this.fieldLabel
12465 labelCfg = cfg.cn[0];
12466 contentCfg = cfg.cn[1];
12469 if(this.labelWidth > 12){
12470 labelCfg.style = "width: " + this.labelWidth + 'px';
12473 if(this.labelWidth < 13 && this.labelmd == 0){
12474 this.labelmd = this.labelWidth;
12477 if(this.labellg > 0){
12478 labelCfg.cls += ' col-lg-' + this.labellg;
12479 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12482 if(this.labelmd > 0){
12483 labelCfg.cls += ' col-md-' + this.labelmd;
12484 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12487 if(this.labelsm > 0){
12488 labelCfg.cls += ' col-sm-' + this.labelsm;
12489 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12492 if(this.labelxs > 0){
12493 labelCfg.cls += ' col-xs-' + this.labelxs;
12494 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12497 } else if ( this.fieldLabel.length) {
12498 // Roo.log(" label");
12503 //cls : 'input-group-addon',
12504 html : this.fieldLabel
12512 if(this.indicatorpos == 'right'){
12520 html : this.fieldLabel
12534 // Roo.log(" no label && no align");
12541 ['xs','sm','md','lg'].map(function(size){
12542 if (settings[size]) {
12543 cfg.cls += ' col-' + size + '-' + settings[size];
12554 onResize : function(w, h){
12555 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12556 // if(typeof w == 'number'){
12557 // var x = w - this.trigger.getWidth();
12558 // this.inputEl().setWidth(this.adjustWidth('input', x));
12559 // this.trigger.setStyle('left', x+'px');
12564 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12567 getResizeEl : function(){
12568 return this.inputEl();
12572 getPositionEl : function(){
12573 return this.inputEl();
12577 alignErrorIcon : function(){
12578 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12582 initEvents : function(){
12586 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12587 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12588 if(!this.multiple && this.showToggleBtn){
12589 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12590 if(this.hideTrigger){
12591 this.trigger.setDisplayed(false);
12593 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12597 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12600 if(this.removable && !this.editable && !this.tickable){
12601 var close = this.closeTriggerEl();
12604 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12605 close.on('click', this.removeBtnClick, this, close);
12609 //this.trigger.addClassOnOver('x-form-trigger-over');
12610 //this.trigger.addClassOnClick('x-form-trigger-click');
12613 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12617 closeTriggerEl : function()
12619 var close = this.el.select('.roo-combo-removable-btn', true).first();
12620 return close ? close : false;
12623 removeBtnClick : function(e, h, el)
12625 e.preventDefault();
12627 if(this.fireEvent("remove", this) !== false){
12629 this.fireEvent("afterremove", this)
12633 createList : function()
12635 this.list = Roo.get(document.body).createChild({
12636 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12637 cls: 'typeahead typeahead-long dropdown-menu shadow',
12638 style: 'display:none'
12641 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12646 initTrigger : function(){
12651 onDestroy : function(){
12653 this.trigger.removeAllListeners();
12654 // this.trigger.remove();
12657 // this.wrap.remove();
12659 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12663 onFocus : function(){
12664 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12666 if(!this.mimicing){
12667 this.wrap.addClass('x-trigger-wrap-focus');
12668 this.mimicing = true;
12669 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12670 if(this.monitorTab){
12671 this.el.on("keydown", this.checkTab, this);
12678 checkTab : function(e){
12679 if(e.getKey() == e.TAB){
12680 this.triggerBlur();
12685 onBlur : function(){
12690 mimicBlur : function(e, t){
12692 if(!this.wrap.contains(t) && this.validateBlur()){
12693 this.triggerBlur();
12699 triggerBlur : function(){
12700 this.mimicing = false;
12701 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12702 if(this.monitorTab){
12703 this.el.un("keydown", this.checkTab, this);
12705 //this.wrap.removeClass('x-trigger-wrap-focus');
12706 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12710 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12711 validateBlur : function(e, t){
12716 onDisable : function(){
12717 this.inputEl().dom.disabled = true;
12718 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12720 // this.wrap.addClass('x-item-disabled');
12725 onEnable : function(){
12726 this.inputEl().dom.disabled = false;
12727 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12729 // this.el.removeClass('x-item-disabled');
12734 onShow : function(){
12735 var ae = this.getActionEl();
12738 ae.dom.style.display = '';
12739 ae.dom.style.visibility = 'visible';
12745 onHide : function(){
12746 var ae = this.getActionEl();
12747 ae.dom.style.display = 'none';
12751 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12752 * by an implementing function.
12754 * @param {EventObject} e
12756 onTriggerClick : Roo.emptyFn
12764 * @class Roo.bootstrap.CardUploader
12765 * @extends Roo.bootstrap.Button
12766 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12767 * @cfg {Number} errorTimeout default 3000
12768 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12769 * @cfg {Array} html The button text.
12773 * Create a new CardUploader
12774 * @param {Object} config The config object
12777 Roo.bootstrap.CardUploader = function(config){
12781 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12784 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12792 * When a image is clicked on - and needs to display a slideshow or similar..
12793 * @param {Roo.bootstrap.Card} this
12794 * @param {Object} The image information data
12800 * When a the download link is clicked
12801 * @param {Roo.bootstrap.Card} this
12802 * @param {Object} The image information data contains
12809 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12812 errorTimeout : 3000,
12816 fileCollection : false,
12819 getAutoCreate : function()
12823 cls :'form-group' ,
12828 //cls : 'input-group-addon',
12829 html : this.fieldLabel
12837 value : this.value,
12838 cls : 'd-none form-control'
12843 multiple : 'multiple',
12845 cls : 'd-none roo-card-upload-selector'
12849 cls : 'roo-card-uploader-button-container w-100 mb-2'
12852 cls : 'card-columns roo-card-uploader-container'
12862 getChildContainer : function() /// what children are added to.
12864 return this.containerEl;
12867 getButtonContainer : function() /// what children are added to.
12869 return this.el.select(".roo-card-uploader-button-container").first();
12872 initEvents : function()
12875 Roo.bootstrap.Input.prototype.initEvents.call(this);
12879 xns: Roo.bootstrap,
12882 container_method : 'getButtonContainer' ,
12883 html : this.html, // fix changable?
12886 'click' : function(btn, e) {
12895 this.urlAPI = (window.createObjectURL && window) ||
12896 (window.URL && URL.revokeObjectURL && URL) ||
12897 (window.webkitURL && webkitURL);
12902 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12904 this.selectorEl.on('change', this.onFileSelected, this);
12907 this.images.forEach(function(img) {
12910 this.images = false;
12912 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12918 onClick : function(e)
12920 e.preventDefault();
12922 this.selectorEl.dom.click();
12926 onFileSelected : function(e)
12928 e.preventDefault();
12930 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12934 Roo.each(this.selectorEl.dom.files, function(file){
12935 this.addFile(file);
12944 addFile : function(file)
12947 if(typeof(file) === 'string'){
12948 throw "Add file by name?"; // should not happen
12952 if(!file || !this.urlAPI){
12962 var url = _this.urlAPI.createObjectURL( file);
12965 id : Roo.bootstrap.CardUploader.ID--,
12966 is_uploaded : false,
12970 mimetype : file.type,
12978 * addCard - add an Attachment to the uploader
12979 * @param data - the data about the image to upload
12983 title : "Title of file",
12984 is_uploaded : false,
12985 src : "http://.....",
12986 srcfile : { the File upload object },
12987 mimetype : file.type,
12990 .. any other data...
12996 addCard : function (data)
12998 // hidden input element?
12999 // if the file is not an image...
13000 //then we need to use something other that and header_image
13005 xns : Roo.bootstrap,
13006 xtype : 'CardFooter',
13009 xns : Roo.bootstrap,
13015 xns : Roo.bootstrap,
13017 html : String.format("<small>{0}</small>", data.title),
13018 cls : 'col-10 text-left',
13023 click : function() {
13025 t.fireEvent( "download", t, data );
13031 xns : Roo.bootstrap,
13033 style: 'max-height: 28px; ',
13039 click : function() {
13040 t.removeCard(data.id)
13052 var cn = this.addxtype(
13055 xns : Roo.bootstrap,
13058 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13059 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13060 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13065 initEvents : function() {
13066 Roo.bootstrap.Card.prototype.initEvents.call(this);
13068 this.imgEl = this.el.select('.card-img-top').first();
13070 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13071 this.imgEl.set({ 'pointer' : 'cursor' });
13074 this.getCardFooter().addClass('p-1');
13081 // dont' really need ot update items.
13082 // this.items.push(cn);
13083 this.fileCollection.add(cn);
13085 if (!data.srcfile) {
13086 this.updateInput();
13091 var reader = new FileReader();
13092 reader.addEventListener("load", function() {
13093 data.srcdata = reader.result;
13096 reader.readAsDataURL(data.srcfile);
13101 removeCard : function(id)
13104 var card = this.fileCollection.get(id);
13105 card.data.is_deleted = 1;
13106 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13107 //this.fileCollection.remove(card);
13108 //this.items = this.items.filter(function(e) { return e != card });
13109 // dont' really need ot update items.
13110 card.el.dom.parentNode.removeChild(card.el.dom);
13111 this.updateInput();
13117 this.fileCollection.each(function(card) {
13118 if (card.el.dom && card.el.dom.parentNode) {
13119 card.el.dom.parentNode.removeChild(card.el.dom);
13122 this.fileCollection.clear();
13123 this.updateInput();
13126 updateInput : function()
13129 this.fileCollection.each(function(e) {
13133 this.inputEl().dom.value = JSON.stringify(data);
13143 Roo.bootstrap.CardUploader.ID = -1;/*
13145 * Ext JS Library 1.1.1
13146 * Copyright(c) 2006-2007, Ext JS, LLC.
13148 * Originally Released Under LGPL - original licence link has changed is not relivant.
13151 * <script type="text/javascript">
13156 * @class Roo.data.SortTypes
13158 * Defines the default sorting (casting?) comparison functions used when sorting data.
13160 Roo.data.SortTypes = {
13162 * Default sort that does nothing
13163 * @param {Mixed} s The value being converted
13164 * @return {Mixed} The comparison value
13166 none : function(s){
13171 * The regular expression used to strip tags
13175 stripTagsRE : /<\/?[^>]+>/gi,
13178 * Strips all HTML tags to sort on text only
13179 * @param {Mixed} s The value being converted
13180 * @return {String} The comparison value
13182 asText : function(s){
13183 return String(s).replace(this.stripTagsRE, "");
13187 * Strips all HTML tags to sort on text only - Case insensitive
13188 * @param {Mixed} s The value being converted
13189 * @return {String} The comparison value
13191 asUCText : function(s){
13192 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13196 * Case insensitive string
13197 * @param {Mixed} s The value being converted
13198 * @return {String} The comparison value
13200 asUCString : function(s) {
13201 return String(s).toUpperCase();
13206 * @param {Mixed} s The value being converted
13207 * @return {Number} The comparison value
13209 asDate : function(s) {
13213 if(s instanceof Date){
13214 return s.getTime();
13216 return Date.parse(String(s));
13221 * @param {Mixed} s The value being converted
13222 * @return {Float} The comparison value
13224 asFloat : function(s) {
13225 var val = parseFloat(String(s).replace(/,/g, ""));
13234 * @param {Mixed} s The value being converted
13235 * @return {Number} The comparison value
13237 asInt : function(s) {
13238 var val = parseInt(String(s).replace(/,/g, ""));
13246 * Ext JS Library 1.1.1
13247 * Copyright(c) 2006-2007, Ext JS, LLC.
13249 * Originally Released Under LGPL - original licence link has changed is not relivant.
13252 * <script type="text/javascript">
13256 * @class Roo.data.Record
13257 * Instances of this class encapsulate both record <em>definition</em> information, and record
13258 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13259 * to access Records cached in an {@link Roo.data.Store} object.<br>
13261 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13262 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13265 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13267 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13268 * {@link #create}. The parameters are the same.
13269 * @param {Array} data An associative Array of data values keyed by the field name.
13270 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13271 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13272 * not specified an integer id is generated.
13274 Roo.data.Record = function(data, id){
13275 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13280 * Generate a constructor for a specific record layout.
13281 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13282 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13283 * Each field definition object may contain the following properties: <ul>
13284 * <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,
13285 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13286 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13287 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13288 * is being used, then this is a string containing the javascript expression to reference the data relative to
13289 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13290 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13291 * this may be omitted.</p></li>
13292 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13293 * <ul><li>auto (Default, implies no conversion)</li>
13298 * <li>date</li></ul></p></li>
13299 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13300 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13301 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13302 * by the Reader into an object that will be stored in the Record. It is passed the
13303 * following parameters:<ul>
13304 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13306 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13308 * <br>usage:<br><pre><code>
13309 var TopicRecord = Roo.data.Record.create(
13310 {name: 'title', mapping: 'topic_title'},
13311 {name: 'author', mapping: 'username'},
13312 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13313 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13314 {name: 'lastPoster', mapping: 'user2'},
13315 {name: 'excerpt', mapping: 'post_text'}
13318 var myNewRecord = new TopicRecord({
13319 title: 'Do my job please',
13322 lastPost: new Date(),
13323 lastPoster: 'Animal',
13324 excerpt: 'No way dude!'
13326 myStore.add(myNewRecord);
13331 Roo.data.Record.create = function(o){
13332 var f = function(){
13333 f.superclass.constructor.apply(this, arguments);
13335 Roo.extend(f, Roo.data.Record);
13336 var p = f.prototype;
13337 p.fields = new Roo.util.MixedCollection(false, function(field){
13340 for(var i = 0, len = o.length; i < len; i++){
13341 p.fields.add(new Roo.data.Field(o[i]));
13343 f.getField = function(name){
13344 return p.fields.get(name);
13349 Roo.data.Record.AUTO_ID = 1000;
13350 Roo.data.Record.EDIT = 'edit';
13351 Roo.data.Record.REJECT = 'reject';
13352 Roo.data.Record.COMMIT = 'commit';
13354 Roo.data.Record.prototype = {
13356 * Readonly flag - true if this record has been modified.
13365 join : function(store){
13366 this.store = store;
13370 * Set the named field to the specified value.
13371 * @param {String} name The name of the field to set.
13372 * @param {Object} value The value to set the field to.
13374 set : function(name, value){
13375 if(this.data[name] == value){
13379 if(!this.modified){
13380 this.modified = {};
13382 if(typeof this.modified[name] == 'undefined'){
13383 this.modified[name] = this.data[name];
13385 this.data[name] = value;
13386 if(!this.editing && this.store){
13387 this.store.afterEdit(this);
13392 * Get the value of the named field.
13393 * @param {String} name The name of the field to get the value of.
13394 * @return {Object} The value of the field.
13396 get : function(name){
13397 return this.data[name];
13401 beginEdit : function(){
13402 this.editing = true;
13403 this.modified = {};
13407 cancelEdit : function(){
13408 this.editing = false;
13409 delete this.modified;
13413 endEdit : function(){
13414 this.editing = false;
13415 if(this.dirty && this.store){
13416 this.store.afterEdit(this);
13421 * Usually called by the {@link Roo.data.Store} which owns the Record.
13422 * Rejects all changes made to the Record since either creation, or the last commit operation.
13423 * Modified fields are reverted to their original values.
13425 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426 * of reject operations.
13428 reject : function(){
13429 var m = this.modified;
13431 if(typeof m[n] != "function"){
13432 this.data[n] = m[n];
13435 this.dirty = false;
13436 delete this.modified;
13437 this.editing = false;
13439 this.store.afterReject(this);
13444 * Usually called by the {@link Roo.data.Store} which owns the Record.
13445 * Commits all changes made to the Record since either creation, or the last commit operation.
13447 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13448 * of commit operations.
13450 commit : function(){
13451 this.dirty = false;
13452 delete this.modified;
13453 this.editing = false;
13455 this.store.afterCommit(this);
13460 hasError : function(){
13461 return this.error != null;
13465 clearError : function(){
13470 * Creates a copy of this record.
13471 * @param {String} id (optional) A new record id if you don't want to use this record's id
13474 copy : function(newId) {
13475 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13479 * Ext JS Library 1.1.1
13480 * Copyright(c) 2006-2007, Ext JS, LLC.
13482 * Originally Released Under LGPL - original licence link has changed is not relivant.
13485 * <script type="text/javascript">
13491 * @class Roo.data.Store
13492 * @extends Roo.util.Observable
13493 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13494 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13496 * 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
13497 * has no knowledge of the format of the data returned by the Proxy.<br>
13499 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13500 * instances from the data object. These records are cached and made available through accessor functions.
13502 * Creates a new Store.
13503 * @param {Object} config A config object containing the objects needed for the Store to access data,
13504 * and read the data into Records.
13506 Roo.data.Store = function(config){
13507 this.data = new Roo.util.MixedCollection(false);
13508 this.data.getKey = function(o){
13511 this.baseParams = {};
13513 this.paramNames = {
13518 "multisort" : "_multisort"
13521 if(config && config.data){
13522 this.inlineData = config.data;
13523 delete config.data;
13526 Roo.apply(this, config);
13528 if(this.reader){ // reader passed
13529 this.reader = Roo.factory(this.reader, Roo.data);
13530 this.reader.xmodule = this.xmodule || false;
13531 if(!this.recordType){
13532 this.recordType = this.reader.recordType;
13534 if(this.reader.onMetaChange){
13535 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13539 if(this.recordType){
13540 this.fields = this.recordType.prototype.fields;
13542 this.modified = [];
13546 * @event datachanged
13547 * Fires when the data cache has changed, and a widget which is using this Store
13548 * as a Record cache should refresh its view.
13549 * @param {Store} this
13551 datachanged : true,
13553 * @event metachange
13554 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13555 * @param {Store} this
13556 * @param {Object} meta The JSON metadata
13561 * Fires when Records have been added to the Store
13562 * @param {Store} this
13563 * @param {Roo.data.Record[]} records The array of Records added
13564 * @param {Number} index The index at which the record(s) were added
13569 * Fires when a Record has been removed from the Store
13570 * @param {Store} this
13571 * @param {Roo.data.Record} record The Record that was removed
13572 * @param {Number} index The index at which the record was removed
13577 * Fires when a Record has been updated
13578 * @param {Store} this
13579 * @param {Roo.data.Record} record The Record that was updated
13580 * @param {String} operation The update operation being performed. Value may be one of:
13582 Roo.data.Record.EDIT
13583 Roo.data.Record.REJECT
13584 Roo.data.Record.COMMIT
13590 * Fires when the data cache has been cleared.
13591 * @param {Store} this
13595 * @event beforeload
13596 * Fires before a request is made for a new data object. If the beforeload handler returns false
13597 * the load action will be canceled.
13598 * @param {Store} this
13599 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13603 * @event beforeloadadd
13604 * Fires after a new set of Records has been loaded.
13605 * @param {Store} this
13606 * @param {Roo.data.Record[]} records The Records that were loaded
13607 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13609 beforeloadadd : true,
13612 * Fires after a new set of Records has been loaded, before they are added to the store.
13613 * @param {Store} this
13614 * @param {Roo.data.Record[]} records The Records that were loaded
13615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616 * @params {Object} return from reader
13620 * @event loadexception
13621 * Fires if an exception occurs in the Proxy during loading.
13622 * Called with the signature of the Proxy's "loadexception" event.
13623 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13626 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13627 * @param {Object} load options
13628 * @param {Object} jsonData from your request (normally this contains the Exception)
13630 loadexception : true
13634 this.proxy = Roo.factory(this.proxy, Roo.data);
13635 this.proxy.xmodule = this.xmodule || false;
13636 this.relayEvents(this.proxy, ["loadexception"]);
13638 this.sortToggle = {};
13639 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13641 Roo.data.Store.superclass.constructor.call(this);
13643 if(this.inlineData){
13644 this.loadData(this.inlineData);
13645 delete this.inlineData;
13649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13651 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13652 * without a remote query - used by combo/forms at present.
13656 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13659 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13662 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13663 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13666 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13667 * on any HTTP request
13670 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13673 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13677 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13678 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13680 remoteSort : false,
13683 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13684 * loaded or when a record is removed. (defaults to false).
13686 pruneModifiedRecords : false,
13689 lastOptions : null,
13692 * Add Records to the Store and fires the add event.
13693 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13695 add : function(records){
13696 records = [].concat(records);
13697 for(var i = 0, len = records.length; i < len; i++){
13698 records[i].join(this);
13700 var index = this.data.length;
13701 this.data.addAll(records);
13702 this.fireEvent("add", this, records, index);
13706 * Remove a Record from the Store and fires the remove event.
13707 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13709 remove : function(record){
13710 var index = this.data.indexOf(record);
13711 this.data.removeAt(index);
13713 if(this.pruneModifiedRecords){
13714 this.modified.remove(record);
13716 this.fireEvent("remove", this, record, index);
13720 * Remove all Records from the Store and fires the clear event.
13722 removeAll : function(){
13724 if(this.pruneModifiedRecords){
13725 this.modified = [];
13727 this.fireEvent("clear", this);
13731 * Inserts Records to the Store at the given index and fires the add event.
13732 * @param {Number} index The start index at which to insert the passed Records.
13733 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13735 insert : function(index, records){
13736 records = [].concat(records);
13737 for(var i = 0, len = records.length; i < len; i++){
13738 this.data.insert(index, records[i]);
13739 records[i].join(this);
13741 this.fireEvent("add", this, records, index);
13745 * Get the index within the cache of the passed Record.
13746 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13747 * @return {Number} The index of the passed Record. Returns -1 if not found.
13749 indexOf : function(record){
13750 return this.data.indexOf(record);
13754 * Get the index within the cache of the Record with the passed id.
13755 * @param {String} id The id of the Record to find.
13756 * @return {Number} The index of the Record. Returns -1 if not found.
13758 indexOfId : function(id){
13759 return this.data.indexOfKey(id);
13763 * Get the Record with the specified id.
13764 * @param {String} id The id of the Record to find.
13765 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13767 getById : function(id){
13768 return this.data.key(id);
13772 * Get the Record at the specified index.
13773 * @param {Number} index The index of the Record to find.
13774 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13776 getAt : function(index){
13777 return this.data.itemAt(index);
13781 * Returns a range of Records between specified indices.
13782 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13783 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13784 * @return {Roo.data.Record[]} An array of Records
13786 getRange : function(start, end){
13787 return this.data.getRange(start, end);
13791 storeOptions : function(o){
13792 o = Roo.apply({}, o);
13795 this.lastOptions = o;
13799 * Loads the Record cache from the configured Proxy using the configured Reader.
13801 * If using remote paging, then the first load call must specify the <em>start</em>
13802 * and <em>limit</em> properties in the options.params property to establish the initial
13803 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13805 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13806 * and this call will return before the new data has been loaded. Perform any post-processing
13807 * in a callback function, or in a "load" event handler.</strong>
13809 * @param {Object} options An object containing properties which control loading options:<ul>
13810 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13811 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13812 * passed the following arguments:<ul>
13813 * <li>r : Roo.data.Record[]</li>
13814 * <li>options: Options object from the load call</li>
13815 * <li>success: Boolean success indicator</li></ul></li>
13816 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13817 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13820 load : function(options){
13821 options = options || {};
13822 if(this.fireEvent("beforeload", this, options) !== false){
13823 this.storeOptions(options);
13824 var p = Roo.apply(options.params || {}, this.baseParams);
13825 // if meta was not loaded from remote source.. try requesting it.
13826 if (!this.reader.metaFromRemote) {
13827 p._requestMeta = 1;
13829 if(this.sortInfo && this.remoteSort){
13830 var pn = this.paramNames;
13831 p[pn["sort"]] = this.sortInfo.field;
13832 p[pn["dir"]] = this.sortInfo.direction;
13834 if (this.multiSort) {
13835 var pn = this.paramNames;
13836 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13839 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13844 * Reloads the Record cache from the configured Proxy using the configured Reader and
13845 * the options from the last load operation performed.
13846 * @param {Object} options (optional) An object containing properties which may override the options
13847 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13848 * the most recently used options are reused).
13850 reload : function(options){
13851 this.load(Roo.applyIf(options||{}, this.lastOptions));
13855 // Called as a callback by the Reader during a load operation.
13856 loadRecords : function(o, options, success){
13857 if(!o || success === false){
13858 if(success !== false){
13859 this.fireEvent("load", this, [], options, o);
13861 if(options.callback){
13862 options.callback.call(options.scope || this, [], options, false);
13866 // if data returned failure - throw an exception.
13867 if (o.success === false) {
13868 // show a message if no listener is registered.
13869 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13870 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13872 // loadmask wil be hooked into this..
13873 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13876 var r = o.records, t = o.totalRecords || r.length;
13878 this.fireEvent("beforeloadadd", this, r, options, o);
13880 if(!options || options.add !== true){
13881 if(this.pruneModifiedRecords){
13882 this.modified = [];
13884 for(var i = 0, len = r.length; i < len; i++){
13888 this.data = this.snapshot;
13889 delete this.snapshot;
13892 this.data.addAll(r);
13893 this.totalLength = t;
13895 this.fireEvent("datachanged", this);
13897 this.totalLength = Math.max(t, this.data.length+r.length);
13901 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13903 var e = new Roo.data.Record({});
13905 e.set(this.parent.displayField, this.parent.emptyTitle);
13906 e.set(this.parent.valueField, '');
13911 this.fireEvent("load", this, r, options, o);
13912 if(options.callback){
13913 options.callback.call(options.scope || this, r, options, true);
13919 * Loads data from a passed data block. A Reader which understands the format of the data
13920 * must have been configured in the constructor.
13921 * @param {Object} data The data block from which to read the Records. The format of the data expected
13922 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13923 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13925 loadData : function(o, append){
13926 var r = this.reader.readRecords(o);
13927 this.loadRecords(r, {add: append}, true);
13931 * using 'cn' the nested child reader read the child array into it's child stores.
13932 * @param {Object} rec The record with a 'children array
13934 loadDataFromChildren : function(rec)
13936 this.loadData(this.reader.toLoadData(rec));
13941 * Gets the number of cached records.
13943 * <em>If using paging, this may not be the total size of the dataset. If the data object
13944 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13945 * the data set size</em>
13947 getCount : function(){
13948 return this.data.length || 0;
13952 * Gets the total number of records in the dataset as returned by the server.
13954 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13955 * the dataset size</em>
13957 getTotalCount : function(){
13958 return this.totalLength || 0;
13962 * Returns the sort state of the Store as an object with two properties:
13964 field {String} The name of the field by which the Records are sorted
13965 direction {String} The sort order, "ASC" or "DESC"
13968 getSortState : function(){
13969 return this.sortInfo;
13973 applySort : function(){
13974 if(this.sortInfo && !this.remoteSort){
13975 var s = this.sortInfo, f = s.field;
13976 var st = this.fields.get(f).sortType;
13977 var fn = function(r1, r2){
13978 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13979 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13981 this.data.sort(s.direction, fn);
13982 if(this.snapshot && this.snapshot != this.data){
13983 this.snapshot.sort(s.direction, fn);
13989 * Sets the default sort column and order to be used by the next load operation.
13990 * @param {String} fieldName The name of the field to sort by.
13991 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13993 setDefaultSort : function(field, dir){
13994 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13998 * Sort the Records.
13999 * If remote sorting is used, the sort is performed on the server, and the cache is
14000 * reloaded. If local sorting is used, the cache is sorted internally.
14001 * @param {String} fieldName The name of the field to sort by.
14002 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14004 sort : function(fieldName, dir){
14005 var f = this.fields.get(fieldName);
14007 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14009 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14010 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14015 this.sortToggle[f.name] = dir;
14016 this.sortInfo = {field: f.name, direction: dir};
14017 if(!this.remoteSort){
14019 this.fireEvent("datachanged", this);
14021 this.load(this.lastOptions);
14026 * Calls the specified function for each of the Records in the cache.
14027 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14028 * Returning <em>false</em> aborts and exits the iteration.
14029 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14031 each : function(fn, scope){
14032 this.data.each(fn, scope);
14036 * Gets all records modified since the last commit. Modified records are persisted across load operations
14037 * (e.g., during paging).
14038 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14040 getModifiedRecords : function(){
14041 return this.modified;
14045 createFilterFn : function(property, value, anyMatch){
14046 if(!value.exec){ // not a regex
14047 value = String(value);
14048 if(value.length == 0){
14051 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14053 return function(r){
14054 return value.test(r.data[property]);
14059 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14060 * @param {String} property A field on your records
14061 * @param {Number} start The record index to start at (defaults to 0)
14062 * @param {Number} end The last record index to include (defaults to length - 1)
14063 * @return {Number} The sum
14065 sum : function(property, start, end){
14066 var rs = this.data.items, v = 0;
14067 start = start || 0;
14068 end = (end || end === 0) ? end : rs.length-1;
14070 for(var i = start; i <= end; i++){
14071 v += (rs[i].data[property] || 0);
14077 * Filter the records by a specified property.
14078 * @param {String} field A field on your records
14079 * @param {String/RegExp} value Either a string that the field
14080 * should start with or a RegExp to test against the field
14081 * @param {Boolean} anyMatch True to match any part not just the beginning
14083 filter : function(property, value, anyMatch){
14084 var fn = this.createFilterFn(property, value, anyMatch);
14085 return fn ? this.filterBy(fn) : this.clearFilter();
14089 * Filter by a function. The specified function will be called with each
14090 * record in this data source. If the function returns true the record is included,
14091 * otherwise it is filtered.
14092 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14093 * @param {Object} scope (optional) The scope of the function (defaults to this)
14095 filterBy : function(fn, scope){
14096 this.snapshot = this.snapshot || this.data;
14097 this.data = this.queryBy(fn, scope||this);
14098 this.fireEvent("datachanged", this);
14102 * Query the records by a specified property.
14103 * @param {String} field A field on your records
14104 * @param {String/RegExp} value Either a string that the field
14105 * should start with or a RegExp to test against the field
14106 * @param {Boolean} anyMatch True to match any part not just the beginning
14107 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14109 query : function(property, value, anyMatch){
14110 var fn = this.createFilterFn(property, value, anyMatch);
14111 return fn ? this.queryBy(fn) : this.data.clone();
14115 * Query by a function. The specified function will be called with each
14116 * record in this data source. If the function returns true the record is included
14118 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14119 * @param {Object} scope (optional) The scope of the function (defaults to this)
14120 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14122 queryBy : function(fn, scope){
14123 var data = this.snapshot || this.data;
14124 return data.filterBy(fn, scope||this);
14128 * Collects unique values for a particular dataIndex from this store.
14129 * @param {String} dataIndex The property to collect
14130 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14131 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14132 * @return {Array} An array of the unique values
14134 collect : function(dataIndex, allowNull, bypassFilter){
14135 var d = (bypassFilter === true && this.snapshot) ?
14136 this.snapshot.items : this.data.items;
14137 var v, sv, r = [], l = {};
14138 for(var i = 0, len = d.length; i < len; i++){
14139 v = d[i].data[dataIndex];
14141 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14150 * Revert to a view of the Record cache with no filtering applied.
14151 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14153 clearFilter : function(suppressEvent){
14154 if(this.snapshot && this.snapshot != this.data){
14155 this.data = this.snapshot;
14156 delete this.snapshot;
14157 if(suppressEvent !== true){
14158 this.fireEvent("datachanged", this);
14164 afterEdit : function(record){
14165 if(this.modified.indexOf(record) == -1){
14166 this.modified.push(record);
14168 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14172 afterReject : function(record){
14173 this.modified.remove(record);
14174 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14178 afterCommit : function(record){
14179 this.modified.remove(record);
14180 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14184 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14185 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14187 commitChanges : function(){
14188 var m = this.modified.slice(0);
14189 this.modified = [];
14190 for(var i = 0, len = m.length; i < len; i++){
14196 * Cancel outstanding changes on all changed records.
14198 rejectChanges : function(){
14199 var m = this.modified.slice(0);
14200 this.modified = [];
14201 for(var i = 0, len = m.length; i < len; i++){
14206 onMetaChange : function(meta, rtype, o){
14207 this.recordType = rtype;
14208 this.fields = rtype.prototype.fields;
14209 delete this.snapshot;
14210 this.sortInfo = meta.sortInfo || this.sortInfo;
14211 this.modified = [];
14212 this.fireEvent('metachange', this, this.reader.meta);
14215 moveIndex : function(data, type)
14217 var index = this.indexOf(data);
14219 var newIndex = index + type;
14223 this.insert(newIndex, data);
14228 * Ext JS Library 1.1.1
14229 * Copyright(c) 2006-2007, Ext JS, LLC.
14231 * Originally Released Under LGPL - original licence link has changed is not relivant.
14234 * <script type="text/javascript">
14238 * @class Roo.data.SimpleStore
14239 * @extends Roo.data.Store
14240 * Small helper class to make creating Stores from Array data easier.
14241 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14242 * @cfg {Array} fields An array of field definition objects, or field name strings.
14243 * @cfg {Object} an existing reader (eg. copied from another store)
14244 * @cfg {Array} data The multi-dimensional array of data
14246 * @param {Object} config
14248 Roo.data.SimpleStore = function(config)
14250 Roo.data.SimpleStore.superclass.constructor.call(this, {
14252 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14255 Roo.data.Record.create(config.fields)
14257 proxy : new Roo.data.MemoryProxy(config.data)
14261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14263 * Ext JS Library 1.1.1
14264 * Copyright(c) 2006-2007, Ext JS, LLC.
14266 * Originally Released Under LGPL - original licence link has changed is not relivant.
14269 * <script type="text/javascript">
14274 * @extends Roo.data.Store
14275 * @class Roo.data.JsonStore
14276 * Small helper class to make creating Stores for JSON data easier. <br/>
14278 var store = new Roo.data.JsonStore({
14279 url: 'get-images.php',
14281 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14284 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14285 * JsonReader and HttpProxy (unless inline data is provided).</b>
14286 * @cfg {Array} fields An array of field definition objects, or field name strings.
14288 * @param {Object} config
14290 Roo.data.JsonStore = function(c){
14291 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14292 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14293 reader: new Roo.data.JsonReader(c, c.fields)
14296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14308 Roo.data.Field = function(config){
14309 if(typeof config == "string"){
14310 config = {name: config};
14312 Roo.apply(this, config);
14315 this.type = "auto";
14318 var st = Roo.data.SortTypes;
14319 // named sortTypes are supported, here we look them up
14320 if(typeof this.sortType == "string"){
14321 this.sortType = st[this.sortType];
14324 // set default sortType for strings and dates
14325 if(!this.sortType){
14328 this.sortType = st.asUCString;
14331 this.sortType = st.asDate;
14334 this.sortType = st.none;
14339 var stripRe = /[\$,%]/g;
14341 // prebuilt conversion function for this field, instead of
14342 // switching every time we're reading a value
14344 var cv, dateFormat = this.dateFormat;
14349 cv = function(v){ return v; };
14352 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14356 return v !== undefined && v !== null && v !== '' ?
14357 parseInt(String(v).replace(stripRe, ""), 10) : '';
14362 return v !== undefined && v !== null && v !== '' ?
14363 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14368 cv = function(v){ return v === true || v === "true" || v == 1; };
14375 if(v instanceof Date){
14379 if(dateFormat == "timestamp"){
14380 return new Date(v*1000);
14382 return Date.parseDate(v, dateFormat);
14384 var parsed = Date.parse(v);
14385 return parsed ? new Date(parsed) : null;
14394 Roo.data.Field.prototype = {
14402 * Ext JS Library 1.1.1
14403 * Copyright(c) 2006-2007, Ext JS, LLC.
14405 * Originally Released Under LGPL - original licence link has changed is not relivant.
14408 * <script type="text/javascript">
14411 // Base class for reading structured data from a data source. This class is intended to be
14412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14415 * @class Roo.data.DataReader
14416 * Base class for reading structured data from a data source. This class is intended to be
14417 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14420 Roo.data.DataReader = function(meta, recordType){
14424 this.recordType = recordType instanceof Array ?
14425 Roo.data.Record.create(recordType) : recordType;
14428 Roo.data.DataReader.prototype = {
14431 readerType : 'Data',
14433 * Create an empty record
14434 * @param {Object} data (optional) - overlay some values
14435 * @return {Roo.data.Record} record created.
14437 newRow : function(d) {
14439 this.recordType.prototype.fields.each(function(c) {
14441 case 'int' : da[c.name] = 0; break;
14442 case 'date' : da[c.name] = new Date(); break;
14443 case 'float' : da[c.name] = 0.0; break;
14444 case 'boolean' : da[c.name] = false; break;
14445 default : da[c.name] = ""; break;
14449 return new this.recordType(Roo.apply(da, d));
14455 * Ext JS Library 1.1.1
14456 * Copyright(c) 2006-2007, Ext JS, LLC.
14458 * Originally Released Under LGPL - original licence link has changed is not relivant.
14461 * <script type="text/javascript">
14465 * @class Roo.data.DataProxy
14466 * @extends Roo.data.Observable
14467 * This class is an abstract base class for implementations which provide retrieval of
14468 * unformatted data objects.<br>
14470 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14471 * (of the appropriate type which knows how to parse the data object) to provide a block of
14472 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14474 * Custom implementations must implement the load method as described in
14475 * {@link Roo.data.HttpProxy#load}.
14477 Roo.data.DataProxy = function(){
14480 * @event beforeload
14481 * Fires before a network request is made to retrieve a data object.
14482 * @param {Object} This DataProxy object.
14483 * @param {Object} params The params parameter to the load function.
14488 * Fires before the load method's callback is called.
14489 * @param {Object} This DataProxy object.
14490 * @param {Object} o The data object.
14491 * @param {Object} arg The callback argument object passed to the load function.
14495 * @event loadexception
14496 * Fires if an Exception occurs during data retrieval.
14497 * @param {Object} This DataProxy object.
14498 * @param {Object} o The data object.
14499 * @param {Object} arg The callback argument object passed to the load function.
14500 * @param {Object} e The Exception.
14502 loadexception : true
14504 Roo.data.DataProxy.superclass.constructor.call(this);
14507 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14510 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14514 * Ext JS Library 1.1.1
14515 * Copyright(c) 2006-2007, Ext JS, LLC.
14517 * Originally Released Under LGPL - original licence link has changed is not relivant.
14520 * <script type="text/javascript">
14523 * @class Roo.data.MemoryProxy
14524 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14525 * to the Reader when its load method is called.
14527 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14529 Roo.data.MemoryProxy = function(data){
14533 Roo.data.MemoryProxy.superclass.constructor.call(this);
14537 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14540 * Load data from the requested source (in this case an in-memory
14541 * data object passed to the constructor), read the data object into
14542 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14543 * process that block using the passed callback.
14544 * @param {Object} params This parameter is not used by the MemoryProxy class.
14545 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14546 * object into a block of Roo.data.Records.
14547 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14548 * The function must be passed <ul>
14549 * <li>The Record block object</li>
14550 * <li>The "arg" argument from the load function</li>
14551 * <li>A boolean success indicator</li>
14553 * @param {Object} scope The scope in which to call the callback
14554 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14556 load : function(params, reader, callback, scope, arg){
14557 params = params || {};
14560 result = reader.readRecords(params.data ? params.data :this.data);
14562 this.fireEvent("loadexception", this, arg, null, e);
14563 callback.call(scope, null, arg, false);
14566 callback.call(scope, result, arg, true);
14570 update : function(params, records){
14575 * Ext JS Library 1.1.1
14576 * Copyright(c) 2006-2007, Ext JS, LLC.
14578 * Originally Released Under LGPL - original licence link has changed is not relivant.
14581 * <script type="text/javascript">
14584 * @class Roo.data.HttpProxy
14585 * @extends Roo.data.DataProxy
14586 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14587 * configured to reference a certain URL.<br><br>
14589 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14590 * from which the running page was served.<br><br>
14592 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14594 * Be aware that to enable the browser to parse an XML document, the server must set
14595 * the Content-Type header in the HTTP response to "text/xml".
14597 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14598 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14599 * will be used to make the request.
14601 Roo.data.HttpProxy = function(conn){
14602 Roo.data.HttpProxy.superclass.constructor.call(this);
14603 // is conn a conn config or a real conn?
14605 this.useAjax = !conn || !conn.events;
14609 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14610 // thse are take from connection...
14613 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14616 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14617 * extra parameters to each request made by this object. (defaults to undefined)
14620 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14621 * to each request made by this object. (defaults to undefined)
14624 * @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)
14627 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14630 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14636 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14640 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14641 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14642 * a finer-grained basis than the DataProxy events.
14644 getConnection : function(){
14645 return this.useAjax ? Roo.Ajax : this.conn;
14649 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14650 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14651 * process that block using the passed callback.
14652 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14653 * for the request to the remote server.
14654 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14655 * object into a block of Roo.data.Records.
14656 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14657 * The function must be passed <ul>
14658 * <li>The Record block object</li>
14659 * <li>The "arg" argument from the load function</li>
14660 * <li>A boolean success indicator</li>
14662 * @param {Object} scope The scope in which to call the callback
14663 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14665 load : function(params, reader, callback, scope, arg){
14666 if(this.fireEvent("beforeload", this, params) !== false){
14668 params : params || {},
14670 callback : callback,
14675 callback : this.loadResponse,
14679 Roo.applyIf(o, this.conn);
14680 if(this.activeRequest){
14681 Roo.Ajax.abort(this.activeRequest);
14683 this.activeRequest = Roo.Ajax.request(o);
14685 this.conn.request(o);
14688 callback.call(scope||this, null, arg, false);
14693 loadResponse : function(o, success, response){
14694 delete this.activeRequest;
14696 this.fireEvent("loadexception", this, o, response);
14697 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14702 result = o.reader.read(response);
14704 this.fireEvent("loadexception", this, o, response, e);
14705 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14709 this.fireEvent("load", this, o, o.request.arg);
14710 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14714 update : function(dataSet){
14719 updateResponse : function(dataSet){
14724 * Ext JS Library 1.1.1
14725 * Copyright(c) 2006-2007, Ext JS, LLC.
14727 * Originally Released Under LGPL - original licence link has changed is not relivant.
14730 * <script type="text/javascript">
14734 * @class Roo.data.ScriptTagProxy
14735 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14736 * other than the originating domain of the running page.<br><br>
14738 * <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
14739 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14741 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14742 * source code that is used as the source inside a <script> tag.<br><br>
14744 * In order for the browser to process the returned data, the server must wrap the data object
14745 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14746 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14747 * depending on whether the callback name was passed:
14750 boolean scriptTag = false;
14751 String cb = request.getParameter("callback");
14754 response.setContentType("text/javascript");
14756 response.setContentType("application/x-json");
14758 Writer out = response.getWriter();
14760 out.write(cb + "(");
14762 out.print(dataBlock.toJsonString());
14769 * @param {Object} config A configuration object.
14771 Roo.data.ScriptTagProxy = function(config){
14772 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14773 Roo.apply(this, config);
14774 this.head = document.getElementsByTagName("head")[0];
14777 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14779 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14781 * @cfg {String} url The URL from which to request the data object.
14784 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14788 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14789 * the server the name of the callback function set up by the load call to process the returned data object.
14790 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14791 * javascript output which calls this named function passing the data object as its only parameter.
14793 callbackParam : "callback",
14795 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14796 * name to the request.
14801 * Load data from the configured URL, read the data object into
14802 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14803 * process that block using the passed callback.
14804 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14805 * for the request to the remote server.
14806 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14807 * object into a block of Roo.data.Records.
14808 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14809 * The function must be passed <ul>
14810 * <li>The Record block object</li>
14811 * <li>The "arg" argument from the load function</li>
14812 * <li>A boolean success indicator</li>
14814 * @param {Object} scope The scope in which to call the callback
14815 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14817 load : function(params, reader, callback, scope, arg){
14818 if(this.fireEvent("beforeload", this, params) !== false){
14820 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14822 var url = this.url;
14823 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14825 url += "&_dc=" + (new Date().getTime());
14827 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14830 cb : "stcCallback"+transId,
14831 scriptId : "stcScript"+transId,
14835 callback : callback,
14841 window[trans.cb] = function(o){
14842 conn.handleResponse(o, trans);
14845 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14847 if(this.autoAbort !== false){
14851 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14853 var script = document.createElement("script");
14854 script.setAttribute("src", url);
14855 script.setAttribute("type", "text/javascript");
14856 script.setAttribute("id", trans.scriptId);
14857 this.head.appendChild(script);
14859 this.trans = trans;
14861 callback.call(scope||this, null, arg, false);
14866 isLoading : function(){
14867 return this.trans ? true : false;
14871 * Abort the current server request.
14873 abort : function(){
14874 if(this.isLoading()){
14875 this.destroyTrans(this.trans);
14880 destroyTrans : function(trans, isLoaded){
14881 this.head.removeChild(document.getElementById(trans.scriptId));
14882 clearTimeout(trans.timeoutId);
14884 window[trans.cb] = undefined;
14886 delete window[trans.cb];
14889 // if hasn't been loaded, wait for load to remove it to prevent script error
14890 window[trans.cb] = function(){
14891 window[trans.cb] = undefined;
14893 delete window[trans.cb];
14900 handleResponse : function(o, trans){
14901 this.trans = false;
14902 this.destroyTrans(trans, true);
14905 result = trans.reader.readRecords(o);
14907 this.fireEvent("loadexception", this, o, trans.arg, e);
14908 trans.callback.call(trans.scope||window, null, trans.arg, false);
14911 this.fireEvent("load", this, o, trans.arg);
14912 trans.callback.call(trans.scope||window, result, trans.arg, true);
14916 handleFailure : function(trans){
14917 this.trans = false;
14918 this.destroyTrans(trans, false);
14919 this.fireEvent("loadexception", this, null, trans.arg);
14920 trans.callback.call(trans.scope||window, null, trans.arg, false);
14924 * Ext JS Library 1.1.1
14925 * Copyright(c) 2006-2007, Ext JS, LLC.
14927 * Originally Released Under LGPL - original licence link has changed is not relivant.
14930 * <script type="text/javascript">
14934 * @class Roo.data.JsonReader
14935 * @extends Roo.data.DataReader
14936 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14937 * based on mappings in a provided Roo.data.Record constructor.
14939 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14940 * in the reply previously.
14945 var RecordDef = Roo.data.Record.create([
14946 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14947 {name: 'occupation'} // This field will use "occupation" as the mapping.
14949 var myReader = new Roo.data.JsonReader({
14950 totalProperty: "results", // The property which contains the total dataset size (optional)
14951 root: "rows", // The property which contains an Array of row objects
14952 id: "id" // The property within each row object that provides an ID for the record (optional)
14956 * This would consume a JSON file like this:
14958 { 'results': 2, 'rows': [
14959 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14960 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14963 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14964 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14965 * paged from the remote server.
14966 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14967 * @cfg {String} root name of the property which contains the Array of row objects.
14968 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14969 * @cfg {Array} fields Array of field definition objects
14971 * Create a new JsonReader
14972 * @param {Object} meta Metadata configuration options
14973 * @param {Object} recordType Either an Array of field definition objects,
14974 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14976 Roo.data.JsonReader = function(meta, recordType){
14979 // set some defaults:
14980 Roo.applyIf(meta, {
14981 totalProperty: 'total',
14982 successProperty : 'success',
14987 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14989 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14991 readerType : 'Json',
14994 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14995 * Used by Store query builder to append _requestMeta to params.
14998 metaFromRemote : false,
15000 * This method is only used by a DataProxy which has retrieved data from a remote server.
15001 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15002 * @return {Object} data A data block which is used by an Roo.data.Store object as
15003 * a cache of Roo.data.Records.
15005 read : function(response){
15006 var json = response.responseText;
15008 var o = /* eval:var:o */ eval("("+json+")");
15010 throw {message: "JsonReader.read: Json object not found"};
15016 this.metaFromRemote = true;
15017 this.meta = o.metaData;
15018 this.recordType = Roo.data.Record.create(o.metaData.fields);
15019 this.onMetaChange(this.meta, this.recordType, o);
15021 return this.readRecords(o);
15024 // private function a store will implement
15025 onMetaChange : function(meta, recordType, o){
15032 simpleAccess: function(obj, subsc) {
15039 getJsonAccessor: function(){
15041 return function(expr) {
15043 return(re.test(expr))
15044 ? new Function("obj", "return obj." + expr)
15049 return Roo.emptyFn;
15054 * Create a data block containing Roo.data.Records from an XML document.
15055 * @param {Object} o An object which contains an Array of row objects in the property specified
15056 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15057 * which contains the total size of the dataset.
15058 * @return {Object} data A data block which is used by an Roo.data.Store object as
15059 * a cache of Roo.data.Records.
15061 readRecords : function(o){
15063 * After any data loads, the raw JSON data is available for further custom processing.
15067 var s = this.meta, Record = this.recordType,
15068 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15070 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15072 if(s.totalProperty) {
15073 this.getTotal = this.getJsonAccessor(s.totalProperty);
15075 if(s.successProperty) {
15076 this.getSuccess = this.getJsonAccessor(s.successProperty);
15078 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15080 var g = this.getJsonAccessor(s.id);
15081 this.getId = function(rec) {
15083 return (r === undefined || r === "") ? null : r;
15086 this.getId = function(){return null;};
15089 for(var jj = 0; jj < fl; jj++){
15091 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15092 this.ef[jj] = this.getJsonAccessor(map);
15096 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15097 if(s.totalProperty){
15098 var vt = parseInt(this.getTotal(o), 10);
15103 if(s.successProperty){
15104 var vs = this.getSuccess(o);
15105 if(vs === false || vs === 'false'){
15110 for(var i = 0; i < c; i++){
15113 var id = this.getId(n);
15114 for(var j = 0; j < fl; j++){
15116 var v = this.ef[j](n);
15118 Roo.log('missing convert for ' + f.name);
15122 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15124 var record = new Record(values, id);
15126 records[i] = record;
15132 totalRecords : totalRecords
15135 // used when loading children.. @see loadDataFromChildren
15136 toLoadData: function(rec)
15138 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15139 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15140 return { data : data, total : data.length };
15145 * Ext JS Library 1.1.1
15146 * Copyright(c) 2006-2007, Ext JS, LLC.
15148 * Originally Released Under LGPL - original licence link has changed is not relivant.
15151 * <script type="text/javascript">
15155 * @class Roo.data.ArrayReader
15156 * @extends Roo.data.DataReader
15157 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15158 * Each element of that Array represents a row of data fields. The
15159 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15160 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15164 var RecordDef = Roo.data.Record.create([
15165 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15166 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15168 var myReader = new Roo.data.ArrayReader({
15169 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15173 * This would consume an Array like this:
15175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15179 * Create a new JsonReader
15180 * @param {Object} meta Metadata configuration options.
15181 * @param {Object|Array} recordType Either an Array of field definition objects
15183 * @cfg {Array} fields Array of field definition objects
15184 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15185 * as specified to {@link Roo.data.Record#create},
15186 * or an {@link Roo.data.Record} object
15189 * created using {@link Roo.data.Record#create}.
15191 Roo.data.ArrayReader = function(meta, recordType)
15193 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15199 * Create a data block containing Roo.data.Records from an XML document.
15200 * @param {Object} o An Array of row objects which represents the dataset.
15201 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15202 * a cache of Roo.data.Records.
15204 readRecords : function(o)
15206 var sid = this.meta ? this.meta.id : null;
15207 var recordType = this.recordType, fields = recordType.prototype.fields;
15210 for(var i = 0; i < root.length; i++){
15213 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15214 for(var j = 0, jlen = fields.length; j < jlen; j++){
15215 var f = fields.items[j];
15216 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15217 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15219 values[f.name] = v;
15221 var record = new recordType(values, id);
15223 records[records.length] = record;
15227 totalRecords : records.length
15230 // used when loading children.. @see loadDataFromChildren
15231 toLoadData: function(rec)
15233 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15234 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15245 * @class Roo.bootstrap.ComboBox
15246 * @extends Roo.bootstrap.TriggerField
15247 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15248 * @cfg {Boolean} append (true|false) default false
15249 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15250 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15251 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15252 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15253 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15254 * @cfg {Boolean} animate default true
15255 * @cfg {Boolean} emptyResultText only for touch device
15256 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15257 * @cfg {String} emptyTitle default ''
15258 * @cfg {Number} width fixed with? experimental
15260 * Create a new ComboBox.
15261 * @param {Object} config Configuration options
15263 Roo.bootstrap.ComboBox = function(config){
15264 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15268 * Fires when the dropdown list is expanded
15269 * @param {Roo.bootstrap.ComboBox} combo This combo box
15274 * Fires when the dropdown list is collapsed
15275 * @param {Roo.bootstrap.ComboBox} combo This combo box
15279 * @event beforeselect
15280 * Fires before a list item is selected. Return false to cancel the selection.
15281 * @param {Roo.bootstrap.ComboBox} combo This combo box
15282 * @param {Roo.data.Record} record The data record returned from the underlying store
15283 * @param {Number} index The index of the selected item in the dropdown list
15285 'beforeselect' : true,
15288 * Fires when a list item is selected
15289 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15291 * @param {Number} index The index of the selected item in the dropdown list
15295 * @event beforequery
15296 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15297 * The event object passed has these properties:
15298 * @param {Roo.bootstrap.ComboBox} combo This combo box
15299 * @param {String} query The query
15300 * @param {Boolean} forceAll true to force "all" query
15301 * @param {Boolean} cancel true to cancel the query
15302 * @param {Object} e The query event object
15304 'beforequery': true,
15307 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15313 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15320 * Fires when the remove value from the combobox array
15321 * @param {Roo.bootstrap.ComboBox} combo This combo box
15325 * @event afterremove
15326 * Fires when the remove value from the combobox array
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 'afterremove' : true,
15331 * @event specialfilter
15332 * Fires when specialfilter
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15335 'specialfilter' : true,
15338 * Fires when tick the element
15339 * @param {Roo.bootstrap.ComboBox} combo This combo box
15343 * @event touchviewdisplay
15344 * Fires when touch view require special display (default is using displayField)
15345 * @param {Roo.bootstrap.ComboBox} combo This combo box
15346 * @param {Object} cfg set html .
15348 'touchviewdisplay' : true
15353 this.tickItems = [];
15355 this.selectedIndex = -1;
15356 if(this.mode == 'local'){
15357 if(config.queryDelay === undefined){
15358 this.queryDelay = 10;
15360 if(config.minChars === undefined){
15366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15369 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15370 * rendering into an Roo.Editor, defaults to false)
15373 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15374 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15377 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15380 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15381 * the dropdown list (defaults to undefined, with no header element)
15385 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15389 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15391 listWidth: undefined,
15393 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15394 * mode = 'remote' or 'text' if mode = 'local')
15396 displayField: undefined,
15399 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15400 * mode = 'remote' or 'value' if mode = 'local').
15401 * Note: use of a valueField requires the user make a selection
15402 * in order for a value to be mapped.
15404 valueField: undefined,
15406 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15411 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15412 * field's data value (defaults to the underlying DOM element's name)
15414 hiddenName: undefined,
15416 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15420 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15422 selectedClass: 'active',
15425 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15429 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15430 * anchor positions (defaults to 'tl-bl')
15432 listAlign: 'tl-bl?',
15434 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15438 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15439 * query specified by the allQuery config option (defaults to 'query')
15441 triggerAction: 'query',
15443 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15444 * (defaults to 4, does not apply if editable = false)
15448 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15449 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15453 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15454 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15458 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15459 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15463 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15464 * when editable = true (defaults to false)
15466 selectOnFocus:false,
15468 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15470 queryParam: 'query',
15472 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15473 * when mode = 'remote' (defaults to 'Loading...')
15475 loadingText: 'Loading...',
15477 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15481 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15485 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15486 * traditional select (defaults to true)
15490 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15494 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15498 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15499 * listWidth has a higher value)
15503 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15504 * allow the user to set arbitrary text into the field (defaults to false)
15506 forceSelection:false,
15508 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15509 * if typeAhead = true (defaults to 250)
15511 typeAheadDelay : 250,
15513 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15514 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15516 valueNotFoundText : undefined,
15518 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15520 blockFocus : false,
15523 * @cfg {Boolean} disableClear Disable showing of clear button.
15525 disableClear : false,
15527 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15529 alwaysQuery : false,
15532 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15537 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15539 invalidClass : "has-warning",
15542 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15544 validClass : "has-success",
15547 * @cfg {Boolean} specialFilter (true|false) special filter default false
15549 specialFilter : false,
15552 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15554 mobileTouchView : true,
15557 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15559 useNativeIOS : false,
15562 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15564 mobile_restrict_height : false,
15566 ios_options : false,
15578 btnPosition : 'right',
15579 triggerList : true,
15580 showToggleBtn : true,
15582 emptyResultText: 'Empty',
15583 triggerText : 'Select',
15587 // element that contains real text value.. (when hidden is used..)
15589 getAutoCreate : function()
15594 * Render classic select for iso
15597 if(Roo.isIOS && this.useNativeIOS){
15598 cfg = this.getAutoCreateNativeIOS();
15606 if(Roo.isTouch && this.mobileTouchView){
15607 cfg = this.getAutoCreateTouchView();
15614 if(!this.tickable){
15615 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15620 * ComboBox with tickable selections
15623 var align = this.labelAlign || this.parentLabelAlign();
15626 cls : 'form-group roo-combobox-tickable' //input-group
15629 var btn_text_select = '';
15630 var btn_text_done = '';
15631 var btn_text_cancel = '';
15633 if (this.btn_text_show) {
15634 btn_text_select = 'Select';
15635 btn_text_done = 'Done';
15636 btn_text_cancel = 'Cancel';
15641 cls : 'tickable-buttons',
15646 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15647 //html : this.triggerText
15648 html: btn_text_select
15654 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15656 html: btn_text_done
15662 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15664 html: btn_text_cancel
15670 buttons.cn.unshift({
15672 cls: 'roo-select2-search-field-input'
15678 Roo.each(buttons.cn, function(c){
15680 c.cls += ' btn-' + _this.size;
15683 if (_this.disabled) {
15690 style : 'display: contents',
15695 cls: 'form-hidden-field'
15699 cls: 'roo-select2-choices',
15703 cls: 'roo-select2-search-field',
15714 cls: 'roo-select2-container input-group roo-select2-container-multi',
15720 // cls: 'typeahead typeahead-long dropdown-menu',
15721 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15726 if(this.hasFeedback && !this.allowBlank){
15730 cls: 'glyphicon form-control-feedback'
15733 combobox.cn.push(feedback);
15740 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15741 tooltip : 'This field is required'
15743 if (Roo.bootstrap.version == 4) {
15746 style : 'display:none'
15749 if (align ==='left' && this.fieldLabel.length) {
15751 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15758 cls : 'control-label col-form-label',
15759 html : this.fieldLabel
15771 var labelCfg = cfg.cn[1];
15772 var contentCfg = cfg.cn[2];
15775 if(this.indicatorpos == 'right'){
15781 cls : 'control-label col-form-label',
15785 html : this.fieldLabel
15801 labelCfg = cfg.cn[0];
15802 contentCfg = cfg.cn[1];
15806 if(this.labelWidth > 12){
15807 labelCfg.style = "width: " + this.labelWidth + 'px';
15809 if(this.width * 1 > 0){
15810 contentCfg.style = "width: " + this.width + 'px';
15812 if(this.labelWidth < 13 && this.labelmd == 0){
15813 this.labelmd = this.labelWidth;
15816 if(this.labellg > 0){
15817 labelCfg.cls += ' col-lg-' + this.labellg;
15818 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15821 if(this.labelmd > 0){
15822 labelCfg.cls += ' col-md-' + this.labelmd;
15823 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15826 if(this.labelsm > 0){
15827 labelCfg.cls += ' col-sm-' + this.labelsm;
15828 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15831 if(this.labelxs > 0){
15832 labelCfg.cls += ' col-xs-' + this.labelxs;
15833 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15837 } else if ( this.fieldLabel.length) {
15838 // Roo.log(" label");
15843 //cls : 'input-group-addon',
15844 html : this.fieldLabel
15849 if(this.indicatorpos == 'right'){
15853 //cls : 'input-group-addon',
15854 html : this.fieldLabel
15864 // Roo.log(" no label && no align");
15871 ['xs','sm','md','lg'].map(function(size){
15872 if (settings[size]) {
15873 cfg.cls += ' col-' + size + '-' + settings[size];
15881 _initEventsCalled : false,
15884 initEvents: function()
15886 if (this._initEventsCalled) { // as we call render... prevent looping...
15889 this._initEventsCalled = true;
15892 throw "can not find store for combo";
15895 this.indicator = this.indicatorEl();
15897 this.store = Roo.factory(this.store, Roo.data);
15898 this.store.parent = this;
15900 // if we are building from html. then this element is so complex, that we can not really
15901 // use the rendered HTML.
15902 // so we have to trash and replace the previous code.
15903 if (Roo.XComponent.build_from_html) {
15904 // remove this element....
15905 var e = this.el.dom, k=0;
15906 while (e ) { e = e.previousSibling; ++k;}
15911 this.rendered = false;
15913 this.render(this.parent().getChildContainer(true), k);
15916 if(Roo.isIOS && this.useNativeIOS){
15917 this.initIOSView();
15925 if(Roo.isTouch && this.mobileTouchView){
15926 this.initTouchView();
15931 this.initTickableEvents();
15935 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 // prevent input submission
15946 this.el.dom.removeAttribute('name');
15947 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15952 // this.el.dom.setAttribute('autocomplete', 'off');
15955 var cls = 'x-combo-list';
15957 //this.list = new Roo.Layer({
15958 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15964 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15965 _this.list.setWidth(lw);
15968 this.list.on('mouseover', this.onViewOver, this);
15969 this.list.on('mousemove', this.onViewMove, this);
15970 this.list.on('scroll', this.onViewScroll, this);
15973 this.list.swallowEvent('mousewheel');
15974 this.assetHeight = 0;
15977 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15978 this.assetHeight += this.header.getHeight();
15981 this.innerList = this.list.createChild({cls:cls+'-inner'});
15982 this.innerList.on('mouseover', this.onViewOver, this);
15983 this.innerList.on('mousemove', this.onViewMove, this);
15984 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15986 if(this.allowBlank && !this.pageSize && !this.disableClear){
15987 this.footer = this.list.createChild({cls:cls+'-ft'});
15988 this.pageTb = new Roo.Toolbar(this.footer);
15992 this.footer = this.list.createChild({cls:cls+'-ft'});
15993 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15994 {pageSize: this.pageSize});
15998 if (this.pageTb && this.allowBlank && !this.disableClear) {
16000 this.pageTb.add(new Roo.Toolbar.Fill(), {
16001 cls: 'x-btn-icon x-btn-clear',
16003 handler: function()
16006 _this.clearValue();
16007 _this.onSelect(false, -1);
16012 this.assetHeight += this.footer.getHeight();
16017 this.tpl = Roo.bootstrap.version == 4 ?
16018 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16019 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16022 this.view = new Roo.View(this.list, this.tpl, {
16023 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16025 //this.view.wrapEl.setDisplayed(false);
16026 this.view.on('click', this.onViewClick, this);
16029 this.store.on('beforeload', this.onBeforeLoad, this);
16030 this.store.on('load', this.onLoad, this);
16031 this.store.on('loadexception', this.onLoadException, this);
16033 if(this.resizable){
16034 this.resizer = new Roo.Resizable(this.list, {
16035 pinned:true, handles:'se'
16037 this.resizer.on('resize', function(r, w, h){
16038 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16039 this.listWidth = w;
16040 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16041 this.restrictHeight();
16043 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16046 if(!this.editable){
16047 this.editable = true;
16048 this.setEditable(false);
16053 if (typeof(this.events.add.listeners) != 'undefined') {
16055 this.addicon = this.wrap.createChild(
16056 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16058 this.addicon.on('click', function(e) {
16059 this.fireEvent('add', this);
16062 if (typeof(this.events.edit.listeners) != 'undefined') {
16064 this.editicon = this.wrap.createChild(
16065 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16066 if (this.addicon) {
16067 this.editicon.setStyle('margin-left', '40px');
16069 this.editicon.on('click', function(e) {
16071 // we fire even if inothing is selected..
16072 this.fireEvent('edit', this, this.lastData );
16078 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16079 "up" : function(e){
16080 this.inKeyMode = true;
16084 "down" : function(e){
16085 if(!this.isExpanded()){
16086 this.onTriggerClick();
16088 this.inKeyMode = true;
16093 "enter" : function(e){
16094 // this.onViewClick();
16098 if(this.fireEvent("specialkey", this, e)){
16099 this.onViewClick(false);
16105 "esc" : function(e){
16109 "tab" : function(e){
16112 if(this.fireEvent("specialkey", this, e)){
16113 this.onViewClick(false);
16121 doRelay : function(foo, bar, hname){
16122 if(hname == 'down' || this.scope.isExpanded()){
16123 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16132 this.queryDelay = Math.max(this.queryDelay || 10,
16133 this.mode == 'local' ? 10 : 250);
16136 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16138 if(this.typeAhead){
16139 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16141 if(this.editable !== false){
16142 this.inputEl().on("keyup", this.onKeyUp, this);
16144 if(this.forceSelection){
16145 this.inputEl().on('blur', this.doForce, this);
16149 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16150 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16154 initTickableEvents: function()
16158 if(this.hiddenName){
16160 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16162 this.hiddenField.dom.value =
16163 this.hiddenValue !== undefined ? this.hiddenValue :
16164 this.value !== undefined ? this.value : '';
16166 // prevent input submission
16167 this.el.dom.removeAttribute('name');
16168 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16173 // this.list = this.el.select('ul.dropdown-menu',true).first();
16175 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16176 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16177 if(this.triggerList){
16178 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16181 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16182 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16184 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16185 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16187 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16188 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16190 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16191 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16192 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16195 this.cancelBtn.hide();
16200 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16201 _this.list.setWidth(lw);
16204 this.list.on('mouseover', this.onViewOver, this);
16205 this.list.on('mousemove', this.onViewMove, this);
16207 this.list.on('scroll', this.onViewScroll, this);
16210 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16211 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16214 this.view = new Roo.View(this.list, this.tpl, {
16219 selectedClass: this.selectedClass
16222 //this.view.wrapEl.setDisplayed(false);
16223 this.view.on('click', this.onViewClick, this);
16227 this.store.on('beforeload', this.onBeforeLoad, this);
16228 this.store.on('load', this.onLoad, this);
16229 this.store.on('loadexception', this.onLoadException, this);
16232 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16233 "up" : function(e){
16234 this.inKeyMode = true;
16238 "down" : function(e){
16239 this.inKeyMode = true;
16243 "enter" : function(e){
16244 if(this.fireEvent("specialkey", this, e)){
16245 this.onViewClick(false);
16251 "esc" : function(e){
16252 this.onTickableFooterButtonClick(e, false, false);
16255 "tab" : function(e){
16256 this.fireEvent("specialkey", this, e);
16258 this.onTickableFooterButtonClick(e, false, false);
16265 doRelay : function(e, fn, key){
16266 if(this.scope.isExpanded()){
16267 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16276 this.queryDelay = Math.max(this.queryDelay || 10,
16277 this.mode == 'local' ? 10 : 250);
16280 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16282 if(this.typeAhead){
16283 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16286 if(this.editable !== false){
16287 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16290 this.indicator = this.indicatorEl();
16292 if(this.indicator){
16293 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16294 this.indicator.hide();
16299 onDestroy : function(){
16301 this.view.setStore(null);
16302 this.view.el.removeAllListeners();
16303 this.view.el.remove();
16304 this.view.purgeListeners();
16307 this.list.dom.innerHTML = '';
16311 this.store.un('beforeload', this.onBeforeLoad, this);
16312 this.store.un('load', this.onLoad, this);
16313 this.store.un('loadexception', this.onLoadException, this);
16315 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16319 fireKey : function(e){
16320 if(e.isNavKeyPress() && !this.list.isVisible()){
16321 this.fireEvent("specialkey", this, e);
16326 onResize: function(w, h)
16330 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16332 // if(typeof w != 'number'){
16333 // // we do not handle it!?!?
16336 // var tw = this.trigger.getWidth();
16337 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16338 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16340 // this.inputEl().setWidth( this.adjustWidth('input', x));
16342 // //this.trigger.setStyle('left', x+'px');
16344 // if(this.list && this.listWidth === undefined){
16345 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16346 // this.list.setWidth(lw);
16347 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16355 * Allow or prevent the user from directly editing the field text. If false is passed,
16356 * the user will only be able to select from the items defined in the dropdown list. This method
16357 * is the runtime equivalent of setting the 'editable' config option at config time.
16358 * @param {Boolean} value True to allow the user to directly edit the field text
16360 setEditable : function(value){
16361 if(value == this.editable){
16364 this.editable = value;
16366 this.inputEl().dom.setAttribute('readOnly', true);
16367 this.inputEl().on('mousedown', this.onTriggerClick, this);
16368 this.inputEl().addClass('x-combo-noedit');
16370 this.inputEl().dom.setAttribute('readOnly', false);
16371 this.inputEl().un('mousedown', this.onTriggerClick, this);
16372 this.inputEl().removeClass('x-combo-noedit');
16378 onBeforeLoad : function(combo,opts){
16379 if(!this.hasFocus){
16383 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16385 this.restrictHeight();
16386 this.selectedIndex = -1;
16390 onLoad : function(){
16392 this.hasQuery = false;
16394 if(!this.hasFocus){
16398 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16399 this.loading.hide();
16402 if(this.store.getCount() > 0){
16405 this.restrictHeight();
16406 if(this.lastQuery == this.allQuery){
16407 if(this.editable && !this.tickable){
16408 this.inputEl().dom.select();
16412 !this.selectByValue(this.value, true) &&
16415 !this.store.lastOptions ||
16416 typeof(this.store.lastOptions.add) == 'undefined' ||
16417 this.store.lastOptions.add != true
16420 this.select(0, true);
16423 if(this.autoFocus){
16426 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16427 this.taTask.delay(this.typeAheadDelay);
16431 this.onEmptyResults();
16437 onLoadException : function()
16439 this.hasQuery = false;
16441 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442 this.loading.hide();
16445 if(this.tickable && this.editable){
16450 // only causes errors at present
16451 //Roo.log(this.store.reader.jsonData);
16452 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16454 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16460 onTypeAhead : function(){
16461 if(this.store.getCount() > 0){
16462 var r = this.store.getAt(0);
16463 var newValue = r.data[this.displayField];
16464 var len = newValue.length;
16465 var selStart = this.getRawValue().length;
16467 if(selStart != len){
16468 this.setRawValue(newValue);
16469 this.selectText(selStart, newValue.length);
16475 onSelect : function(record, index){
16477 if(this.fireEvent('beforeselect', this, record, index) !== false){
16479 this.setFromData(index > -1 ? record.data : false);
16482 this.fireEvent('select', this, record, index);
16487 * Returns the currently selected field value or empty string if no value is set.
16488 * @return {String} value The selected value
16490 getValue : function()
16492 if(Roo.isIOS && this.useNativeIOS){
16493 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16497 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16500 if(this.valueField){
16501 return typeof this.value != 'undefined' ? this.value : '';
16503 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16507 getRawValue : function()
16509 if(Roo.isIOS && this.useNativeIOS){
16510 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16513 var v = this.inputEl().getValue();
16519 * Clears any text/value currently set in the field
16521 clearValue : function(){
16523 if(this.hiddenField){
16524 this.hiddenField.dom.value = '';
16527 this.setRawValue('');
16528 this.lastSelectionText = '';
16529 this.lastData = false;
16531 var close = this.closeTriggerEl();
16542 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16543 * will be displayed in the field. If the value does not match the data value of an existing item,
16544 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16545 * Otherwise the field will be blank (although the value will still be set).
16546 * @param {String} value The value to match
16548 setValue : function(v)
16550 if(Roo.isIOS && this.useNativeIOS){
16551 this.setIOSValue(v);
16561 if(this.valueField){
16562 var r = this.findRecord(this.valueField, v);
16564 text = r.data[this.displayField];
16565 }else if(this.valueNotFoundText !== undefined){
16566 text = this.valueNotFoundText;
16569 this.lastSelectionText = text;
16570 if(this.hiddenField){
16571 this.hiddenField.dom.value = v;
16573 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16576 var close = this.closeTriggerEl();
16579 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16585 * @property {Object} the last set data for the element
16590 * Sets the value of the field based on a object which is related to the record format for the store.
16591 * @param {Object} value the value to set as. or false on reset?
16593 setFromData : function(o){
16600 var dv = ''; // display value
16601 var vv = ''; // value value..
16603 if (this.displayField) {
16604 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16606 // this is an error condition!!!
16607 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16610 if(this.valueField){
16611 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16614 var close = this.closeTriggerEl();
16617 if(dv.length || vv * 1 > 0){
16619 this.blockFocus=true;
16625 if(this.hiddenField){
16626 this.hiddenField.dom.value = vv;
16628 this.lastSelectionText = dv;
16629 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16633 // no hidden field.. - we store the value in 'value', but still display
16634 // display field!!!!
16635 this.lastSelectionText = dv;
16636 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16643 reset : function(){
16644 // overridden so that last data is reset..
16651 this.setValue(this.originalValue);
16652 //this.clearInvalid();
16653 this.lastData = false;
16655 this.view.clearSelections();
16661 findRecord : function(prop, value){
16663 if(this.store.getCount() > 0){
16664 this.store.each(function(r){
16665 if(r.data[prop] == value){
16675 getName: function()
16677 // returns hidden if it's set..
16678 if (!this.rendered) {return ''};
16679 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16683 onViewMove : function(e, t){
16684 this.inKeyMode = false;
16688 onViewOver : function(e, t){
16689 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16692 var item = this.view.findItemFromChild(t);
16695 var index = this.view.indexOf(item);
16696 this.select(index, false);
16701 onViewClick : function(view, doFocus, el, e)
16703 var index = this.view.getSelectedIndexes()[0];
16705 var r = this.store.getAt(index);
16709 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16716 Roo.each(this.tickItems, function(v,k){
16718 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16720 _this.tickItems.splice(k, 1);
16722 if(typeof(e) == 'undefined' && view == false){
16723 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16735 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16736 this.tickItems.push(r.data);
16739 if(typeof(e) == 'undefined' && view == false){
16740 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16747 this.onSelect(r, index);
16749 if(doFocus !== false && !this.blockFocus){
16750 this.inputEl().focus();
16755 restrictHeight : function(){
16756 //this.innerList.dom.style.height = '';
16757 //var inner = this.innerList.dom;
16758 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16759 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16760 //this.list.beginUpdate();
16761 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16762 this.list.alignTo(this.inputEl(), this.listAlign);
16763 this.list.alignTo(this.inputEl(), this.listAlign);
16764 //this.list.endUpdate();
16768 onEmptyResults : function(){
16770 if(this.tickable && this.editable){
16771 this.hasFocus = false;
16772 this.restrictHeight();
16780 * Returns true if the dropdown list is expanded, else false.
16782 isExpanded : function(){
16783 return this.list.isVisible();
16787 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16788 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16789 * @param {String} value The data value of the item to select
16790 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16791 * selected item if it is not currently in view (defaults to true)
16792 * @return {Boolean} True if the value matched an item in the list, else false
16794 selectByValue : function(v, scrollIntoView){
16795 if(v !== undefined && v !== null){
16796 var r = this.findRecord(this.valueField || this.displayField, v);
16798 this.select(this.store.indexOf(r), scrollIntoView);
16806 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16807 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16808 * @param {Number} index The zero-based index of the list item to select
16809 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16810 * selected item if it is not currently in view (defaults to true)
16812 select : function(index, scrollIntoView){
16813 this.selectedIndex = index;
16814 this.view.select(index);
16815 if(scrollIntoView !== false){
16816 var el = this.view.getNode(index);
16818 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16821 this.list.scrollChildIntoView(el, false);
16827 selectNext : function(){
16828 var ct = this.store.getCount();
16830 if(this.selectedIndex == -1){
16832 }else if(this.selectedIndex < ct-1){
16833 this.select(this.selectedIndex+1);
16839 selectPrev : function(){
16840 var ct = this.store.getCount();
16842 if(this.selectedIndex == -1){
16844 }else if(this.selectedIndex != 0){
16845 this.select(this.selectedIndex-1);
16851 onKeyUp : function(e){
16852 if(this.editable !== false && !e.isSpecialKey()){
16853 this.lastKey = e.getKey();
16854 this.dqTask.delay(this.queryDelay);
16859 validateBlur : function(){
16860 return !this.list || !this.list.isVisible();
16864 initQuery : function(){
16866 var v = this.getRawValue();
16868 if(this.tickable && this.editable){
16869 v = this.tickableInputEl().getValue();
16876 doForce : function(){
16877 if(this.inputEl().dom.value.length > 0){
16878 this.inputEl().dom.value =
16879 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16885 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16886 * query allowing the query action to be canceled if needed.
16887 * @param {String} query The SQL query to execute
16888 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16889 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16890 * saved in the current store (defaults to false)
16892 doQuery : function(q, forceAll){
16894 if(q === undefined || q === null){
16899 forceAll: forceAll,
16903 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16908 forceAll = qe.forceAll;
16909 if(forceAll === true || (q.length >= this.minChars)){
16911 this.hasQuery = true;
16913 if(this.lastQuery != q || this.alwaysQuery){
16914 this.lastQuery = q;
16915 if(this.mode == 'local'){
16916 this.selectedIndex = -1;
16918 this.store.clearFilter();
16921 if(this.specialFilter){
16922 this.fireEvent('specialfilter', this);
16927 this.store.filter(this.displayField, q);
16930 this.store.fireEvent("datachanged", this.store);
16937 this.store.baseParams[this.queryParam] = q;
16939 var options = {params : this.getParams(q)};
16942 options.add = true;
16943 options.params.start = this.page * this.pageSize;
16946 this.store.load(options);
16949 * this code will make the page width larger, at the beginning, the list not align correctly,
16950 * we should expand the list on onLoad
16951 * so command out it
16956 this.selectedIndex = -1;
16961 this.loadNext = false;
16965 getParams : function(q){
16967 //p[this.queryParam] = q;
16971 p.limit = this.pageSize;
16977 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16979 collapse : function(){
16980 if(!this.isExpanded()){
16986 this.hasFocus = false;
16990 this.cancelBtn.hide();
16991 this.trigger.show();
16994 this.tickableInputEl().dom.value = '';
16995 this.tickableInputEl().blur();
17000 Roo.get(document).un('mousedown', this.collapseIf, this);
17001 Roo.get(document).un('mousewheel', this.collapseIf, this);
17002 if (!this.editable) {
17003 Roo.get(document).un('keydown', this.listKeyPress, this);
17005 this.fireEvent('collapse', this);
17011 collapseIf : function(e){
17012 var in_combo = e.within(this.el);
17013 var in_list = e.within(this.list);
17014 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17016 if (in_combo || in_list || is_list) {
17017 //e.stopPropagation();
17022 this.onTickableFooterButtonClick(e, false, false);
17030 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17032 expand : function(){
17034 if(this.isExpanded() || !this.hasFocus){
17038 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17039 this.list.setWidth(lw);
17045 this.restrictHeight();
17049 this.tickItems = Roo.apply([], this.item);
17052 this.cancelBtn.show();
17053 this.trigger.hide();
17056 this.tickableInputEl().focus();
17061 Roo.get(document).on('mousedown', this.collapseIf, this);
17062 Roo.get(document).on('mousewheel', this.collapseIf, this);
17063 if (!this.editable) {
17064 Roo.get(document).on('keydown', this.listKeyPress, this);
17067 this.fireEvent('expand', this);
17071 // Implements the default empty TriggerField.onTriggerClick function
17072 onTriggerClick : function(e)
17074 Roo.log('trigger click');
17076 if(this.disabled || !this.triggerList){
17081 this.loadNext = false;
17083 if(this.isExpanded()){
17085 if (!this.blockFocus) {
17086 this.inputEl().focus();
17090 this.hasFocus = true;
17091 if(this.triggerAction == 'all') {
17092 this.doQuery(this.allQuery, true);
17094 this.doQuery(this.getRawValue());
17096 if (!this.blockFocus) {
17097 this.inputEl().focus();
17102 onTickableTriggerClick : function(e)
17109 this.loadNext = false;
17110 this.hasFocus = true;
17112 if(this.triggerAction == 'all') {
17113 this.doQuery(this.allQuery, true);
17115 this.doQuery(this.getRawValue());
17119 onSearchFieldClick : function(e)
17121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17122 this.onTickableFooterButtonClick(e, false, false);
17126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17131 this.loadNext = false;
17132 this.hasFocus = true;
17134 if(this.triggerAction == 'all') {
17135 this.doQuery(this.allQuery, true);
17137 this.doQuery(this.getRawValue());
17141 listKeyPress : function(e)
17143 //Roo.log('listkeypress');
17144 // scroll to first matching element based on key pres..
17145 if (e.isSpecialKey()) {
17148 var k = String.fromCharCode(e.getKey()).toUpperCase();
17151 var csel = this.view.getSelectedNodes();
17152 var cselitem = false;
17154 var ix = this.view.indexOf(csel[0]);
17155 cselitem = this.store.getAt(ix);
17156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17162 this.store.each(function(v) {
17164 // start at existing selection.
17165 if (cselitem.id == v.id) {
17171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17172 match = this.store.indexOf(v);
17178 if (match === false) {
17179 return true; // no more action?
17182 this.view.select(match);
17183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17184 sn.scrollIntoView(sn.dom.parentNode, false);
17187 onViewScroll : function(e, t){
17189 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){
17193 this.hasQuery = true;
17195 this.loading = this.list.select('.loading', true).first();
17197 if(this.loading === null){
17198 this.list.createChild({
17200 cls: 'loading roo-select2-more-results roo-select2-active',
17201 html: 'Loading more results...'
17204 this.loading = this.list.select('.loading', true).first();
17206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17208 this.loading.hide();
17211 this.loading.show();
17216 this.loadNext = true;
17218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17223 addItem : function(o)
17225 var dv = ''; // display value
17227 if (this.displayField) {
17228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17230 // this is an error condition!!!
17231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17238 var choice = this.choices.createChild({
17240 cls: 'roo-select2-search-choice',
17249 cls: 'roo-select2-search-choice-close fa fa-times',
17254 }, this.searchField);
17256 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17266 this.inputEl().dom.value = '';
17271 onRemoveItem : function(e, _self, o)
17273 e.preventDefault();
17275 this.lastItem = Roo.apply([], this.item);
17277 var index = this.item.indexOf(o.data) * 1;
17280 Roo.log('not this item?!');
17284 this.item.splice(index, 1);
17289 this.fireEvent('remove', this, e);
17295 syncValue : function()
17297 if(!this.item.length){
17304 Roo.each(this.item, function(i){
17305 if(_this.valueField){
17306 value.push(i[_this.valueField]);
17313 this.value = value.join(',');
17315 if(this.hiddenField){
17316 this.hiddenField.dom.value = this.value;
17319 this.store.fireEvent("datachanged", this.store);
17324 clearItem : function()
17326 if(!this.multiple){
17332 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17340 if(this.tickable && !Roo.isTouch){
17341 this.view.refresh();
17345 inputEl: function ()
17347 if(Roo.isIOS && this.useNativeIOS){
17348 return this.el.select('select.roo-ios-select', true).first();
17351 if(Roo.isTouch && this.mobileTouchView){
17352 return this.el.select('input.form-control',true).first();
17356 return this.searchField;
17359 return this.el.select('input.form-control',true).first();
17362 onTickableFooterButtonClick : function(e, btn, el)
17364 e.preventDefault();
17366 this.lastItem = Roo.apply([], this.item);
17368 if(btn && btn.name == 'cancel'){
17369 this.tickItems = Roo.apply([], this.item);
17378 Roo.each(this.tickItems, function(o){
17386 validate : function()
17388 if(this.getVisibilityEl().hasClass('hidden')){
17392 var v = this.getRawValue();
17395 v = this.getValue();
17398 if(this.disabled || this.allowBlank || v.length){
17403 this.markInvalid();
17407 tickableInputEl : function()
17409 if(!this.tickable || !this.editable){
17410 return this.inputEl();
17413 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17417 getAutoCreateTouchView : function()
17422 cls: 'form-group' //input-group
17428 type : this.inputType,
17429 cls : 'form-control x-combo-noedit',
17430 autocomplete: 'new-password',
17431 placeholder : this.placeholder || '',
17436 input.name = this.name;
17440 input.cls += ' input-' + this.size;
17443 if (this.disabled) {
17444 input.disabled = true;
17448 cls : 'roo-combobox-wrap',
17455 inputblock.cls += ' input-group';
17457 inputblock.cn.unshift({
17459 cls : 'input-group-addon input-group-prepend input-group-text',
17464 if(this.removable && !this.multiple){
17465 inputblock.cls += ' roo-removable';
17467 inputblock.cn.push({
17470 cls : 'roo-combo-removable-btn close'
17474 if(this.hasFeedback && !this.allowBlank){
17476 inputblock.cls += ' has-feedback';
17478 inputblock.cn.push({
17480 cls: 'glyphicon form-control-feedback'
17487 inputblock.cls += (this.before) ? '' : ' input-group';
17489 inputblock.cn.push({
17491 cls : 'input-group-addon input-group-append input-group-text',
17497 var ibwrap = inputblock;
17502 cls: 'roo-select2-choices',
17506 cls: 'roo-select2-search-field',
17519 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17524 cls: 'form-hidden-field'
17530 if(!this.multiple && this.showToggleBtn){
17536 if (this.caret != false) {
17539 cls: 'fa fa-' + this.caret
17546 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17548 Roo.bootstrap.version == 3 ? caret : '',
17551 cls: 'combobox-clear',
17565 combobox.cls += ' roo-select2-container-multi';
17568 var align = this.labelAlign || this.parentLabelAlign();
17570 if (align ==='left' && this.fieldLabel.length) {
17575 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17576 tooltip : 'This field is required'
17580 cls : 'control-label col-form-label',
17581 html : this.fieldLabel
17585 cls : 'roo-combobox-wrap ',
17592 var labelCfg = cfg.cn[1];
17593 var contentCfg = cfg.cn[2];
17596 if(this.indicatorpos == 'right'){
17601 cls : 'control-label col-form-label',
17605 html : this.fieldLabel
17609 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17610 tooltip : 'This field is required'
17615 cls : "roo-combobox-wrap ",
17623 labelCfg = cfg.cn[0];
17624 contentCfg = cfg.cn[1];
17629 if(this.labelWidth > 12){
17630 labelCfg.style = "width: " + this.labelWidth + 'px';
17633 if(this.labelWidth < 13 && this.labelmd == 0){
17634 this.labelmd = this.labelWidth;
17637 if(this.labellg > 0){
17638 labelCfg.cls += ' col-lg-' + this.labellg;
17639 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17642 if(this.labelmd > 0){
17643 labelCfg.cls += ' col-md-' + this.labelmd;
17644 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17647 if(this.labelsm > 0){
17648 labelCfg.cls += ' col-sm-' + this.labelsm;
17649 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17652 if(this.labelxs > 0){
17653 labelCfg.cls += ' col-xs-' + this.labelxs;
17654 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17658 } else if ( this.fieldLabel.length) {
17662 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17663 tooltip : 'This field is required'
17667 cls : 'control-label',
17668 html : this.fieldLabel
17679 if(this.indicatorpos == 'right'){
17683 cls : 'control-label',
17684 html : this.fieldLabel,
17688 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17689 tooltip : 'This field is required'
17706 var settings = this;
17708 ['xs','sm','md','lg'].map(function(size){
17709 if (settings[size]) {
17710 cfg.cls += ' col-' + size + '-' + settings[size];
17717 initTouchView : function()
17719 this.renderTouchView();
17721 this.touchViewEl.on('scroll', function(){
17722 this.el.dom.scrollTop = 0;
17725 this.originalValue = this.getValue();
17727 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17729 this.inputEl().on("click", this.showTouchView, this);
17730 if (this.triggerEl) {
17731 this.triggerEl.on("click", this.showTouchView, this);
17735 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17736 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17738 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17740 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17741 this.store.on('load', this.onTouchViewLoad, this);
17742 this.store.on('loadexception', this.onTouchViewLoadException, this);
17744 if(this.hiddenName){
17746 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748 this.hiddenField.dom.value =
17749 this.hiddenValue !== undefined ? this.hiddenValue :
17750 this.value !== undefined ? this.value : '';
17752 this.el.dom.removeAttribute('name');
17753 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17757 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17761 if(this.removable && !this.multiple){
17762 var close = this.closeTriggerEl();
17764 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17765 close.on('click', this.removeBtnClick, this, close);
17769 * fix the bug in Safari iOS8
17771 this.inputEl().on("focus", function(e){
17772 document.activeElement.blur();
17775 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17782 renderTouchView : function()
17784 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17785 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17787 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17788 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17790 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17791 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17792 this.touchViewBodyEl.setStyle('overflow', 'auto');
17794 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17795 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17797 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17798 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17802 showTouchView : function()
17808 this.touchViewHeaderEl.hide();
17810 if(this.modalTitle.length){
17811 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17812 this.touchViewHeaderEl.show();
17815 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17816 this.touchViewEl.show();
17818 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17820 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17821 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17823 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17825 if(this.modalTitle.length){
17826 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17829 this.touchViewBodyEl.setHeight(bodyHeight);
17833 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17835 this.touchViewEl.addClass(['in','show']);
17838 if(this._touchViewMask){
17839 Roo.get(document.body).addClass("x-body-masked");
17840 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17841 this._touchViewMask.setStyle('z-index', 10000);
17842 this._touchViewMask.addClass('show');
17845 this.doTouchViewQuery();
17849 hideTouchView : function()
17851 this.touchViewEl.removeClass(['in','show']);
17855 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17857 this.touchViewEl.setStyle('display', 'none');
17860 if(this._touchViewMask){
17861 this._touchViewMask.removeClass('show');
17862 Roo.get(document.body).removeClass("x-body-masked");
17866 setTouchViewValue : function()
17873 Roo.each(this.tickItems, function(o){
17878 this.hideTouchView();
17881 doTouchViewQuery : function()
17890 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17894 if(!this.alwaysQuery || this.mode == 'local'){
17895 this.onTouchViewLoad();
17902 onTouchViewBeforeLoad : function(combo,opts)
17908 onTouchViewLoad : function()
17910 if(this.store.getCount() < 1){
17911 this.onTouchViewEmptyResults();
17915 this.clearTouchView();
17917 var rawValue = this.getRawValue();
17919 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17921 this.tickItems = [];
17923 this.store.data.each(function(d, rowIndex){
17924 var row = this.touchViewListGroup.createChild(template);
17926 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17927 row.addClass(d.data.cls);
17930 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17933 html : d.data[this.displayField]
17936 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17937 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17940 row.removeClass('selected');
17941 if(!this.multiple && this.valueField &&
17942 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17945 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17946 row.addClass('selected');
17949 if(this.multiple && this.valueField &&
17950 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955 this.tickItems.push(d.data);
17958 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17962 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17964 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17966 if(this.modalTitle.length){
17967 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17970 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17972 if(this.mobile_restrict_height && listHeight < bodyHeight){
17973 this.touchViewBodyEl.setHeight(listHeight);
17978 if(firstChecked && listHeight > bodyHeight){
17979 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17984 onTouchViewLoadException : function()
17986 this.hideTouchView();
17989 onTouchViewEmptyResults : function()
17991 this.clearTouchView();
17993 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17995 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17999 clearTouchView : function()
18001 this.touchViewListGroup.dom.innerHTML = '';
18004 onTouchViewClick : function(e, el, o)
18006 e.preventDefault();
18009 var rowIndex = o.rowIndex;
18011 var r = this.store.getAt(rowIndex);
18013 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18015 if(!this.multiple){
18016 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18017 c.dom.removeAttribute('checked');
18020 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18022 this.setFromData(r.data);
18024 var close = this.closeTriggerEl();
18030 this.hideTouchView();
18032 this.fireEvent('select', this, r, rowIndex);
18037 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18038 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18039 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18043 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044 this.addItem(r.data);
18045 this.tickItems.push(r.data);
18049 getAutoCreateNativeIOS : function()
18052 cls: 'form-group' //input-group,
18057 cls : 'roo-ios-select'
18061 combobox.name = this.name;
18064 if (this.disabled) {
18065 combobox.disabled = true;
18068 var settings = this;
18070 ['xs','sm','md','lg'].map(function(size){
18071 if (settings[size]) {
18072 cfg.cls += ' col-' + size + '-' + settings[size];
18082 initIOSView : function()
18084 this.store.on('load', this.onIOSViewLoad, this);
18089 onIOSViewLoad : function()
18091 if(this.store.getCount() < 1){
18095 this.clearIOSView();
18097 if(this.allowBlank) {
18099 var default_text = '-- SELECT --';
18101 if(this.placeholder.length){
18102 default_text = this.placeholder;
18105 if(this.emptyTitle.length){
18106 default_text += ' - ' + this.emptyTitle + ' -';
18109 var opt = this.inputEl().createChild({
18112 html : default_text
18116 o[this.valueField] = 0;
18117 o[this.displayField] = default_text;
18119 this.ios_options.push({
18126 this.store.data.each(function(d, rowIndex){
18130 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18131 html = d.data[this.displayField];
18136 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18137 value = d.data[this.valueField];
18146 if(this.value == d.data[this.valueField]){
18147 option['selected'] = true;
18150 var opt = this.inputEl().createChild(option);
18152 this.ios_options.push({
18159 this.inputEl().on('change', function(){
18160 this.fireEvent('select', this);
18165 clearIOSView: function()
18167 this.inputEl().dom.innerHTML = '';
18169 this.ios_options = [];
18172 setIOSValue: function(v)
18176 if(!this.ios_options){
18180 Roo.each(this.ios_options, function(opts){
18182 opts.el.dom.removeAttribute('selected');
18184 if(opts.data[this.valueField] != v){
18188 opts.el.dom.setAttribute('selected', true);
18194 * @cfg {Boolean} grow
18198 * @cfg {Number} growMin
18202 * @cfg {Number} growMax
18211 Roo.apply(Roo.bootstrap.ComboBox, {
18215 cls: 'modal-header',
18237 cls: 'list-group-item',
18241 cls: 'roo-combobox-list-group-item-value'
18245 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18259 listItemCheckbox : {
18261 cls: 'list-group-item',
18265 cls: 'roo-combobox-list-group-item-value'
18269 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18285 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18290 cls: 'modal-footer',
18298 cls: 'col-xs-6 text-left',
18301 cls: 'btn btn-danger roo-touch-view-cancel',
18307 cls: 'col-xs-6 text-right',
18310 cls: 'btn btn-success roo-touch-view-ok',
18321 Roo.apply(Roo.bootstrap.ComboBox, {
18323 touchViewTemplate : {
18325 cls: 'modal fade roo-combobox-touch-view',
18329 cls: 'modal-dialog',
18330 style : 'position:fixed', // we have to fix position....
18334 cls: 'modal-content',
18336 Roo.bootstrap.ComboBox.header,
18337 Roo.bootstrap.ComboBox.body,
18338 Roo.bootstrap.ComboBox.footer
18347 * Ext JS Library 1.1.1
18348 * Copyright(c) 2006-2007, Ext JS, LLC.
18350 * Originally Released Under LGPL - original licence link has changed is not relivant.
18353 * <script type="text/javascript">
18358 * @extends Roo.util.Observable
18359 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18360 * This class also supports single and multi selection modes. <br>
18361 * Create a data model bound view:
18363 var store = new Roo.data.Store(...);
18365 var view = new Roo.View({
18367 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18369 singleSelect: true,
18370 selectedClass: "ydataview-selected",
18374 // listen for node click?
18375 view.on("click", function(vw, index, node, e){
18376 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18380 dataModel.load("foobar.xml");
18382 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18384 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18385 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18387 * Note: old style constructor is still suported (container, template, config)
18390 * Create a new View
18391 * @param {Object} config The config object
18394 Roo.View = function(config, depreciated_tpl, depreciated_config){
18396 this.parent = false;
18398 if (typeof(depreciated_tpl) == 'undefined') {
18399 // new way.. - universal constructor.
18400 Roo.apply(this, config);
18401 this.el = Roo.get(this.el);
18404 this.el = Roo.get(config);
18405 this.tpl = depreciated_tpl;
18406 Roo.apply(this, depreciated_config);
18408 this.wrapEl = this.el.wrap().wrap();
18409 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18412 if(typeof(this.tpl) == "string"){
18413 this.tpl = new Roo.Template(this.tpl);
18415 // support xtype ctors..
18416 this.tpl = new Roo.factory(this.tpl, Roo);
18420 this.tpl.compile();
18425 * @event beforeclick
18426 * Fires before a click is processed. Returns false to cancel the default action.
18427 * @param {Roo.View} this
18428 * @param {Number} index The index of the target node
18429 * @param {HTMLElement} node The target node
18430 * @param {Roo.EventObject} e The raw event object
18432 "beforeclick" : true,
18435 * Fires when a template node is clicked.
18436 * @param {Roo.View} this
18437 * @param {Number} index The index of the target node
18438 * @param {HTMLElement} node The target node
18439 * @param {Roo.EventObject} e The raw event object
18444 * Fires when a template node is double clicked.
18445 * @param {Roo.View} this
18446 * @param {Number} index The index of the target node
18447 * @param {HTMLElement} node The target node
18448 * @param {Roo.EventObject} e The raw event object
18452 * @event contextmenu
18453 * Fires when a template node is right clicked.
18454 * @param {Roo.View} this
18455 * @param {Number} index The index of the target node
18456 * @param {HTMLElement} node The target node
18457 * @param {Roo.EventObject} e The raw event object
18459 "contextmenu" : true,
18461 * @event selectionchange
18462 * Fires when the selected nodes change.
18463 * @param {Roo.View} this
18464 * @param {Array} selections Array of the selected nodes
18466 "selectionchange" : true,
18469 * @event beforeselect
18470 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18471 * @param {Roo.View} this
18472 * @param {HTMLElement} node The node to be selected
18473 * @param {Array} selections Array of currently selected nodes
18475 "beforeselect" : true,
18477 * @event preparedata
18478 * Fires on every row to render, to allow you to change the data.
18479 * @param {Roo.View} this
18480 * @param {Object} data to be rendered (change this)
18482 "preparedata" : true
18490 "click": this.onClick,
18491 "dblclick": this.onDblClick,
18492 "contextmenu": this.onContextMenu,
18496 this.selections = [];
18498 this.cmp = new Roo.CompositeElementLite([]);
18500 this.store = Roo.factory(this.store, Roo.data);
18501 this.setStore(this.store, true);
18504 if ( this.footer && this.footer.xtype) {
18506 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18508 this.footer.dataSource = this.store;
18509 this.footer.container = fctr;
18510 this.footer = Roo.factory(this.footer, Roo);
18511 fctr.insertFirst(this.el);
18513 // this is a bit insane - as the paging toolbar seems to detach the el..
18514 // dom.parentNode.parentNode.parentNode
18515 // they get detached?
18519 Roo.View.superclass.constructor.call(this);
18524 Roo.extend(Roo.View, Roo.util.Observable, {
18527 * @cfg {Roo.data.Store} store Data store to load data from.
18532 * @cfg {String|Roo.Element} el The container element.
18537 * @cfg {String|Roo.Template} tpl The template used by this View
18541 * @cfg {String} dataName the named area of the template to use as the data area
18542 * Works with domtemplates roo-name="name"
18546 * @cfg {String} selectedClass The css class to add to selected nodes
18548 selectedClass : "x-view-selected",
18550 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18555 * @cfg {String} text to display on mask (default Loading)
18559 * @cfg {Boolean} multiSelect Allow multiple selection
18561 multiSelect : false,
18563 * @cfg {Boolean} singleSelect Allow single selection
18565 singleSelect: false,
18568 * @cfg {Boolean} toggleSelect - selecting
18570 toggleSelect : false,
18573 * @cfg {Boolean} tickable - selecting
18578 * Returns the element this view is bound to.
18579 * @return {Roo.Element}
18581 getEl : function(){
18582 return this.wrapEl;
18588 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18590 refresh : function(){
18591 //Roo.log('refresh');
18594 // if we are using something like 'domtemplate', then
18595 // the what gets used is:
18596 // t.applySubtemplate(NAME, data, wrapping data..)
18597 // the outer template then get' applied with
18598 // the store 'extra data'
18599 // and the body get's added to the
18600 // roo-name="data" node?
18601 // <span class='roo-tpl-{name}'></span> ?????
18605 this.clearSelections();
18606 this.el.update("");
18608 var records = this.store.getRange();
18609 if(records.length < 1) {
18611 // is this valid?? = should it render a template??
18613 this.el.update(this.emptyText);
18617 if (this.dataName) {
18618 this.el.update(t.apply(this.store.meta)); //????
18619 el = this.el.child('.roo-tpl-' + this.dataName);
18622 for(var i = 0, len = records.length; i < len; i++){
18623 var data = this.prepareData(records[i].data, i, records[i]);
18624 this.fireEvent("preparedata", this, data, i, records[i]);
18626 var d = Roo.apply({}, data);
18629 Roo.apply(d, {'roo-id' : Roo.id()});
18633 Roo.each(this.parent.item, function(item){
18634 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18637 Roo.apply(d, {'roo-data-checked' : 'checked'});
18641 html[html.length] = Roo.util.Format.trim(
18643 t.applySubtemplate(this.dataName, d, this.store.meta) :
18650 el.update(html.join(""));
18651 this.nodes = el.dom.childNodes;
18652 this.updateIndexes(0);
18657 * Function to override to reformat the data that is sent to
18658 * the template for each node.
18659 * DEPRICATED - use the preparedata event handler.
18660 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18661 * a JSON object for an UpdateManager bound view).
18663 prepareData : function(data, index, record)
18665 this.fireEvent("preparedata", this, data, index, record);
18669 onUpdate : function(ds, record){
18670 // Roo.log('on update');
18671 this.clearSelections();
18672 var index = this.store.indexOf(record);
18673 var n = this.nodes[index];
18674 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18675 n.parentNode.removeChild(n);
18676 this.updateIndexes(index, index);
18682 onAdd : function(ds, records, index)
18684 //Roo.log(['on Add', ds, records, index] );
18685 this.clearSelections();
18686 if(this.nodes.length == 0){
18690 var n = this.nodes[index];
18691 for(var i = 0, len = records.length; i < len; i++){
18692 var d = this.prepareData(records[i].data, i, records[i]);
18694 this.tpl.insertBefore(n, d);
18697 this.tpl.append(this.el, d);
18700 this.updateIndexes(index);
18703 onRemove : function(ds, record, index){
18704 // Roo.log('onRemove');
18705 this.clearSelections();
18706 var el = this.dataName ?
18707 this.el.child('.roo-tpl-' + this.dataName) :
18710 el.dom.removeChild(this.nodes[index]);
18711 this.updateIndexes(index);
18715 * Refresh an individual node.
18716 * @param {Number} index
18718 refreshNode : function(index){
18719 this.onUpdate(this.store, this.store.getAt(index));
18722 updateIndexes : function(startIndex, endIndex){
18723 var ns = this.nodes;
18724 startIndex = startIndex || 0;
18725 endIndex = endIndex || ns.length - 1;
18726 for(var i = startIndex; i <= endIndex; i++){
18727 ns[i].nodeIndex = i;
18732 * Changes the data store this view uses and refresh the view.
18733 * @param {Store} store
18735 setStore : function(store, initial){
18736 if(!initial && this.store){
18737 this.store.un("datachanged", this.refresh);
18738 this.store.un("add", this.onAdd);
18739 this.store.un("remove", this.onRemove);
18740 this.store.un("update", this.onUpdate);
18741 this.store.un("clear", this.refresh);
18742 this.store.un("beforeload", this.onBeforeLoad);
18743 this.store.un("load", this.onLoad);
18744 this.store.un("loadexception", this.onLoad);
18748 store.on("datachanged", this.refresh, this);
18749 store.on("add", this.onAdd, this);
18750 store.on("remove", this.onRemove, this);
18751 store.on("update", this.onUpdate, this);
18752 store.on("clear", this.refresh, this);
18753 store.on("beforeload", this.onBeforeLoad, this);
18754 store.on("load", this.onLoad, this);
18755 store.on("loadexception", this.onLoad, this);
18763 * onbeforeLoad - masks the loading area.
18766 onBeforeLoad : function(store,opts)
18768 //Roo.log('onBeforeLoad');
18770 this.el.update("");
18772 this.el.mask(this.mask ? this.mask : "Loading" );
18774 onLoad : function ()
18781 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18782 * @param {HTMLElement} node
18783 * @return {HTMLElement} The template node
18785 findItemFromChild : function(node){
18786 var el = this.dataName ?
18787 this.el.child('.roo-tpl-' + this.dataName,true) :
18790 if(!node || node.parentNode == el){
18793 var p = node.parentNode;
18794 while(p && p != el){
18795 if(p.parentNode == el){
18804 onClick : function(e){
18805 var item = this.findItemFromChild(e.getTarget());
18807 var index = this.indexOf(item);
18808 if(this.onItemClick(item, index, e) !== false){
18809 this.fireEvent("click", this, index, item, e);
18812 this.clearSelections();
18817 onContextMenu : function(e){
18818 var item = this.findItemFromChild(e.getTarget());
18820 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18825 onDblClick : function(e){
18826 var item = this.findItemFromChild(e.getTarget());
18828 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18832 onItemClick : function(item, index, e)
18834 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18837 if (this.toggleSelect) {
18838 var m = this.isSelected(item) ? 'unselect' : 'select';
18841 _t[m](item, true, false);
18844 if(this.multiSelect || this.singleSelect){
18845 if(this.multiSelect && e.shiftKey && this.lastSelection){
18846 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18848 this.select(item, this.multiSelect && e.ctrlKey);
18849 this.lastSelection = item;
18852 if(!this.tickable){
18853 e.preventDefault();
18861 * Get the number of selected nodes.
18864 getSelectionCount : function(){
18865 return this.selections.length;
18869 * Get the currently selected nodes.
18870 * @return {Array} An array of HTMLElements
18872 getSelectedNodes : function(){
18873 return this.selections;
18877 * Get the indexes of the selected nodes.
18880 getSelectedIndexes : function(){
18881 var indexes = [], s = this.selections;
18882 for(var i = 0, len = s.length; i < len; i++){
18883 indexes.push(s[i].nodeIndex);
18889 * Clear all selections
18890 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18892 clearSelections : function(suppressEvent){
18893 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18894 this.cmp.elements = this.selections;
18895 this.cmp.removeClass(this.selectedClass);
18896 this.selections = [];
18897 if(!suppressEvent){
18898 this.fireEvent("selectionchange", this, this.selections);
18904 * Returns true if the passed node is selected
18905 * @param {HTMLElement/Number} node The node or node index
18906 * @return {Boolean}
18908 isSelected : function(node){
18909 var s = this.selections;
18913 node = this.getNode(node);
18914 return s.indexOf(node) !== -1;
18919 * @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
18920 * @param {Boolean} keepExisting (optional) true to keep existing selections
18921 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18923 select : function(nodeInfo, keepExisting, suppressEvent){
18924 if(nodeInfo instanceof Array){
18926 this.clearSelections(true);
18928 for(var i = 0, len = nodeInfo.length; i < len; i++){
18929 this.select(nodeInfo[i], true, true);
18933 var node = this.getNode(nodeInfo);
18934 if(!node || this.isSelected(node)){
18935 return; // already selected.
18938 this.clearSelections(true);
18941 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18942 Roo.fly(node).addClass(this.selectedClass);
18943 this.selections.push(node);
18944 if(!suppressEvent){
18945 this.fireEvent("selectionchange", this, this.selections);
18953 * @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
18954 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18955 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18957 unselect : function(nodeInfo, keepExisting, suppressEvent)
18959 if(nodeInfo instanceof Array){
18960 Roo.each(this.selections, function(s) {
18961 this.unselect(s, nodeInfo);
18965 var node = this.getNode(nodeInfo);
18966 if(!node || !this.isSelected(node)){
18967 //Roo.log("not selected");
18968 return; // not selected.
18972 Roo.each(this.selections, function(s) {
18974 Roo.fly(node).removeClass(this.selectedClass);
18981 this.selections= ns;
18982 this.fireEvent("selectionchange", this, this.selections);
18986 * Gets a template node.
18987 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988 * @return {HTMLElement} The node or null if it wasn't found
18990 getNode : function(nodeInfo){
18991 if(typeof nodeInfo == "string"){
18992 return document.getElementById(nodeInfo);
18993 }else if(typeof nodeInfo == "number"){
18994 return this.nodes[nodeInfo];
19000 * Gets a range template nodes.
19001 * @param {Number} startIndex
19002 * @param {Number} endIndex
19003 * @return {Array} An array of nodes
19005 getNodes : function(start, end){
19006 var ns = this.nodes;
19007 start = start || 0;
19008 end = typeof end == "undefined" ? ns.length - 1 : end;
19011 for(var i = start; i <= end; i++){
19015 for(var i = start; i >= end; i--){
19023 * Finds the index of the passed node
19024 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025 * @return {Number} The index of the node or -1
19027 indexOf : function(node){
19028 node = this.getNode(node);
19029 if(typeof node.nodeIndex == "number"){
19030 return node.nodeIndex;
19032 var ns = this.nodes;
19033 for(var i = 0, len = ns.length; i < len; i++){
19044 * based on jquery fullcalendar
19048 Roo.bootstrap = Roo.bootstrap || {};
19050 * @class Roo.bootstrap.Calendar
19051 * @extends Roo.bootstrap.Component
19052 * Bootstrap Calendar class
19053 * @cfg {Boolean} loadMask (true|false) default false
19054 * @cfg {Object} header generate the user specific header of the calendar, default false
19057 * Create a new Container
19058 * @param {Object} config The config object
19063 Roo.bootstrap.Calendar = function(config){
19064 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19068 * Fires when a date is selected
19069 * @param {DatePicker} this
19070 * @param {Date} date The selected date
19074 * @event monthchange
19075 * Fires when the displayed month changes
19076 * @param {DatePicker} this
19077 * @param {Date} date The selected month
19079 'monthchange': true,
19081 * @event evententer
19082 * Fires when mouse over an event
19083 * @param {Calendar} this
19084 * @param {event} Event
19086 'evententer': true,
19088 * @event eventleave
19089 * Fires when the mouse leaves an
19090 * @param {Calendar} this
19093 'eventleave': true,
19095 * @event eventclick
19096 * Fires when the mouse click an
19097 * @param {Calendar} this
19106 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19109 * @cfg {Number} startDay
19110 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19118 getAutoCreate : function(){
19121 var fc_button = function(name, corner, style, content ) {
19122 return Roo.apply({},{
19124 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19126 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19129 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19140 style : 'width:100%',
19147 cls : 'fc-header-left',
19149 fc_button('prev', 'left', 'arrow', '‹' ),
19150 fc_button('next', 'right', 'arrow', '›' ),
19151 { tag: 'span', cls: 'fc-header-space' },
19152 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19160 cls : 'fc-header-center',
19164 cls: 'fc-header-title',
19167 html : 'month / year'
19175 cls : 'fc-header-right',
19177 /* fc_button('month', 'left', '', 'month' ),
19178 fc_button('week', '', '', 'week' ),
19179 fc_button('day', 'right', '', 'day' )
19191 header = this.header;
19194 var cal_heads = function() {
19196 // fixme - handle this.
19198 for (var i =0; i < Date.dayNames.length; i++) {
19199 var d = Date.dayNames[i];
19202 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19203 html : d.substring(0,3)
19207 ret[0].cls += ' fc-first';
19208 ret[6].cls += ' fc-last';
19211 var cal_cell = function(n) {
19214 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19219 cls: 'fc-day-number',
19223 cls: 'fc-day-content',
19227 style: 'position: relative;' // height: 17px;
19239 var cal_rows = function() {
19242 for (var r = 0; r < 6; r++) {
19249 for (var i =0; i < Date.dayNames.length; i++) {
19250 var d = Date.dayNames[i];
19251 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19254 row.cn[0].cls+=' fc-first';
19255 row.cn[0].cn[0].style = 'min-height:90px';
19256 row.cn[6].cls+=' fc-last';
19260 ret[0].cls += ' fc-first';
19261 ret[4].cls += ' fc-prev-last';
19262 ret[5].cls += ' fc-last';
19269 cls: 'fc-border-separate',
19270 style : 'width:100%',
19278 cls : 'fc-first fc-last',
19296 cls : 'fc-content',
19297 style : "position: relative;",
19300 cls : 'fc-view fc-view-month fc-grid',
19301 style : 'position: relative',
19302 unselectable : 'on',
19305 cls : 'fc-event-container',
19306 style : 'position:absolute;z-index:8;top:0;left:0;'
19324 initEvents : function()
19327 throw "can not find store for calendar";
19333 style: "text-align:center",
19337 style: "background-color:white;width:50%;margin:250 auto",
19341 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19352 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19354 var size = this.el.select('.fc-content', true).first().getSize();
19355 this.maskEl.setSize(size.width, size.height);
19356 this.maskEl.enableDisplayMode("block");
19357 if(!this.loadMask){
19358 this.maskEl.hide();
19361 this.store = Roo.factory(this.store, Roo.data);
19362 this.store.on('load', this.onLoad, this);
19363 this.store.on('beforeload', this.onBeforeLoad, this);
19367 this.cells = this.el.select('.fc-day',true);
19368 //Roo.log(this.cells);
19369 this.textNodes = this.el.query('.fc-day-number');
19370 this.cells.addClassOnOver('fc-state-hover');
19372 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19373 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19374 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19375 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19377 this.on('monthchange', this.onMonthChange, this);
19379 this.update(new Date().clearTime());
19382 resize : function() {
19383 var sz = this.el.getSize();
19385 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19386 this.el.select('.fc-day-content div',true).setHeight(34);
19391 showPrevMonth : function(e){
19392 this.update(this.activeDate.add("mo", -1));
19394 showToday : function(e){
19395 this.update(new Date().clearTime());
19398 showNextMonth : function(e){
19399 this.update(this.activeDate.add("mo", 1));
19403 showPrevYear : function(){
19404 this.update(this.activeDate.add("y", -1));
19408 showNextYear : function(){
19409 this.update(this.activeDate.add("y", 1));
19414 update : function(date)
19416 var vd = this.activeDate;
19417 this.activeDate = date;
19418 // if(vd && this.el){
19419 // var t = date.getTime();
19420 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19421 // Roo.log('using add remove');
19423 // this.fireEvent('monthchange', this, date);
19425 // this.cells.removeClass("fc-state-highlight");
19426 // this.cells.each(function(c){
19427 // if(c.dateValue == t){
19428 // c.addClass("fc-state-highlight");
19429 // setTimeout(function(){
19430 // try{c.dom.firstChild.focus();}catch(e){}
19440 var days = date.getDaysInMonth();
19442 var firstOfMonth = date.getFirstDateOfMonth();
19443 var startingPos = firstOfMonth.getDay()-this.startDay;
19445 if(startingPos < this.startDay){
19449 var pm = date.add(Date.MONTH, -1);
19450 var prevStart = pm.getDaysInMonth()-startingPos;
19452 this.cells = this.el.select('.fc-day',true);
19453 this.textNodes = this.el.query('.fc-day-number');
19454 this.cells.addClassOnOver('fc-state-hover');
19456 var cells = this.cells.elements;
19457 var textEls = this.textNodes;
19459 Roo.each(cells, function(cell){
19460 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19463 days += startingPos;
19465 // convert everything to numbers so it's fast
19466 var day = 86400000;
19467 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19470 //Roo.log(prevStart);
19472 var today = new Date().clearTime().getTime();
19473 var sel = date.clearTime().getTime();
19474 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19475 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19476 var ddMatch = this.disabledDatesRE;
19477 var ddText = this.disabledDatesText;
19478 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19479 var ddaysText = this.disabledDaysText;
19480 var format = this.format;
19482 var setCellClass = function(cal, cell){
19486 //Roo.log('set Cell Class');
19488 var t = d.getTime();
19492 cell.dateValue = t;
19494 cell.className += " fc-today";
19495 cell.className += " fc-state-highlight";
19496 cell.title = cal.todayText;
19499 // disable highlight in other month..
19500 //cell.className += " fc-state-highlight";
19505 cell.className = " fc-state-disabled";
19506 cell.title = cal.minText;
19510 cell.className = " fc-state-disabled";
19511 cell.title = cal.maxText;
19515 if(ddays.indexOf(d.getDay()) != -1){
19516 cell.title = ddaysText;
19517 cell.className = " fc-state-disabled";
19520 if(ddMatch && format){
19521 var fvalue = d.dateFormat(format);
19522 if(ddMatch.test(fvalue)){
19523 cell.title = ddText.replace("%0", fvalue);
19524 cell.className = " fc-state-disabled";
19528 if (!cell.initialClassName) {
19529 cell.initialClassName = cell.dom.className;
19532 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19537 for(; i < startingPos; i++) {
19538 textEls[i].innerHTML = (++prevStart);
19539 d.setDate(d.getDate()+1);
19541 cells[i].className = "fc-past fc-other-month";
19542 setCellClass(this, cells[i]);
19547 for(; i < days; i++){
19548 intDay = i - startingPos + 1;
19549 textEls[i].innerHTML = (intDay);
19550 d.setDate(d.getDate()+1);
19552 cells[i].className = ''; // "x-date-active";
19553 setCellClass(this, cells[i]);
19557 for(; i < 42; i++) {
19558 textEls[i].innerHTML = (++extraDays);
19559 d.setDate(d.getDate()+1);
19561 cells[i].className = "fc-future fc-other-month";
19562 setCellClass(this, cells[i]);
19565 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19567 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19569 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19570 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19572 if(totalRows != 6){
19573 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19574 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19577 this.fireEvent('monthchange', this, date);
19581 if(!this.internalRender){
19582 var main = this.el.dom.firstChild;
19583 var w = main.offsetWidth;
19584 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19585 Roo.fly(main).setWidth(w);
19586 this.internalRender = true;
19587 // opera does not respect the auto grow header center column
19588 // then, after it gets a width opera refuses to recalculate
19589 // without a second pass
19590 if(Roo.isOpera && !this.secondPass){
19591 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19592 this.secondPass = true;
19593 this.update.defer(10, this, [date]);
19600 findCell : function(dt) {
19601 dt = dt.clearTime().getTime();
19603 this.cells.each(function(c){
19604 //Roo.log("check " +c.dateValue + '?=' + dt);
19605 if(c.dateValue == dt){
19615 findCells : function(ev) {
19616 var s = ev.start.clone().clearTime().getTime();
19618 var e= ev.end.clone().clearTime().getTime();
19621 this.cells.each(function(c){
19622 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19624 if(c.dateValue > e){
19627 if(c.dateValue < s){
19636 // findBestRow: function(cells)
19640 // for (var i =0 ; i < cells.length;i++) {
19641 // ret = Math.max(cells[i].rows || 0,ret);
19648 addItem : function(ev)
19650 // look for vertical location slot in
19651 var cells = this.findCells(ev);
19653 // ev.row = this.findBestRow(cells);
19655 // work out the location.
19659 for(var i =0; i < cells.length; i++) {
19661 cells[i].row = cells[0].row;
19664 cells[i].row = cells[i].row + 1;
19674 if (crow.start.getY() == cells[i].getY()) {
19676 crow.end = cells[i];
19693 cells[0].events.push(ev);
19695 this.calevents.push(ev);
19698 clearEvents: function() {
19700 if(!this.calevents){
19704 Roo.each(this.cells.elements, function(c){
19710 Roo.each(this.calevents, function(e) {
19711 Roo.each(e.els, function(el) {
19712 el.un('mouseenter' ,this.onEventEnter, this);
19713 el.un('mouseleave' ,this.onEventLeave, this);
19718 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19724 renderEvents: function()
19728 this.cells.each(function(c) {
19737 if(c.row != c.events.length){
19738 r = 4 - (4 - (c.row - c.events.length));
19741 c.events = ev.slice(0, r);
19742 c.more = ev.slice(r);
19744 if(c.more.length && c.more.length == 1){
19745 c.events.push(c.more.pop());
19748 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19752 this.cells.each(function(c) {
19754 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19757 for (var e = 0; e < c.events.length; e++){
19758 var ev = c.events[e];
19759 var rows = ev.rows;
19761 for(var i = 0; i < rows.length; i++) {
19763 // how many rows should it span..
19766 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19767 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19769 unselectable : "on",
19772 cls: 'fc-event-inner',
19776 // cls: 'fc-event-time',
19777 // html : cells.length > 1 ? '' : ev.time
19781 cls: 'fc-event-title',
19782 html : String.format('{0}', ev.title)
19789 cls: 'ui-resizable-handle ui-resizable-e',
19790 html : '  '
19797 cfg.cls += ' fc-event-start';
19799 if ((i+1) == rows.length) {
19800 cfg.cls += ' fc-event-end';
19803 var ctr = _this.el.select('.fc-event-container',true).first();
19804 var cg = ctr.createChild(cfg);
19806 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19807 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19809 var r = (c.more.length) ? 1 : 0;
19810 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19811 cg.setWidth(ebox.right - sbox.x -2);
19813 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19814 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19815 cg.on('click', _this.onEventClick, _this, ev);
19826 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19827 style : 'position: absolute',
19828 unselectable : "on",
19831 cls: 'fc-event-inner',
19835 cls: 'fc-event-title',
19843 cls: 'ui-resizable-handle ui-resizable-e',
19844 html : '  '
19850 var ctr = _this.el.select('.fc-event-container',true).first();
19851 var cg = ctr.createChild(cfg);
19853 var sbox = c.select('.fc-day-content',true).first().getBox();
19854 var ebox = c.select('.fc-day-content',true).first().getBox();
19856 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19857 cg.setWidth(ebox.right - sbox.x -2);
19859 cg.on('click', _this.onMoreEventClick, _this, c.more);
19869 onEventEnter: function (e, el,event,d) {
19870 this.fireEvent('evententer', this, el, event);
19873 onEventLeave: function (e, el,event,d) {
19874 this.fireEvent('eventleave', this, el, event);
19877 onEventClick: function (e, el,event,d) {
19878 this.fireEvent('eventclick', this, el, event);
19881 onMonthChange: function () {
19885 onMoreEventClick: function(e, el, more)
19889 this.calpopover.placement = 'right';
19890 this.calpopover.setTitle('More');
19892 this.calpopover.setContent('');
19894 var ctr = this.calpopover.el.select('.popover-content', true).first();
19896 Roo.each(more, function(m){
19898 cls : 'fc-event-hori fc-event-draggable',
19901 var cg = ctr.createChild(cfg);
19903 cg.on('click', _this.onEventClick, _this, m);
19906 this.calpopover.show(el);
19911 onLoad: function ()
19913 this.calevents = [];
19916 if(this.store.getCount() > 0){
19917 this.store.data.each(function(d){
19920 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19921 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19922 time : d.data.start_time,
19923 title : d.data.title,
19924 description : d.data.description,
19925 venue : d.data.venue
19930 this.renderEvents();
19932 if(this.calevents.length && this.loadMask){
19933 this.maskEl.hide();
19937 onBeforeLoad: function()
19939 this.clearEvents();
19941 this.maskEl.show();
19955 * @class Roo.bootstrap.Popover
19956 * @extends Roo.bootstrap.Component
19957 * Bootstrap Popover class
19958 * @cfg {String} html contents of the popover (or false to use children..)
19959 * @cfg {String} title of popover (or false to hide)
19960 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19961 * @cfg {String} trigger click || hover (or false to trigger manually)
19962 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19963 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19964 * - if false and it has a 'parent' then it will be automatically added to that element
19965 * - if string - Roo.get will be called
19966 * @cfg {Number} delay - delay before showing
19969 * Create a new Popover
19970 * @param {Object} config The config object
19973 Roo.bootstrap.Popover = function(config){
19974 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19980 * After the popover show
19982 * @param {Roo.bootstrap.Popover} this
19987 * After the popover hide
19989 * @param {Roo.bootstrap.Popover} this
19995 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20000 placement : 'right',
20001 trigger : 'hover', // hover
20007 can_build_overlaid : false,
20009 maskEl : false, // the mask element
20012 alignEl : false, // when show is called with an element - this get's stored.
20014 getChildContainer : function()
20016 return this.contentEl;
20019 getPopoverHeader : function()
20021 this.title = true; // flag not to hide it..
20022 this.headerEl.addClass('p-0');
20023 return this.headerEl
20027 getAutoCreate : function(){
20030 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20031 style: 'display:block',
20037 cls : 'popover-inner ',
20041 cls: 'popover-title popover-header',
20042 html : this.title === false ? '' : this.title
20045 cls : 'popover-content popover-body ' + (this.cls || ''),
20046 html : this.html || ''
20057 * @param {string} the title
20059 setTitle: function(str)
20063 this.headerEl.dom.innerHTML = str;
20068 * @param {string} the body content
20070 setContent: function(str)
20073 if (this.contentEl) {
20074 this.contentEl.dom.innerHTML = str;
20078 // as it get's added to the bottom of the page.
20079 onRender : function(ct, position)
20081 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20086 var cfg = Roo.apply({}, this.getAutoCreate());
20090 cfg.cls += ' ' + this.cls;
20093 cfg.style = this.style;
20095 //Roo.log("adding to ");
20096 this.el = Roo.get(document.body).createChild(cfg, position);
20097 // Roo.log(this.el);
20100 this.contentEl = this.el.select('.popover-content',true).first();
20101 this.headerEl = this.el.select('.popover-title',true).first();
20104 if(typeof(this.items) != 'undefined'){
20105 var items = this.items;
20108 for(var i =0;i < items.length;i++) {
20109 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20113 this.items = nitems;
20115 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20116 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20123 resizeMask : function()
20125 this.maskEl.setSize(
20126 Roo.lib.Dom.getViewWidth(true),
20127 Roo.lib.Dom.getViewHeight(true)
20131 initEvents : function()
20135 Roo.bootstrap.Popover.register(this);
20138 this.arrowEl = this.el.select('.arrow',true).first();
20139 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20140 this.el.enableDisplayMode('block');
20144 if (this.over === false && !this.parent()) {
20147 if (this.triggers === false) {
20152 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20153 var triggers = this.trigger ? this.trigger.split(' ') : [];
20154 Roo.each(triggers, function(trigger) {
20156 if (trigger == 'click') {
20157 on_el.on('click', this.toggle, this);
20158 } else if (trigger != 'manual') {
20159 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20160 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20162 on_el.on(eventIn ,this.enter, this);
20163 on_el.on(eventOut, this.leave, this);
20173 toggle : function () {
20174 this.hoverState == 'in' ? this.leave() : this.enter();
20177 enter : function () {
20179 clearTimeout(this.timeout);
20181 this.hoverState = 'in';
20183 if (!this.delay || !this.delay.show) {
20188 this.timeout = setTimeout(function () {
20189 if (_t.hoverState == 'in') {
20192 }, this.delay.show)
20195 leave : function() {
20196 clearTimeout(this.timeout);
20198 this.hoverState = 'out';
20200 if (!this.delay || !this.delay.hide) {
20205 this.timeout = setTimeout(function () {
20206 if (_t.hoverState == 'out') {
20209 }, this.delay.hide)
20213 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20214 * @param {string} (left|right|top|bottom) position
20216 show : function (on_el, placement)
20218 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20219 on_el = on_el || false; // default to false
20222 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20223 on_el = this.parent().el;
20224 } else if (this.over) {
20225 Roo.get(this.over);
20230 this.alignEl = Roo.get( on_el );
20233 this.render(document.body);
20239 if (this.title === false) {
20240 this.headerEl.hide();
20245 this.el.dom.style.display = 'block';
20248 if (this.alignEl) {
20249 this.updatePosition(this.placement, true);
20252 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20253 var es = this.el.getSize();
20254 var x = Roo.lib.Dom.getViewWidth()/2;
20255 var y = Roo.lib.Dom.getViewHeight()/2;
20256 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20261 //var arrow = this.el.select('.arrow',true).first();
20262 //arrow.set(align[2],
20264 this.el.addClass('in');
20268 this.hoverState = 'in';
20271 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20272 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20273 this.maskEl.dom.style.display = 'block';
20274 this.maskEl.addClass('show');
20276 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20278 this.fireEvent('show', this);
20282 * fire this manually after loading a grid in the table for example
20283 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20284 * @param {Boolean} try and move it if we cant get right position.
20286 updatePosition : function(placement, try_move)
20288 // allow for calling with no parameters
20289 placement = placement ? placement : this.placement;
20290 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20292 this.el.removeClass([
20293 'fade','top','bottom', 'left', 'right','in',
20294 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20296 this.el.addClass(placement + ' bs-popover-' + placement);
20298 if (!this.alignEl ) {
20302 switch (placement) {
20304 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20305 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20306 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20307 //normal display... or moved up/down.
20308 this.el.setXY(offset);
20309 var xy = this.alignEl.getAnchorXY('tr', false);
20311 this.arrowEl.setXY(xy);
20314 // continue through...
20315 return this.updatePosition('left', false);
20319 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20320 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20321 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20322 //normal display... or moved up/down.
20323 this.el.setXY(offset);
20324 var xy = this.alignEl.getAnchorXY('tl', false);
20325 xy[0]-=10;xy[1]+=5; // << fix me
20326 this.arrowEl.setXY(xy);
20330 return this.updatePosition('right', false);
20333 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20334 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20335 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20336 //normal display... or moved up/down.
20337 this.el.setXY(offset);
20338 var xy = this.alignEl.getAnchorXY('t', false);
20339 xy[1]-=10; // << fix me
20340 this.arrowEl.setXY(xy);
20344 return this.updatePosition('bottom', false);
20347 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20348 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20349 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20350 //normal display... or moved up/down.
20351 this.el.setXY(offset);
20352 var xy = this.alignEl.getAnchorXY('b', false);
20353 xy[1]+=2; // << fix me
20354 this.arrowEl.setXY(xy);
20358 return this.updatePosition('top', false);
20369 this.el.setXY([0,0]);
20370 this.el.removeClass('in');
20372 this.hoverState = null;
20373 this.maskEl.hide(); // always..
20374 this.fireEvent('hide', this);
20380 Roo.apply(Roo.bootstrap.Popover, {
20383 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20384 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20385 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20386 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20391 clickHander : false,
20394 onMouseDown : function(e)
20396 if (!e.getTarget(".roo-popover")) {
20404 register : function(popup)
20406 if (!Roo.bootstrap.Popover.clickHandler) {
20407 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20409 // hide other popups.
20411 this.popups.push(popup);
20413 hideAll : function()
20415 this.popups.forEach(function(p) {
20423 * Card header - holder for the card header elements.
20428 * @class Roo.bootstrap.PopoverNav
20429 * @extends Roo.bootstrap.NavGroup
20430 * Bootstrap Popover header navigation class
20432 * Create a new Popover Header Navigation
20433 * @param {Object} config The config object
20436 Roo.bootstrap.PopoverNav = function(config){
20437 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20440 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20443 container_method : 'getPopoverHeader'
20461 * @class Roo.bootstrap.Progress
20462 * @extends Roo.bootstrap.Component
20463 * Bootstrap Progress class
20464 * @cfg {Boolean} striped striped of the progress bar
20465 * @cfg {Boolean} active animated of the progress bar
20469 * Create a new Progress
20470 * @param {Object} config The config object
20473 Roo.bootstrap.Progress = function(config){
20474 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20477 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20482 getAutoCreate : function(){
20490 cfg.cls += ' progress-striped';
20494 cfg.cls += ' active';
20513 * @class Roo.bootstrap.ProgressBar
20514 * @extends Roo.bootstrap.Component
20515 * Bootstrap ProgressBar class
20516 * @cfg {Number} aria_valuenow aria-value now
20517 * @cfg {Number} aria_valuemin aria-value min
20518 * @cfg {Number} aria_valuemax aria-value max
20519 * @cfg {String} label label for the progress bar
20520 * @cfg {String} panel (success | info | warning | danger )
20521 * @cfg {String} role role of the progress bar
20522 * @cfg {String} sr_only text
20526 * Create a new ProgressBar
20527 * @param {Object} config The config object
20530 Roo.bootstrap.ProgressBar = function(config){
20531 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20534 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20538 aria_valuemax : 100,
20544 getAutoCreate : function()
20549 cls: 'progress-bar',
20550 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20562 cfg.role = this.role;
20565 if(this.aria_valuenow){
20566 cfg['aria-valuenow'] = this.aria_valuenow;
20569 if(this.aria_valuemin){
20570 cfg['aria-valuemin'] = this.aria_valuemin;
20573 if(this.aria_valuemax){
20574 cfg['aria-valuemax'] = this.aria_valuemax;
20577 if(this.label && !this.sr_only){
20578 cfg.html = this.label;
20582 cfg.cls += ' progress-bar-' + this.panel;
20588 update : function(aria_valuenow)
20590 this.aria_valuenow = aria_valuenow;
20592 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20607 * @class Roo.bootstrap.TabGroup
20608 * @extends Roo.bootstrap.Column
20609 * Bootstrap Column class
20610 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20611 * @cfg {Boolean} carousel true to make the group behave like a carousel
20612 * @cfg {Boolean} bullets show bullets for the panels
20613 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20614 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20615 * @cfg {Boolean} showarrow (true|false) show arrow default true
20618 * Create a new TabGroup
20619 * @param {Object} config The config object
20622 Roo.bootstrap.TabGroup = function(config){
20623 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20625 this.navId = Roo.id();
20628 Roo.bootstrap.TabGroup.register(this);
20632 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20635 transition : false,
20640 slideOnTouch : false,
20643 getAutoCreate : function()
20645 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20647 cfg.cls += ' tab-content';
20649 if (this.carousel) {
20650 cfg.cls += ' carousel slide';
20653 cls : 'carousel-inner',
20657 if(this.bullets && !Roo.isTouch){
20660 cls : 'carousel-bullets',
20664 if(this.bullets_cls){
20665 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20672 cfg.cn[0].cn.push(bullets);
20675 if(this.showarrow){
20676 cfg.cn[0].cn.push({
20678 class : 'carousel-arrow',
20682 class : 'carousel-prev',
20686 class : 'fa fa-chevron-left'
20692 class : 'carousel-next',
20696 class : 'fa fa-chevron-right'
20709 initEvents: function()
20711 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20712 // this.el.on("touchstart", this.onTouchStart, this);
20715 if(this.autoslide){
20718 this.slideFn = window.setInterval(function() {
20719 _this.showPanelNext();
20723 if(this.showarrow){
20724 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20725 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20731 // onTouchStart : function(e, el, o)
20733 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20737 // this.showPanelNext();
20741 getChildContainer : function()
20743 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20747 * register a Navigation item
20748 * @param {Roo.bootstrap.NavItem} the navitem to add
20750 register : function(item)
20752 this.tabs.push( item);
20753 item.navId = this.navId; // not really needed..
20758 getActivePanel : function()
20761 Roo.each(this.tabs, function(t) {
20771 getPanelByName : function(n)
20774 Roo.each(this.tabs, function(t) {
20775 if (t.tabId == n) {
20783 indexOfPanel : function(p)
20786 Roo.each(this.tabs, function(t,i) {
20787 if (t.tabId == p.tabId) {
20796 * show a specific panel
20797 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20798 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20800 showPanel : function (pan)
20802 if(this.transition || typeof(pan) == 'undefined'){
20803 Roo.log("waiting for the transitionend");
20807 if (typeof(pan) == 'number') {
20808 pan = this.tabs[pan];
20811 if (typeof(pan) == 'string') {
20812 pan = this.getPanelByName(pan);
20815 var cur = this.getActivePanel();
20818 Roo.log('pan or acitve pan is undefined');
20822 if (pan.tabId == this.getActivePanel().tabId) {
20826 if (false === cur.fireEvent('beforedeactivate')) {
20830 if(this.bullets > 0 && !Roo.isTouch){
20831 this.setActiveBullet(this.indexOfPanel(pan));
20834 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20836 //class="carousel-item carousel-item-next carousel-item-left"
20838 this.transition = true;
20839 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20840 var lr = dir == 'next' ? 'left' : 'right';
20841 pan.el.addClass(dir); // or prev
20842 pan.el.addClass('carousel-item-' + dir); // or prev
20843 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20844 cur.el.addClass(lr); // or right
20845 pan.el.addClass(lr);
20846 cur.el.addClass('carousel-item-' +lr); // or right
20847 pan.el.addClass('carousel-item-' +lr);
20851 cur.el.on('transitionend', function() {
20852 Roo.log("trans end?");
20854 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20855 pan.setActive(true);
20857 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20858 cur.setActive(false);
20860 _this.transition = false;
20862 }, this, { single: true } );
20867 cur.setActive(false);
20868 pan.setActive(true);
20873 showPanelNext : function()
20875 var i = this.indexOfPanel(this.getActivePanel());
20877 if (i >= this.tabs.length - 1 && !this.autoslide) {
20881 if (i >= this.tabs.length - 1 && this.autoslide) {
20885 this.showPanel(this.tabs[i+1]);
20888 showPanelPrev : function()
20890 var i = this.indexOfPanel(this.getActivePanel());
20892 if (i < 1 && !this.autoslide) {
20896 if (i < 1 && this.autoslide) {
20897 i = this.tabs.length;
20900 this.showPanel(this.tabs[i-1]);
20904 addBullet: function()
20906 if(!this.bullets || Roo.isTouch){
20909 var ctr = this.el.select('.carousel-bullets',true).first();
20910 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20911 var bullet = ctr.createChild({
20912 cls : 'bullet bullet-' + i
20913 },ctr.dom.lastChild);
20918 bullet.on('click', (function(e, el, o, ii, t){
20920 e.preventDefault();
20922 this.showPanel(ii);
20924 if(this.autoslide && this.slideFn){
20925 clearInterval(this.slideFn);
20926 this.slideFn = window.setInterval(function() {
20927 _this.showPanelNext();
20931 }).createDelegate(this, [i, bullet], true));
20936 setActiveBullet : function(i)
20942 Roo.each(this.el.select('.bullet', true).elements, function(el){
20943 el.removeClass('selected');
20946 var bullet = this.el.select('.bullet-' + i, true).first();
20952 bullet.addClass('selected');
20963 Roo.apply(Roo.bootstrap.TabGroup, {
20967 * register a Navigation Group
20968 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20970 register : function(navgrp)
20972 this.groups[navgrp.navId] = navgrp;
20976 * fetch a Navigation Group based on the navigation ID
20977 * if one does not exist , it will get created.
20978 * @param {string} the navgroup to add
20979 * @returns {Roo.bootstrap.NavGroup} the navgroup
20981 get: function(navId) {
20982 if (typeof(this.groups[navId]) == 'undefined') {
20983 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20985 return this.groups[navId] ;
21000 * @class Roo.bootstrap.TabPanel
21001 * @extends Roo.bootstrap.Component
21002 * Bootstrap TabPanel class
21003 * @cfg {Boolean} active panel active
21004 * @cfg {String} html panel content
21005 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21006 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21007 * @cfg {String} href click to link..
21008 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21012 * Create a new TabPanel
21013 * @param {Object} config The config object
21016 Roo.bootstrap.TabPanel = function(config){
21017 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21021 * Fires when the active status changes
21022 * @param {Roo.bootstrap.TabPanel} this
21023 * @param {Boolean} state the new state
21028 * @event beforedeactivate
21029 * Fires before a tab is de-activated - can be used to do validation on a form.
21030 * @param {Roo.bootstrap.TabPanel} this
21031 * @return {Boolean} false if there is an error
21034 'beforedeactivate': true
21037 this.tabId = this.tabId || Roo.id();
21041 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21048 touchSlide : false,
21049 getAutoCreate : function(){
21054 // item is needed for carousel - not sure if it has any effect otherwise
21055 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21056 html: this.html || ''
21060 cfg.cls += ' active';
21064 cfg.tabId = this.tabId;
21072 initEvents: function()
21074 var p = this.parent();
21076 this.navId = this.navId || p.navId;
21078 if (typeof(this.navId) != 'undefined') {
21079 // not really needed.. but just in case.. parent should be a NavGroup.
21080 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21084 var i = tg.tabs.length - 1;
21086 if(this.active && tg.bullets > 0 && i < tg.bullets){
21087 tg.setActiveBullet(i);
21091 this.el.on('click', this.onClick, this);
21093 if(Roo.isTouch && this.touchSlide){
21094 this.el.on("touchstart", this.onTouchStart, this);
21095 this.el.on("touchmove", this.onTouchMove, this);
21096 this.el.on("touchend", this.onTouchEnd, this);
21101 onRender : function(ct, position)
21103 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21106 setActive : function(state)
21108 Roo.log("panel - set active " + this.tabId + "=" + state);
21110 this.active = state;
21112 this.el.removeClass('active');
21114 } else if (!this.el.hasClass('active')) {
21115 this.el.addClass('active');
21118 this.fireEvent('changed', this, state);
21121 onClick : function(e)
21123 e.preventDefault();
21125 if(!this.href.length){
21129 window.location.href = this.href;
21138 onTouchStart : function(e)
21140 this.swiping = false;
21142 this.startX = e.browserEvent.touches[0].clientX;
21143 this.startY = e.browserEvent.touches[0].clientY;
21146 onTouchMove : function(e)
21148 this.swiping = true;
21150 this.endX = e.browserEvent.touches[0].clientX;
21151 this.endY = e.browserEvent.touches[0].clientY;
21154 onTouchEnd : function(e)
21161 var tabGroup = this.parent();
21163 if(this.endX > this.startX){ // swiping right
21164 tabGroup.showPanelPrev();
21168 if(this.startX > this.endX){ // swiping left
21169 tabGroup.showPanelNext();
21188 * @class Roo.bootstrap.DateField
21189 * @extends Roo.bootstrap.Input
21190 * Bootstrap DateField class
21191 * @cfg {Number} weekStart default 0
21192 * @cfg {String} viewMode default empty, (months|years)
21193 * @cfg {String} minViewMode default empty, (months|years)
21194 * @cfg {Number} startDate default -Infinity
21195 * @cfg {Number} endDate default Infinity
21196 * @cfg {Boolean} todayHighlight default false
21197 * @cfg {Boolean} todayBtn default false
21198 * @cfg {Boolean} calendarWeeks default false
21199 * @cfg {Object} daysOfWeekDisabled default empty
21200 * @cfg {Boolean} singleMode default false (true | false)
21202 * @cfg {Boolean} keyboardNavigation default true
21203 * @cfg {String} language default en
21206 * Create a new DateField
21207 * @param {Object} config The config object
21210 Roo.bootstrap.DateField = function(config){
21211 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21215 * Fires when this field show.
21216 * @param {Roo.bootstrap.DateField} this
21217 * @param {Mixed} date The date value
21222 * Fires when this field hide.
21223 * @param {Roo.bootstrap.DateField} this
21224 * @param {Mixed} date The date value
21229 * Fires when select a date.
21230 * @param {Roo.bootstrap.DateField} this
21231 * @param {Mixed} date The date value
21235 * @event beforeselect
21236 * Fires when before select a date.
21237 * @param {Roo.bootstrap.DateField} this
21238 * @param {Mixed} date The date value
21240 beforeselect : true
21244 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21247 * @cfg {String} format
21248 * The default date format string which can be overriden for localization support. The format must be
21249 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21253 * @cfg {String} altFormats
21254 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21255 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21257 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21265 todayHighlight : false,
21271 keyboardNavigation: true,
21273 calendarWeeks: false,
21275 startDate: -Infinity,
21279 daysOfWeekDisabled: [],
21283 singleMode : false,
21285 UTCDate: function()
21287 return new Date(Date.UTC.apply(Date, arguments));
21290 UTCToday: function()
21292 var today = new Date();
21293 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21296 getDate: function() {
21297 var d = this.getUTCDate();
21298 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21301 getUTCDate: function() {
21305 setDate: function(d) {
21306 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21309 setUTCDate: function(d) {
21311 this.setValue(this.formatDate(this.date));
21314 onRender: function(ct, position)
21317 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21319 this.language = this.language || 'en';
21320 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21321 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21323 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21324 this.format = this.format || 'm/d/y';
21325 this.isInline = false;
21326 this.isInput = true;
21327 this.component = this.el.select('.add-on', true).first() || false;
21328 this.component = (this.component && this.component.length === 0) ? false : this.component;
21329 this.hasInput = this.component && this.inputEl().length;
21331 if (typeof(this.minViewMode === 'string')) {
21332 switch (this.minViewMode) {
21334 this.minViewMode = 1;
21337 this.minViewMode = 2;
21340 this.minViewMode = 0;
21345 if (typeof(this.viewMode === 'string')) {
21346 switch (this.viewMode) {
21359 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21361 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21363 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21365 this.picker().on('mousedown', this.onMousedown, this);
21366 this.picker().on('click', this.onClick, this);
21368 this.picker().addClass('datepicker-dropdown');
21370 this.startViewMode = this.viewMode;
21372 if(this.singleMode){
21373 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21374 v.setVisibilityMode(Roo.Element.DISPLAY);
21378 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21379 v.setStyle('width', '189px');
21383 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21384 if(!this.calendarWeeks){
21389 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21390 v.attr('colspan', function(i, val){
21391 return parseInt(val) + 1;
21396 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21398 this.setStartDate(this.startDate);
21399 this.setEndDate(this.endDate);
21401 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21408 if(this.isInline) {
21413 picker : function()
21415 return this.pickerEl;
21416 // return this.el.select('.datepicker', true).first();
21419 fillDow: function()
21421 var dowCnt = this.weekStart;
21430 if(this.calendarWeeks){
21438 while (dowCnt < this.weekStart + 7) {
21442 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21446 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21449 fillMonths: function()
21452 var months = this.picker().select('>.datepicker-months td', true).first();
21454 months.dom.innerHTML = '';
21460 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21463 months.createChild(month);
21470 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;
21472 if (this.date < this.startDate) {
21473 this.viewDate = new Date(this.startDate);
21474 } else if (this.date > this.endDate) {
21475 this.viewDate = new Date(this.endDate);
21477 this.viewDate = new Date(this.date);
21485 var d = new Date(this.viewDate),
21486 year = d.getUTCFullYear(),
21487 month = d.getUTCMonth(),
21488 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21489 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21490 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21491 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21492 currentDate = this.date && this.date.valueOf(),
21493 today = this.UTCToday();
21495 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21497 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21499 // this.picker.select('>tfoot th.today').
21500 // .text(dates[this.language].today)
21501 // .toggle(this.todayBtn !== false);
21503 this.updateNavArrows();
21506 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21508 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21510 prevMonth.setUTCDate(day);
21512 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21514 var nextMonth = new Date(prevMonth);
21516 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21518 nextMonth = nextMonth.valueOf();
21520 var fillMonths = false;
21522 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21524 while(prevMonth.valueOf() <= nextMonth) {
21527 if (prevMonth.getUTCDay() === this.weekStart) {
21529 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21537 if(this.calendarWeeks){
21538 // ISO 8601: First week contains first thursday.
21539 // ISO also states week starts on Monday, but we can be more abstract here.
21541 // Start of current week: based on weekstart/current date
21542 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21543 // Thursday of this week
21544 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21545 // First Thursday of year, year from thursday
21546 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21547 // Calendar week: ms between thursdays, div ms per day, div 7 days
21548 calWeek = (th - yth) / 864e5 / 7 + 1;
21550 fillMonths.cn.push({
21558 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21560 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21563 if (this.todayHighlight &&
21564 prevMonth.getUTCFullYear() == today.getFullYear() &&
21565 prevMonth.getUTCMonth() == today.getMonth() &&
21566 prevMonth.getUTCDate() == today.getDate()) {
21567 clsName += ' today';
21570 if (currentDate && prevMonth.valueOf() === currentDate) {
21571 clsName += ' active';
21574 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21575 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21576 clsName += ' disabled';
21579 fillMonths.cn.push({
21581 cls: 'day ' + clsName,
21582 html: prevMonth.getDate()
21585 prevMonth.setDate(prevMonth.getDate()+1);
21588 var currentYear = this.date && this.date.getUTCFullYear();
21589 var currentMonth = this.date && this.date.getUTCMonth();
21591 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21593 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21594 v.removeClass('active');
21596 if(currentYear === year && k === currentMonth){
21597 v.addClass('active');
21600 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21601 v.addClass('disabled');
21607 year = parseInt(year/10, 10) * 10;
21609 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21611 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21614 for (var i = -1; i < 11; i++) {
21615 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21617 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21625 showMode: function(dir)
21628 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21631 Roo.each(this.picker().select('>div',true).elements, function(v){
21632 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21635 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21640 if(this.isInline) {
21644 this.picker().removeClass(['bottom', 'top']);
21646 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21648 * place to the top of element!
21652 this.picker().addClass('top');
21653 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21658 this.picker().addClass('bottom');
21660 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21663 parseDate : function(value)
21665 if(!value || value instanceof Date){
21668 var v = Date.parseDate(value, this.format);
21669 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21670 v = Date.parseDate(value, 'Y-m-d');
21672 if(!v && this.altFormats){
21673 if(!this.altFormatsArray){
21674 this.altFormatsArray = this.altFormats.split("|");
21676 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21677 v = Date.parseDate(value, this.altFormatsArray[i]);
21683 formatDate : function(date, fmt)
21685 return (!date || !(date instanceof Date)) ?
21686 date : date.dateFormat(fmt || this.format);
21689 onFocus : function()
21691 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21695 onBlur : function()
21697 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21699 var d = this.inputEl().getValue();
21706 showPopup : function()
21708 this.picker().show();
21712 this.fireEvent('showpopup', this, this.date);
21715 hidePopup : function()
21717 if(this.isInline) {
21720 this.picker().hide();
21721 this.viewMode = this.startViewMode;
21724 this.fireEvent('hidepopup', this, this.date);
21728 onMousedown: function(e)
21730 e.stopPropagation();
21731 e.preventDefault();
21736 Roo.bootstrap.DateField.superclass.keyup.call(this);
21740 setValue: function(v)
21742 if(this.fireEvent('beforeselect', this, v) !== false){
21743 var d = new Date(this.parseDate(v) ).clearTime();
21745 if(isNaN(d.getTime())){
21746 this.date = this.viewDate = '';
21747 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21751 v = this.formatDate(d);
21753 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21755 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21759 this.fireEvent('select', this, this.date);
21763 getValue: function()
21765 return this.formatDate(this.date);
21768 fireKey: function(e)
21770 if (!this.picker().isVisible()){
21771 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21777 var dateChanged = false,
21779 newDate, newViewDate;
21784 e.preventDefault();
21788 if (!this.keyboardNavigation) {
21791 dir = e.keyCode == 37 ? -1 : 1;
21794 newDate = this.moveYear(this.date, dir);
21795 newViewDate = this.moveYear(this.viewDate, dir);
21796 } else if (e.shiftKey){
21797 newDate = this.moveMonth(this.date, dir);
21798 newViewDate = this.moveMonth(this.viewDate, dir);
21800 newDate = new Date(this.date);
21801 newDate.setUTCDate(this.date.getUTCDate() + dir);
21802 newViewDate = new Date(this.viewDate);
21803 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21805 if (this.dateWithinRange(newDate)){
21806 this.date = newDate;
21807 this.viewDate = newViewDate;
21808 this.setValue(this.formatDate(this.date));
21810 e.preventDefault();
21811 dateChanged = true;
21816 if (!this.keyboardNavigation) {
21819 dir = e.keyCode == 38 ? -1 : 1;
21821 newDate = this.moveYear(this.date, dir);
21822 newViewDate = this.moveYear(this.viewDate, dir);
21823 } else if (e.shiftKey){
21824 newDate = this.moveMonth(this.date, dir);
21825 newViewDate = this.moveMonth(this.viewDate, dir);
21827 newDate = new Date(this.date);
21828 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21829 newViewDate = new Date(this.viewDate);
21830 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21832 if (this.dateWithinRange(newDate)){
21833 this.date = newDate;
21834 this.viewDate = newViewDate;
21835 this.setValue(this.formatDate(this.date));
21837 e.preventDefault();
21838 dateChanged = true;
21842 this.setValue(this.formatDate(this.date));
21844 e.preventDefault();
21847 this.setValue(this.formatDate(this.date));
21861 onClick: function(e)
21863 e.stopPropagation();
21864 e.preventDefault();
21866 var target = e.getTarget();
21868 if(target.nodeName.toLowerCase() === 'i'){
21869 target = Roo.get(target).dom.parentNode;
21872 var nodeName = target.nodeName;
21873 var className = target.className;
21874 var html = target.innerHTML;
21875 //Roo.log(nodeName);
21877 switch(nodeName.toLowerCase()) {
21879 switch(className) {
21885 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21886 switch(this.viewMode){
21888 this.viewDate = this.moveMonth(this.viewDate, dir);
21892 this.viewDate = this.moveYear(this.viewDate, dir);
21898 var date = new Date();
21899 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21901 this.setValue(this.formatDate(this.date));
21908 if (className.indexOf('disabled') < 0) {
21909 this.viewDate.setUTCDate(1);
21910 if (className.indexOf('month') > -1) {
21911 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21913 var year = parseInt(html, 10) || 0;
21914 this.viewDate.setUTCFullYear(year);
21918 if(this.singleMode){
21919 this.setValue(this.formatDate(this.viewDate));
21930 //Roo.log(className);
21931 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21932 var day = parseInt(html, 10) || 1;
21933 var year = (this.viewDate || new Date()).getUTCFullYear(),
21934 month = (this.viewDate || new Date()).getUTCMonth();
21936 if (className.indexOf('old') > -1) {
21943 } else if (className.indexOf('new') > -1) {
21951 //Roo.log([year,month,day]);
21952 this.date = this.UTCDate(year, month, day,0,0,0,0);
21953 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21955 //Roo.log(this.formatDate(this.date));
21956 this.setValue(this.formatDate(this.date));
21963 setStartDate: function(startDate)
21965 this.startDate = startDate || -Infinity;
21966 if (this.startDate !== -Infinity) {
21967 this.startDate = this.parseDate(this.startDate);
21970 this.updateNavArrows();
21973 setEndDate: function(endDate)
21975 this.endDate = endDate || Infinity;
21976 if (this.endDate !== Infinity) {
21977 this.endDate = this.parseDate(this.endDate);
21980 this.updateNavArrows();
21983 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21985 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21986 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21987 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21989 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21990 return parseInt(d, 10);
21993 this.updateNavArrows();
21996 updateNavArrows: function()
21998 if(this.singleMode){
22002 var d = new Date(this.viewDate),
22003 year = d.getUTCFullYear(),
22004 month = d.getUTCMonth();
22006 Roo.each(this.picker().select('.prev', true).elements, function(v){
22008 switch (this.viewMode) {
22011 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22017 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22024 Roo.each(this.picker().select('.next', true).elements, function(v){
22026 switch (this.viewMode) {
22029 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22035 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22043 moveMonth: function(date, dir)
22048 var new_date = new Date(date.valueOf()),
22049 day = new_date.getUTCDate(),
22050 month = new_date.getUTCMonth(),
22051 mag = Math.abs(dir),
22053 dir = dir > 0 ? 1 : -1;
22056 // If going back one month, make sure month is not current month
22057 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22059 return new_date.getUTCMonth() == month;
22061 // If going forward one month, make sure month is as expected
22062 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22064 return new_date.getUTCMonth() != new_month;
22066 new_month = month + dir;
22067 new_date.setUTCMonth(new_month);
22068 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22069 if (new_month < 0 || new_month > 11) {
22070 new_month = (new_month + 12) % 12;
22073 // For magnitudes >1, move one month at a time...
22074 for (var i=0; i<mag; i++) {
22075 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22076 new_date = this.moveMonth(new_date, dir);
22078 // ...then reset the day, keeping it in the new month
22079 new_month = new_date.getUTCMonth();
22080 new_date.setUTCDate(day);
22082 return new_month != new_date.getUTCMonth();
22085 // Common date-resetting loop -- if date is beyond end of month, make it
22088 new_date.setUTCDate(--day);
22089 new_date.setUTCMonth(new_month);
22094 moveYear: function(date, dir)
22096 return this.moveMonth(date, dir*12);
22099 dateWithinRange: function(date)
22101 return date >= this.startDate && date <= this.endDate;
22107 this.picker().remove();
22110 validateValue : function(value)
22112 if(this.getVisibilityEl().hasClass('hidden')){
22116 if(value.length < 1) {
22117 if(this.allowBlank){
22123 if(value.length < this.minLength){
22126 if(value.length > this.maxLength){
22130 var vt = Roo.form.VTypes;
22131 if(!vt[this.vtype](value, this)){
22135 if(typeof this.validator == "function"){
22136 var msg = this.validator(value);
22142 if(this.regex && !this.regex.test(value)){
22146 if(typeof(this.parseDate(value)) == 'undefined'){
22150 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22154 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22164 this.date = this.viewDate = '';
22166 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22171 Roo.apply(Roo.bootstrap.DateField, {
22182 html: '<i class="fa fa-arrow-left"/>'
22192 html: '<i class="fa fa-arrow-right"/>'
22234 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22235 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22236 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22237 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22238 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22251 navFnc: 'FullYear',
22256 navFnc: 'FullYear',
22261 Roo.apply(Roo.bootstrap.DateField, {
22265 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22269 cls: 'datepicker-days',
22273 cls: 'table-condensed',
22275 Roo.bootstrap.DateField.head,
22279 Roo.bootstrap.DateField.footer
22286 cls: 'datepicker-months',
22290 cls: 'table-condensed',
22292 Roo.bootstrap.DateField.head,
22293 Roo.bootstrap.DateField.content,
22294 Roo.bootstrap.DateField.footer
22301 cls: 'datepicker-years',
22305 cls: 'table-condensed',
22307 Roo.bootstrap.DateField.head,
22308 Roo.bootstrap.DateField.content,
22309 Roo.bootstrap.DateField.footer
22328 * @class Roo.bootstrap.TimeField
22329 * @extends Roo.bootstrap.Input
22330 * Bootstrap DateField class
22334 * Create a new TimeField
22335 * @param {Object} config The config object
22338 Roo.bootstrap.TimeField = function(config){
22339 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22343 * Fires when this field show.
22344 * @param {Roo.bootstrap.DateField} thisthis
22345 * @param {Mixed} date The date value
22350 * Fires when this field hide.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22357 * Fires when select a date.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22368 * @cfg {String} format
22369 * The default time format string which can be overriden for localization support. The format must be
22370 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22374 getAutoCreate : function()
22376 this.after = '<i class="fa far fa-clock"></i>';
22377 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22381 onRender: function(ct, position)
22384 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22386 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22388 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22390 this.pop = this.picker().select('>.datepicker-time',true).first();
22391 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22393 this.picker().on('mousedown', this.onMousedown, this);
22394 this.picker().on('click', this.onClick, this);
22396 this.picker().addClass('datepicker-dropdown');
22401 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22402 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22403 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22404 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22405 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22406 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22410 fireKey: function(e){
22411 if (!this.picker().isVisible()){
22412 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22418 e.preventDefault();
22426 this.onTogglePeriod();
22429 this.onIncrementMinutes();
22432 this.onDecrementMinutes();
22441 onClick: function(e) {
22442 e.stopPropagation();
22443 e.preventDefault();
22446 picker : function()
22448 return this.pickerEl;
22451 fillTime: function()
22453 var time = this.pop.select('tbody', true).first();
22455 time.dom.innerHTML = '';
22470 cls: 'hours-up fa fas fa-chevron-up'
22490 cls: 'minutes-up fa fas fa-chevron-up'
22511 cls: 'timepicker-hour',
22526 cls: 'timepicker-minute',
22541 cls: 'btn btn-primary period',
22563 cls: 'hours-down fa fas fa-chevron-down'
22583 cls: 'minutes-down fa fas fa-chevron-down'
22601 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22608 var hours = this.time.getHours();
22609 var minutes = this.time.getMinutes();
22622 hours = hours - 12;
22626 hours = '0' + hours;
22630 minutes = '0' + minutes;
22633 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22634 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22635 this.pop.select('button', true).first().dom.innerHTML = period;
22641 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22643 var cls = ['bottom'];
22645 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22652 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22656 //this.picker().setXY(20000,20000);
22657 this.picker().addClass(cls.join('-'));
22661 Roo.each(cls, function(c){
22666 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22667 //_this.picker().setTop(_this.inputEl().getHeight());
22671 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22673 //_this.picker().setTop(0 - _this.picker().getHeight());
22678 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22682 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22690 onFocus : function()
22692 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22696 onBlur : function()
22698 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22704 this.picker().show();
22709 this.fireEvent('show', this, this.date);
22714 this.picker().hide();
22717 this.fireEvent('hide', this, this.date);
22720 setTime : function()
22723 this.setValue(this.time.format(this.format));
22725 this.fireEvent('select', this, this.date);
22730 onMousedown: function(e){
22731 e.stopPropagation();
22732 e.preventDefault();
22735 onIncrementHours: function()
22737 Roo.log('onIncrementHours');
22738 this.time = this.time.add(Date.HOUR, 1);
22743 onDecrementHours: function()
22745 Roo.log('onDecrementHours');
22746 this.time = this.time.add(Date.HOUR, -1);
22750 onIncrementMinutes: function()
22752 Roo.log('onIncrementMinutes');
22753 this.time = this.time.add(Date.MINUTE, 1);
22757 onDecrementMinutes: function()
22759 Roo.log('onDecrementMinutes');
22760 this.time = this.time.add(Date.MINUTE, -1);
22764 onTogglePeriod: function()
22766 Roo.log('onTogglePeriod');
22767 this.time = this.time.add(Date.HOUR, 12);
22775 Roo.apply(Roo.bootstrap.TimeField, {
22779 cls: 'datepicker dropdown-menu',
22783 cls: 'datepicker-time',
22787 cls: 'table-condensed',
22816 cls: 'btn btn-info ok',
22844 * @class Roo.bootstrap.MonthField
22845 * @extends Roo.bootstrap.Input
22846 * Bootstrap MonthField class
22848 * @cfg {String} language default en
22851 * Create a new MonthField
22852 * @param {Object} config The config object
22855 Roo.bootstrap.MonthField = function(config){
22856 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22861 * Fires when this field show.
22862 * @param {Roo.bootstrap.MonthField} this
22863 * @param {Mixed} date The date value
22868 * Fires when this field hide.
22869 * @param {Roo.bootstrap.MonthField} this
22870 * @param {Mixed} date The date value
22875 * Fires when select a date.
22876 * @param {Roo.bootstrap.MonthField} this
22877 * @param {String} oldvalue The old value
22878 * @param {String} newvalue The new value
22884 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22886 onRender: function(ct, position)
22889 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22891 this.language = this.language || 'en';
22892 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22893 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22895 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22896 this.isInline = false;
22897 this.isInput = true;
22898 this.component = this.el.select('.add-on', true).first() || false;
22899 this.component = (this.component && this.component.length === 0) ? false : this.component;
22900 this.hasInput = this.component && this.inputEL().length;
22902 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22904 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22906 this.picker().on('mousedown', this.onMousedown, this);
22907 this.picker().on('click', this.onClick, this);
22909 this.picker().addClass('datepicker-dropdown');
22911 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22912 v.setStyle('width', '189px');
22919 if(this.isInline) {
22925 setValue: function(v, suppressEvent)
22927 var o = this.getValue();
22929 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22933 if(suppressEvent !== true){
22934 this.fireEvent('select', this, o, v);
22939 getValue: function()
22944 onClick: function(e)
22946 e.stopPropagation();
22947 e.preventDefault();
22949 var target = e.getTarget();
22951 if(target.nodeName.toLowerCase() === 'i'){
22952 target = Roo.get(target).dom.parentNode;
22955 var nodeName = target.nodeName;
22956 var className = target.className;
22957 var html = target.innerHTML;
22959 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22963 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22965 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22971 picker : function()
22973 return this.pickerEl;
22976 fillMonths: function()
22979 var months = this.picker().select('>.datepicker-months td', true).first();
22981 months.dom.innerHTML = '';
22987 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22990 months.createChild(month);
22999 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23000 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23003 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23004 e.removeClass('active');
23006 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23007 e.addClass('active');
23014 if(this.isInline) {
23018 this.picker().removeClass(['bottom', 'top']);
23020 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23022 * place to the top of element!
23026 this.picker().addClass('top');
23027 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23032 this.picker().addClass('bottom');
23034 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23037 onFocus : function()
23039 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23043 onBlur : function()
23045 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23047 var d = this.inputEl().getValue();
23056 this.picker().show();
23057 this.picker().select('>.datepicker-months', true).first().show();
23061 this.fireEvent('show', this, this.date);
23066 if(this.isInline) {
23069 this.picker().hide();
23070 this.fireEvent('hide', this, this.date);
23074 onMousedown: function(e)
23076 e.stopPropagation();
23077 e.preventDefault();
23082 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23086 fireKey: function(e)
23088 if (!this.picker().isVisible()){
23089 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23100 e.preventDefault();
23104 dir = e.keyCode == 37 ? -1 : 1;
23106 this.vIndex = this.vIndex + dir;
23108 if(this.vIndex < 0){
23112 if(this.vIndex > 11){
23116 if(isNaN(this.vIndex)){
23120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23126 dir = e.keyCode == 38 ? -1 : 1;
23128 this.vIndex = this.vIndex + dir * 4;
23130 if(this.vIndex < 0){
23134 if(this.vIndex > 11){
23138 if(isNaN(this.vIndex)){
23142 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23147 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23148 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23152 e.preventDefault();
23155 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23156 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23172 this.picker().remove();
23177 Roo.apply(Roo.bootstrap.MonthField, {
23196 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23197 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23202 Roo.apply(Roo.bootstrap.MonthField, {
23206 cls: 'datepicker dropdown-menu roo-dynamic',
23210 cls: 'datepicker-months',
23214 cls: 'table-condensed',
23216 Roo.bootstrap.DateField.content
23236 * @class Roo.bootstrap.CheckBox
23237 * @extends Roo.bootstrap.Input
23238 * Bootstrap CheckBox class
23240 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23241 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23242 * @cfg {String} boxLabel The text that appears beside the checkbox
23243 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23244 * @cfg {Boolean} checked initnal the element
23245 * @cfg {Boolean} inline inline the element (default false)
23246 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23247 * @cfg {String} tooltip label tooltip
23250 * Create a new CheckBox
23251 * @param {Object} config The config object
23254 Roo.bootstrap.CheckBox = function(config){
23255 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23260 * Fires when the element is checked or unchecked.
23261 * @param {Roo.bootstrap.CheckBox} this This input
23262 * @param {Boolean} checked The new checked value
23267 * Fires when the element is click.
23268 * @param {Roo.bootstrap.CheckBox} this This input
23275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23277 inputType: 'checkbox',
23286 // checkbox success does not make any sense really..
23291 getAutoCreate : function()
23293 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23299 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23302 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23308 type : this.inputType,
23309 value : this.inputValue,
23310 cls : 'roo-' + this.inputType, //'form-box',
23311 placeholder : this.placeholder || ''
23315 if(this.inputType != 'radio'){
23319 cls : 'roo-hidden-value',
23320 value : this.checked ? this.inputValue : this.valueOff
23325 if (this.weight) { // Validity check?
23326 cfg.cls += " " + this.inputType + "-" + this.weight;
23329 if (this.disabled) {
23330 input.disabled=true;
23334 input.checked = this.checked;
23339 input.name = this.name;
23341 if(this.inputType != 'radio'){
23342 hidden.name = this.name;
23343 input.name = '_hidden_' + this.name;
23348 input.cls += ' input-' + this.size;
23353 ['xs','sm','md','lg'].map(function(size){
23354 if (settings[size]) {
23355 cfg.cls += ' col-' + size + '-' + settings[size];
23359 var inputblock = input;
23361 if (this.before || this.after) {
23364 cls : 'input-group',
23369 inputblock.cn.push({
23371 cls : 'input-group-addon',
23376 inputblock.cn.push(input);
23378 if(this.inputType != 'radio'){
23379 inputblock.cn.push(hidden);
23383 inputblock.cn.push({
23385 cls : 'input-group-addon',
23391 var boxLabelCfg = false;
23397 //'for': id, // box label is handled by onclick - so no for...
23399 html: this.boxLabel
23402 boxLabelCfg.tooltip = this.tooltip;
23408 if (align ==='left' && this.fieldLabel.length) {
23409 // Roo.log("left and has label");
23414 cls : 'control-label',
23415 html : this.fieldLabel
23426 cfg.cn[1].cn.push(boxLabelCfg);
23429 if(this.labelWidth > 12){
23430 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23433 if(this.labelWidth < 13 && this.labelmd == 0){
23434 this.labelmd = this.labelWidth;
23437 if(this.labellg > 0){
23438 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23439 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23442 if(this.labelmd > 0){
23443 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23444 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23447 if(this.labelsm > 0){
23448 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23449 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23452 if(this.labelxs > 0){
23453 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23454 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23457 } else if ( this.fieldLabel.length) {
23458 // Roo.log(" label");
23462 tag: this.boxLabel ? 'span' : 'label',
23464 cls: 'control-label box-input-label',
23465 //cls : 'input-group-addon',
23466 html : this.fieldLabel
23473 cfg.cn.push(boxLabelCfg);
23478 // Roo.log(" no label && no align");
23479 cfg.cn = [ inputblock ] ;
23481 cfg.cn.push(boxLabelCfg);
23489 if(this.inputType != 'radio'){
23490 cfg.cn.push(hidden);
23498 * return the real input element.
23500 inputEl: function ()
23502 return this.el.select('input.roo-' + this.inputType,true).first();
23504 hiddenEl: function ()
23506 return this.el.select('input.roo-hidden-value',true).first();
23509 labelEl: function()
23511 return this.el.select('label.control-label',true).first();
23513 /* depricated... */
23517 return this.labelEl();
23520 boxLabelEl: function()
23522 return this.el.select('label.box-label',true).first();
23525 initEvents : function()
23527 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23529 this.inputEl().on('click', this.onClick, this);
23531 if (this.boxLabel) {
23532 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23535 this.startValue = this.getValue();
23538 Roo.bootstrap.CheckBox.register(this);
23542 onClick : function(e)
23544 if(this.fireEvent('click', this, e) !== false){
23545 this.setChecked(!this.checked);
23550 setChecked : function(state,suppressEvent)
23552 this.startValue = this.getValue();
23554 if(this.inputType == 'radio'){
23556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557 e.dom.checked = false;
23560 this.inputEl().dom.checked = true;
23562 this.inputEl().dom.value = this.inputValue;
23564 if(suppressEvent !== true){
23565 this.fireEvent('check', this, true);
23573 this.checked = state;
23575 this.inputEl().dom.checked = state;
23578 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23580 if(suppressEvent !== true){
23581 this.fireEvent('check', this, state);
23587 getValue : function()
23589 if(this.inputType == 'radio'){
23590 return this.getGroupValue();
23593 return this.hiddenEl().dom.value;
23597 getGroupValue : function()
23599 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23603 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23606 setValue : function(v,suppressEvent)
23608 if(this.inputType == 'radio'){
23609 this.setGroupValue(v, suppressEvent);
23613 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23618 setGroupValue : function(v, suppressEvent)
23620 this.startValue = this.getValue();
23622 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623 e.dom.checked = false;
23625 if(e.dom.value == v){
23626 e.dom.checked = true;
23630 if(suppressEvent !== true){
23631 this.fireEvent('check', this, true);
23639 validate : function()
23641 if(this.getVisibilityEl().hasClass('hidden')){
23647 (this.inputType == 'radio' && this.validateRadio()) ||
23648 (this.inputType == 'checkbox' && this.validateCheckbox())
23654 this.markInvalid();
23658 validateRadio : function()
23660 if(this.getVisibilityEl().hasClass('hidden')){
23664 if(this.allowBlank){
23670 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671 if(!e.dom.checked){
23683 validateCheckbox : function()
23686 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23687 //return (this.getValue() == this.inputValue) ? true : false;
23690 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23698 for(var i in group){
23699 if(group[i].el.isVisible(true)){
23707 for(var i in group){
23712 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23719 * Mark this field as valid
23721 markValid : function()
23725 this.fireEvent('valid', this);
23727 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23730 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23737 if(this.inputType == 'radio'){
23738 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23739 var fg = e.findParent('.form-group', false, true);
23740 if (Roo.bootstrap.version == 3) {
23741 fg.removeClass([_this.invalidClass, _this.validClass]);
23742 fg.addClass(_this.validClass);
23744 fg.removeClass(['is-valid', 'is-invalid']);
23745 fg.addClass('is-valid');
23753 var fg = this.el.findParent('.form-group', false, true);
23754 if (Roo.bootstrap.version == 3) {
23755 fg.removeClass([this.invalidClass, this.validClass]);
23756 fg.addClass(this.validClass);
23758 fg.removeClass(['is-valid', 'is-invalid']);
23759 fg.addClass('is-valid');
23764 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23770 for(var i in group){
23771 var fg = group[i].el.findParent('.form-group', false, true);
23772 if (Roo.bootstrap.version == 3) {
23773 fg.removeClass([this.invalidClass, this.validClass]);
23774 fg.addClass(this.validClass);
23776 fg.removeClass(['is-valid', 'is-invalid']);
23777 fg.addClass('is-valid');
23783 * Mark this field as invalid
23784 * @param {String} msg The validation message
23786 markInvalid : function(msg)
23788 if(this.allowBlank){
23794 this.fireEvent('invalid', this, msg);
23796 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23799 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23803 label.markInvalid();
23806 if(this.inputType == 'radio'){
23808 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23809 var fg = e.findParent('.form-group', false, true);
23810 if (Roo.bootstrap.version == 3) {
23811 fg.removeClass([_this.invalidClass, _this.validClass]);
23812 fg.addClass(_this.invalidClass);
23814 fg.removeClass(['is-invalid', 'is-valid']);
23815 fg.addClass('is-invalid');
23823 var fg = this.el.findParent('.form-group', false, true);
23824 if (Roo.bootstrap.version == 3) {
23825 fg.removeClass([_this.invalidClass, _this.validClass]);
23826 fg.addClass(_this.invalidClass);
23828 fg.removeClass(['is-invalid', 'is-valid']);
23829 fg.addClass('is-invalid');
23834 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23840 for(var i in group){
23841 var fg = group[i].el.findParent('.form-group', false, true);
23842 if (Roo.bootstrap.version == 3) {
23843 fg.removeClass([_this.invalidClass, _this.validClass]);
23844 fg.addClass(_this.invalidClass);
23846 fg.removeClass(['is-invalid', 'is-valid']);
23847 fg.addClass('is-invalid');
23853 clearInvalid : function()
23855 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23857 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23859 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23861 if (label && label.iconEl) {
23862 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23863 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23867 disable : function()
23869 if(this.inputType != 'radio'){
23870 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23877 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878 _this.getActionEl().addClass(this.disabledClass);
23879 e.dom.disabled = true;
23883 this.disabled = true;
23884 this.fireEvent("disable", this);
23888 enable : function()
23890 if(this.inputType != 'radio'){
23891 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23898 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23899 _this.getActionEl().removeClass(this.disabledClass);
23900 e.dom.disabled = false;
23904 this.disabled = false;
23905 this.fireEvent("enable", this);
23909 setBoxLabel : function(v)
23914 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23920 Roo.apply(Roo.bootstrap.CheckBox, {
23925 * register a CheckBox Group
23926 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23928 register : function(checkbox)
23930 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23931 this.groups[checkbox.groupId] = {};
23934 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23938 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23942 * fetch a CheckBox Group based on the group ID
23943 * @param {string} the group ID
23944 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23946 get: function(groupId) {
23947 if (typeof(this.groups[groupId]) == 'undefined') {
23951 return this.groups[groupId] ;
23964 * @class Roo.bootstrap.Radio
23965 * @extends Roo.bootstrap.Component
23966 * Bootstrap Radio class
23967 * @cfg {String} boxLabel - the label associated
23968 * @cfg {String} value - the value of radio
23971 * Create a new Radio
23972 * @param {Object} config The config object
23974 Roo.bootstrap.Radio = function(config){
23975 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23979 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23985 getAutoCreate : function()
23989 cls : 'form-group radio',
23994 html : this.boxLabel
24002 initEvents : function()
24004 this.parent().register(this);
24006 this.el.on('click', this.onClick, this);
24010 onClick : function(e)
24012 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24013 this.setChecked(true);
24017 setChecked : function(state, suppressEvent)
24019 this.parent().setValue(this.value, suppressEvent);
24023 setBoxLabel : function(v)
24028 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24043 * @class Roo.bootstrap.SecurePass
24044 * @extends Roo.bootstrap.Input
24045 * Bootstrap SecurePass class
24049 * Create a new SecurePass
24050 * @param {Object} config The config object
24053 Roo.bootstrap.SecurePass = function (config) {
24054 // these go here, so the translation tool can replace them..
24056 PwdEmpty: "Please type a password, and then retype it to confirm.",
24057 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24058 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24059 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24060 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24061 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24062 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24063 TooWeak: "Your password is Too Weak."
24065 this.meterLabel = "Password strength:";
24066 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24067 this.meterClass = [
24068 "roo-password-meter-tooweak",
24069 "roo-password-meter-weak",
24070 "roo-password-meter-medium",
24071 "roo-password-meter-strong",
24072 "roo-password-meter-grey"
24077 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24080 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24082 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24084 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24085 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24086 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24087 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24088 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24089 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24090 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24100 * @cfg {String/Object} Label for the strength meter (defaults to
24101 * 'Password strength:')
24106 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24107 * ['Weak', 'Medium', 'Strong'])
24110 pwdStrengths: false,
24123 initEvents: function ()
24125 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24127 if (this.el.is('input[type=password]') && Roo.isSafari) {
24128 this.el.on('keydown', this.SafariOnKeyDown, this);
24131 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24134 onRender: function (ct, position)
24136 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24137 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24138 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24140 this.trigger.createChild({
24145 cls: 'roo-password-meter-grey col-xs-12',
24148 //width: this.meterWidth + 'px'
24152 cls: 'roo-password-meter-text'
24158 if (this.hideTrigger) {
24159 this.trigger.setDisplayed(false);
24161 this.setSize(this.width || '', this.height || '');
24164 onDestroy: function ()
24166 if (this.trigger) {
24167 this.trigger.removeAllListeners();
24168 this.trigger.remove();
24171 this.wrap.remove();
24173 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24176 checkStrength: function ()
24178 var pwd = this.inputEl().getValue();
24179 if (pwd == this._lastPwd) {
24184 if (this.ClientSideStrongPassword(pwd)) {
24186 } else if (this.ClientSideMediumPassword(pwd)) {
24188 } else if (this.ClientSideWeakPassword(pwd)) {
24194 Roo.log('strength1: ' + strength);
24196 //var pm = this.trigger.child('div/div/div').dom;
24197 var pm = this.trigger.child('div/div');
24198 pm.removeClass(this.meterClass);
24199 pm.addClass(this.meterClass[strength]);
24202 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24204 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24206 this._lastPwd = pwd;
24210 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24212 this._lastPwd = '';
24214 var pm = this.trigger.child('div/div');
24215 pm.removeClass(this.meterClass);
24216 pm.addClass('roo-password-meter-grey');
24219 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24222 this.inputEl().dom.type='password';
24225 validateValue: function (value)
24227 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24230 if (value.length == 0) {
24231 if (this.allowBlank) {
24232 this.clearInvalid();
24236 this.markInvalid(this.errors.PwdEmpty);
24237 this.errorMsg = this.errors.PwdEmpty;
24245 if (!value.match(/[\x21-\x7e]+/)) {
24246 this.markInvalid(this.errors.PwdBadChar);
24247 this.errorMsg = this.errors.PwdBadChar;
24250 if (value.length < 6) {
24251 this.markInvalid(this.errors.PwdShort);
24252 this.errorMsg = this.errors.PwdShort;
24255 if (value.length > 16) {
24256 this.markInvalid(this.errors.PwdLong);
24257 this.errorMsg = this.errors.PwdLong;
24261 if (this.ClientSideStrongPassword(value)) {
24263 } else if (this.ClientSideMediumPassword(value)) {
24265 } else if (this.ClientSideWeakPassword(value)) {
24272 if (strength < 2) {
24273 //this.markInvalid(this.errors.TooWeak);
24274 this.errorMsg = this.errors.TooWeak;
24279 console.log('strength2: ' + strength);
24281 //var pm = this.trigger.child('div/div/div').dom;
24283 var pm = this.trigger.child('div/div');
24284 pm.removeClass(this.meterClass);
24285 pm.addClass(this.meterClass[strength]);
24287 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24289 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24291 this.errorMsg = '';
24295 CharacterSetChecks: function (type)
24298 this.fResult = false;
24301 isctype: function (character, type)
24304 case this.kCapitalLetter:
24305 if (character >= 'A' && character <= 'Z') {
24310 case this.kSmallLetter:
24311 if (character >= 'a' && character <= 'z') {
24317 if (character >= '0' && character <= '9') {
24322 case this.kPunctuation:
24323 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24334 IsLongEnough: function (pwd, size)
24336 return !(pwd == null || isNaN(size) || pwd.length < size);
24339 SpansEnoughCharacterSets: function (word, nb)
24341 if (!this.IsLongEnough(word, nb))
24346 var characterSetChecks = new Array(
24347 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24348 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24351 for (var index = 0; index < word.length; ++index) {
24352 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24353 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24354 characterSetChecks[nCharSet].fResult = true;
24361 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362 if (characterSetChecks[nCharSet].fResult) {
24367 if (nCharSets < nb) {
24373 ClientSideStrongPassword: function (pwd)
24375 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24378 ClientSideMediumPassword: function (pwd)
24380 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24383 ClientSideWeakPassword: function (pwd)
24385 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24388 })//<script type="text/javascript">
24391 * Based Ext JS Library 1.1.1
24392 * Copyright(c) 2006-2007, Ext JS, LLC.
24398 * @class Roo.HtmlEditorCore
24399 * @extends Roo.Component
24400 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24402 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24405 Roo.HtmlEditorCore = function(config){
24408 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24413 * @event initialize
24414 * Fires when the editor is fully initialized (including the iframe)
24415 * @param {Roo.HtmlEditorCore} this
24420 * Fires when the editor is first receives the focus. Any insertion must wait
24421 * until after this event.
24422 * @param {Roo.HtmlEditorCore} this
24426 * @event beforesync
24427 * Fires before the textarea is updated with content from the editor iframe. Return false
24428 * to cancel the sync.
24429 * @param {Roo.HtmlEditorCore} this
24430 * @param {String} html
24434 * @event beforepush
24435 * Fires before the iframe editor is updated with content from the textarea. Return false
24436 * to cancel the push.
24437 * @param {Roo.HtmlEditorCore} this
24438 * @param {String} html
24443 * Fires when the textarea is updated with content from the editor iframe.
24444 * @param {Roo.HtmlEditorCore} this
24445 * @param {String} html
24450 * Fires when the iframe editor is updated with content from the textarea.
24451 * @param {Roo.HtmlEditorCore} this
24452 * @param {String} html
24457 * @event editorevent
24458 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24459 * @param {Roo.HtmlEditorCore} this
24465 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24467 // defaults : white / black...
24468 this.applyBlacklists();
24475 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24479 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24485 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24490 * @cfg {Number} height (in pixels)
24494 * @cfg {Number} width (in pixels)
24499 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24502 stylesheets: false,
24507 // private properties
24508 validationEvent : false,
24510 initialized : false,
24512 sourceEditMode : false,
24513 onFocus : Roo.emptyFn,
24515 hideMode:'offsets',
24519 // blacklist + whitelisted elements..
24526 * Protected method that will not generally be called directly. It
24527 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24528 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24530 getDocMarkup : function(){
24534 // inherit styels from page...??
24535 if (this.stylesheets === false) {
24537 Roo.get(document.head).select('style').each(function(node) {
24538 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24541 Roo.get(document.head).select('link').each(function(node) {
24542 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24545 } else if (!this.stylesheets.length) {
24547 st = '<style type="text/css">' +
24548 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24551 for (var i in this.stylesheets) {
24552 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24557 st += '<style type="text/css">' +
24558 'IMG { cursor: pointer } ' +
24561 var cls = 'roo-htmleditor-body';
24563 if(this.bodyCls.length){
24564 cls += ' ' + this.bodyCls;
24567 return '<html><head>' + st +
24568 //<style type="text/css">' +
24569 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24571 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24575 onRender : function(ct, position)
24578 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24579 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24582 this.el.dom.style.border = '0 none';
24583 this.el.dom.setAttribute('tabIndex', -1);
24584 this.el.addClass('x-hidden hide');
24588 if(Roo.isIE){ // fix IE 1px bogus margin
24589 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24593 this.frameId = Roo.id();
24597 var iframe = this.owner.wrap.createChild({
24599 cls: 'form-control', // bootstrap..
24601 name: this.frameId,
24602 frameBorder : 'no',
24603 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24608 this.iframe = iframe.dom;
24610 this.assignDocWin();
24612 this.doc.designMode = 'on';
24615 this.doc.write(this.getDocMarkup());
24619 var task = { // must defer to wait for browser to be ready
24621 //console.log("run task?" + this.doc.readyState);
24622 this.assignDocWin();
24623 if(this.doc.body || this.doc.readyState == 'complete'){
24625 this.doc.designMode="on";
24629 Roo.TaskMgr.stop(task);
24630 this.initEditor.defer(10, this);
24637 Roo.TaskMgr.start(task);
24642 onResize : function(w, h)
24644 Roo.log('resize: ' +w + ',' + h );
24645 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24649 if(typeof w == 'number'){
24651 this.iframe.style.width = w + 'px';
24653 if(typeof h == 'number'){
24655 this.iframe.style.height = h + 'px';
24657 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24664 * Toggles the editor between standard and source edit mode.
24665 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24667 toggleSourceEdit : function(sourceEditMode){
24669 this.sourceEditMode = sourceEditMode === true;
24671 if(this.sourceEditMode){
24673 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24676 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24677 //this.iframe.className = '';
24680 //this.setSize(this.owner.wrap.getSize());
24681 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24688 * Protected method that will not generally be called directly. If you need/want
24689 * custom HTML cleanup, this is the method you should override.
24690 * @param {String} html The HTML to be cleaned
24691 * return {String} The cleaned HTML
24693 cleanHtml : function(html){
24694 html = String(html);
24695 if(html.length > 5){
24696 if(Roo.isSafari){ // strip safari nonsense
24697 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24700 if(html == ' '){
24707 * HTML Editor -> Textarea
24708 * Protected method that will not generally be called directly. Syncs the contents
24709 * of the editor iframe with the textarea.
24711 syncValue : function(){
24712 if(this.initialized){
24713 var bd = (this.doc.body || this.doc.documentElement);
24714 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24715 var html = bd.innerHTML;
24717 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24718 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24720 html = '<div style="'+m[0]+'">' + html + '</div>';
24723 html = this.cleanHtml(html);
24724 // fix up the special chars.. normaly like back quotes in word...
24725 // however we do not want to do this with chinese..
24726 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24728 var cc = match.charCodeAt();
24730 // Get the character value, handling surrogate pairs
24731 if (match.length == 2) {
24732 // It's a surrogate pair, calculate the Unicode code point
24733 var high = match.charCodeAt(0) - 0xD800;
24734 var low = match.charCodeAt(1) - 0xDC00;
24735 cc = (high * 0x400) + low + 0x10000;
24737 (cc >= 0x4E00 && cc < 0xA000 ) ||
24738 (cc >= 0x3400 && cc < 0x4E00 ) ||
24739 (cc >= 0xf900 && cc < 0xfb00 )
24744 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24745 return "&#" + cc + ";";
24752 if(this.owner.fireEvent('beforesync', this, html) !== false){
24753 this.el.dom.value = html;
24754 this.owner.fireEvent('sync', this, html);
24760 * Protected method that will not generally be called directly. Pushes the value of the textarea
24761 * into the iframe editor.
24763 pushValue : function(){
24764 if(this.initialized){
24765 var v = this.el.dom.value.trim();
24767 // if(v.length < 1){
24771 if(this.owner.fireEvent('beforepush', this, v) !== false){
24772 var d = (this.doc.body || this.doc.documentElement);
24774 this.cleanUpPaste();
24775 this.el.dom.value = d.innerHTML;
24776 this.owner.fireEvent('push', this, v);
24782 deferFocus : function(){
24783 this.focus.defer(10, this);
24787 focus : function(){
24788 if(this.win && !this.sourceEditMode){
24795 assignDocWin: function()
24797 var iframe = this.iframe;
24800 this.doc = iframe.contentWindow.document;
24801 this.win = iframe.contentWindow;
24803 // if (!Roo.get(this.frameId)) {
24806 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24807 // this.win = Roo.get(this.frameId).dom.contentWindow;
24809 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24813 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24819 initEditor : function(){
24820 //console.log("INIT EDITOR");
24821 this.assignDocWin();
24825 this.doc.designMode="on";
24827 this.doc.write(this.getDocMarkup());
24830 var dbody = (this.doc.body || this.doc.documentElement);
24831 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24832 // this copies styles from the containing element into thsi one..
24833 // not sure why we need all of this..
24834 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24836 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24837 //ss['background-attachment'] = 'fixed'; // w3c
24838 dbody.bgProperties = 'fixed'; // ie
24839 //Roo.DomHelper.applyStyles(dbody, ss);
24840 Roo.EventManager.on(this.doc, {
24841 //'mousedown': this.onEditorEvent,
24842 'mouseup': this.onEditorEvent,
24843 'dblclick': this.onEditorEvent,
24844 'click': this.onEditorEvent,
24845 'keyup': this.onEditorEvent,
24850 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24852 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24853 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24855 this.initialized = true;
24857 this.owner.fireEvent('initialize', this);
24862 onDestroy : function(){
24868 //for (var i =0; i < this.toolbars.length;i++) {
24869 // // fixme - ask toolbars for heights?
24870 // this.toolbars[i].onDestroy();
24873 //this.wrap.dom.innerHTML = '';
24874 //this.wrap.remove();
24879 onFirstFocus : function(){
24881 this.assignDocWin();
24884 this.activated = true;
24887 if(Roo.isGecko){ // prevent silly gecko errors
24889 var s = this.win.getSelection();
24890 if(!s.focusNode || s.focusNode.nodeType != 3){
24891 var r = s.getRangeAt(0);
24892 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24897 this.execCmd('useCSS', true);
24898 this.execCmd('styleWithCSS', false);
24901 this.owner.fireEvent('activate', this);
24905 adjustFont: function(btn){
24906 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24907 //if(Roo.isSafari){ // safari
24910 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24911 if(Roo.isSafari){ // safari
24912 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24913 v = (v < 10) ? 10 : v;
24914 v = (v > 48) ? 48 : v;
24915 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24920 v = Math.max(1, v+adjust);
24922 this.execCmd('FontSize', v );
24925 onEditorEvent : function(e)
24927 this.owner.fireEvent('editorevent', this, e);
24928 // this.updateToolbar();
24929 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24932 insertTag : function(tg)
24934 // could be a bit smarter... -> wrap the current selected tRoo..
24935 if (tg.toLowerCase() == 'span' ||
24936 tg.toLowerCase() == 'code' ||
24937 tg.toLowerCase() == 'sup' ||
24938 tg.toLowerCase() == 'sub'
24941 range = this.createRange(this.getSelection());
24942 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24943 wrappingNode.appendChild(range.extractContents());
24944 range.insertNode(wrappingNode);
24951 this.execCmd("formatblock", tg);
24955 insertText : function(txt)
24959 var range = this.createRange();
24960 range.deleteContents();
24961 //alert(Sender.getAttribute('label'));
24963 range.insertNode(this.doc.createTextNode(txt));
24969 * Executes a Midas editor command on the editor document and performs necessary focus and
24970 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24971 * @param {String} cmd The Midas command
24972 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24974 relayCmd : function(cmd, value){
24976 this.execCmd(cmd, value);
24977 this.owner.fireEvent('editorevent', this);
24978 //this.updateToolbar();
24979 this.owner.deferFocus();
24983 * Executes a Midas editor command directly on the editor document.
24984 * For visual commands, you should use {@link #relayCmd} instead.
24985 * <b>This should only be called after the editor is initialized.</b>
24986 * @param {String} cmd The Midas command
24987 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24989 execCmd : function(cmd, value){
24990 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24997 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24999 * @param {String} text | dom node..
25001 insertAtCursor : function(text)
25004 if(!this.activated){
25010 var r = this.doc.selection.createRange();
25021 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25025 // from jquery ui (MIT licenced)
25027 var win = this.win;
25029 if (win.getSelection && win.getSelection().getRangeAt) {
25030 range = win.getSelection().getRangeAt(0);
25031 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25032 range.insertNode(node);
25033 } else if (win.document.selection && win.document.selection.createRange) {
25034 // no firefox support
25035 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25036 win.document.selection.createRange().pasteHTML(txt);
25038 // no firefox support
25039 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25040 this.execCmd('InsertHTML', txt);
25049 mozKeyPress : function(e){
25051 var c = e.getCharCode(), cmd;
25054 c = String.fromCharCode(c).toLowerCase();
25068 this.cleanUpPaste.defer(100, this);
25076 e.preventDefault();
25084 fixKeys : function(){ // load time branching for fastest keydown performance
25086 return function(e){
25087 var k = e.getKey(), r;
25090 r = this.doc.selection.createRange();
25093 r.pasteHTML('    ');
25100 r = this.doc.selection.createRange();
25102 var target = r.parentElement();
25103 if(!target || target.tagName.toLowerCase() != 'li'){
25105 r.pasteHTML('<br />');
25111 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25112 this.cleanUpPaste.defer(100, this);
25118 }else if(Roo.isOpera){
25119 return function(e){
25120 var k = e.getKey();
25124 this.execCmd('InsertHTML','    ');
25127 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128 this.cleanUpPaste.defer(100, this);
25133 }else if(Roo.isSafari){
25134 return function(e){
25135 var k = e.getKey();
25139 this.execCmd('InsertText','\t');
25143 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144 this.cleanUpPaste.defer(100, this);
25152 getAllAncestors: function()
25154 var p = this.getSelectedNode();
25157 a.push(p); // push blank onto stack..
25158 p = this.getParentElement();
25162 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25166 a.push(this.doc.body);
25170 lastSelNode : false,
25173 getSelection : function()
25175 this.assignDocWin();
25176 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25179 getSelectedNode: function()
25181 // this may only work on Gecko!!!
25183 // should we cache this!!!!
25188 var range = this.createRange(this.getSelection()).cloneRange();
25191 var parent = range.parentElement();
25193 var testRange = range.duplicate();
25194 testRange.moveToElementText(parent);
25195 if (testRange.inRange(range)) {
25198 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25201 parent = parent.parentElement;
25206 // is ancestor a text element.
25207 var ac = range.commonAncestorContainer;
25208 if (ac.nodeType == 3) {
25209 ac = ac.parentNode;
25212 var ar = ac.childNodes;
25215 var other_nodes = [];
25216 var has_other_nodes = false;
25217 for (var i=0;i<ar.length;i++) {
25218 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25221 // fullly contained node.
25223 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25228 // probably selected..
25229 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25230 other_nodes.push(ar[i]);
25234 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25239 has_other_nodes = true;
25241 if (!nodes.length && other_nodes.length) {
25242 nodes= other_nodes;
25244 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25250 createRange: function(sel)
25252 // this has strange effects when using with
25253 // top toolbar - not sure if it's a great idea.
25254 //this.editor.contentWindow.focus();
25255 if (typeof sel != "undefined") {
25257 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25259 return this.doc.createRange();
25262 return this.doc.createRange();
25265 getParentElement: function()
25268 this.assignDocWin();
25269 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25271 var range = this.createRange(sel);
25274 var p = range.commonAncestorContainer;
25275 while (p.nodeType == 3) { // text node
25286 * Range intersection.. the hard stuff...
25290 * [ -- selected range --- ]
25294 * if end is before start or hits it. fail.
25295 * if start is after end or hits it fail.
25297 * if either hits (but other is outside. - then it's not
25303 // @see http://www.thismuchiknow.co.uk/?p=64.
25304 rangeIntersectsNode : function(range, node)
25306 var nodeRange = node.ownerDocument.createRange();
25308 nodeRange.selectNode(node);
25310 nodeRange.selectNodeContents(node);
25313 var rangeStartRange = range.cloneRange();
25314 rangeStartRange.collapse(true);
25316 var rangeEndRange = range.cloneRange();
25317 rangeEndRange.collapse(false);
25319 var nodeStartRange = nodeRange.cloneRange();
25320 nodeStartRange.collapse(true);
25322 var nodeEndRange = nodeRange.cloneRange();
25323 nodeEndRange.collapse(false);
25325 return rangeStartRange.compareBoundaryPoints(
25326 Range.START_TO_START, nodeEndRange) == -1 &&
25327 rangeEndRange.compareBoundaryPoints(
25328 Range.START_TO_START, nodeStartRange) == 1;
25332 rangeCompareNode : function(range, node)
25334 var nodeRange = node.ownerDocument.createRange();
25336 nodeRange.selectNode(node);
25338 nodeRange.selectNodeContents(node);
25342 range.collapse(true);
25344 nodeRange.collapse(true);
25346 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25347 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25349 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25351 var nodeIsBefore = ss == 1;
25352 var nodeIsAfter = ee == -1;
25354 if (nodeIsBefore && nodeIsAfter) {
25357 if (!nodeIsBefore && nodeIsAfter) {
25358 return 1; //right trailed.
25361 if (nodeIsBefore && !nodeIsAfter) {
25362 return 2; // left trailed.
25368 // private? - in a new class?
25369 cleanUpPaste : function()
25371 // cleans up the whole document..
25372 Roo.log('cleanuppaste');
25374 this.cleanUpChildren(this.doc.body);
25375 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25376 if (clean != this.doc.body.innerHTML) {
25377 this.doc.body.innerHTML = clean;
25382 cleanWordChars : function(input) {// change the chars to hex code
25383 var he = Roo.HtmlEditorCore;
25385 var output = input;
25386 Roo.each(he.swapCodes, function(sw) {
25387 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25389 output = output.replace(swapper, sw[1]);
25396 cleanUpChildren : function (n)
25398 if (!n.childNodes.length) {
25401 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25402 this.cleanUpChild(n.childNodes[i]);
25409 cleanUpChild : function (node)
25412 //console.log(node);
25413 if (node.nodeName == "#text") {
25414 // clean up silly Windows -- stuff?
25417 if (node.nodeName == "#comment") {
25418 node.parentNode.removeChild(node);
25419 // clean up silly Windows -- stuff?
25422 var lcname = node.tagName.toLowerCase();
25423 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25424 // whitelist of tags..
25426 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25428 node.parentNode.removeChild(node);
25433 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25435 // spans with no attributes - just remove them..
25436 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25437 remove_keep_children = true;
25440 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25441 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25443 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25444 // remove_keep_children = true;
25447 if (remove_keep_children) {
25448 this.cleanUpChildren(node);
25449 // inserts everything just before this node...
25450 while (node.childNodes.length) {
25451 var cn = node.childNodes[0];
25452 node.removeChild(cn);
25453 node.parentNode.insertBefore(cn, node);
25455 node.parentNode.removeChild(node);
25459 if (!node.attributes || !node.attributes.length) {
25464 this.cleanUpChildren(node);
25468 function cleanAttr(n,v)
25471 if (v.match(/^\./) || v.match(/^\//)) {
25474 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25477 if (v.match(/^#/)) {
25480 if (v.match(/^\{/)) { // allow template editing.
25483 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25484 node.removeAttribute(n);
25488 var cwhite = this.cwhite;
25489 var cblack = this.cblack;
25491 function cleanStyle(n,v)
25493 if (v.match(/expression/)) { //XSS?? should we even bother..
25494 node.removeAttribute(n);
25498 var parts = v.split(/;/);
25501 Roo.each(parts, function(p) {
25502 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25506 var l = p.split(':').shift().replace(/\s+/g,'');
25507 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25509 if ( cwhite.length && cblack.indexOf(l) > -1) {
25510 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25511 //node.removeAttribute(n);
25515 // only allow 'c whitelisted system attributes'
25516 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25517 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518 //node.removeAttribute(n);
25528 if (clean.length) {
25529 node.setAttribute(n, clean.join(';'));
25531 node.removeAttribute(n);
25537 for (var i = node.attributes.length-1; i > -1 ; i--) {
25538 var a = node.attributes[i];
25541 if (a.name.toLowerCase().substr(0,2)=='on') {
25542 node.removeAttribute(a.name);
25545 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25546 node.removeAttribute(a.name);
25549 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25550 cleanAttr(a.name,a.value); // fixme..
25553 if (a.name == 'style') {
25554 cleanStyle(a.name,a.value);
25557 /// clean up MS crap..
25558 // tecnically this should be a list of valid class'es..
25561 if (a.name == 'class') {
25562 if (a.value.match(/^Mso/)) {
25563 node.removeAttribute('class');
25566 if (a.value.match(/^body$/)) {
25567 node.removeAttribute('class');
25578 this.cleanUpChildren(node);
25584 * Clean up MS wordisms...
25586 cleanWord : function(node)
25589 this.cleanWord(this.doc.body);
25594 node.nodeName == 'SPAN' &&
25595 !node.hasAttributes() &&
25596 node.childNodes.length == 1 &&
25597 node.firstChild.nodeName == "#text"
25599 var textNode = node.firstChild;
25600 node.removeChild(textNode);
25601 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25602 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25604 node.parentNode.insertBefore(textNode, node);
25605 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25606 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25608 node.parentNode.removeChild(node);
25611 if (node.nodeName == "#text") {
25612 // clean up silly Windows -- stuff?
25615 if (node.nodeName == "#comment") {
25616 node.parentNode.removeChild(node);
25617 // clean up silly Windows -- stuff?
25621 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25622 node.parentNode.removeChild(node);
25625 //Roo.log(node.tagName);
25626 // remove - but keep children..
25627 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25628 //Roo.log('-- removed');
25629 while (node.childNodes.length) {
25630 var cn = node.childNodes[0];
25631 node.removeChild(cn);
25632 node.parentNode.insertBefore(cn, node);
25633 // move node to parent - and clean it..
25634 this.cleanWord(cn);
25636 node.parentNode.removeChild(node);
25637 /// no need to iterate chidlren = it's got none..
25638 //this.iterateChildren(node, this.cleanWord);
25642 if (node.className.length) {
25644 var cn = node.className.split(/\W+/);
25646 Roo.each(cn, function(cls) {
25647 if (cls.match(/Mso[a-zA-Z]+/)) {
25652 node.className = cna.length ? cna.join(' ') : '';
25654 node.removeAttribute("class");
25658 if (node.hasAttribute("lang")) {
25659 node.removeAttribute("lang");
25662 if (node.hasAttribute("style")) {
25664 var styles = node.getAttribute("style").split(";");
25666 Roo.each(styles, function(s) {
25667 if (!s.match(/:/)) {
25670 var kv = s.split(":");
25671 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25674 // what ever is left... we allow.
25677 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25678 if (!nstyle.length) {
25679 node.removeAttribute('style');
25682 this.iterateChildren(node, this.cleanWord);
25688 * iterateChildren of a Node, calling fn each time, using this as the scole..
25689 * @param {DomNode} node node to iterate children of.
25690 * @param {Function} fn method of this class to call on each item.
25692 iterateChildren : function(node, fn)
25694 if (!node.childNodes.length) {
25697 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25698 fn.call(this, node.childNodes[i])
25704 * cleanTableWidths.
25706 * Quite often pasting from word etc.. results in tables with column and widths.
25707 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25710 cleanTableWidths : function(node)
25715 this.cleanTableWidths(this.doc.body);
25720 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25723 Roo.log(node.tagName);
25724 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25725 this.iterateChildren(node, this.cleanTableWidths);
25728 if (node.hasAttribute('width')) {
25729 node.removeAttribute('width');
25733 if (node.hasAttribute("style")) {
25736 var styles = node.getAttribute("style").split(";");
25738 Roo.each(styles, function(s) {
25739 if (!s.match(/:/)) {
25742 var kv = s.split(":");
25743 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25746 // what ever is left... we allow.
25749 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25750 if (!nstyle.length) {
25751 node.removeAttribute('style');
25755 this.iterateChildren(node, this.cleanTableWidths);
25763 domToHTML : function(currentElement, depth, nopadtext) {
25765 depth = depth || 0;
25766 nopadtext = nopadtext || false;
25768 if (!currentElement) {
25769 return this.domToHTML(this.doc.body);
25772 //Roo.log(currentElement);
25774 var allText = false;
25775 var nodeName = currentElement.nodeName;
25776 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25778 if (nodeName == '#text') {
25780 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25785 if (nodeName != 'BODY') {
25788 // Prints the node tagName, such as <A>, <IMG>, etc
25791 for(i = 0; i < currentElement.attributes.length;i++) {
25793 var aname = currentElement.attributes.item(i).name;
25794 if (!currentElement.attributes.item(i).value.length) {
25797 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25800 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25809 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25812 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25817 // Traverse the tree
25819 var currentElementChild = currentElement.childNodes.item(i);
25820 var allText = true;
25821 var innerHTML = '';
25823 while (currentElementChild) {
25824 // Formatting code (indent the tree so it looks nice on the screen)
25825 var nopad = nopadtext;
25826 if (lastnode == 'SPAN') {
25830 if (currentElementChild.nodeName == '#text') {
25831 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25832 toadd = nopadtext ? toadd : toadd.trim();
25833 if (!nopad && toadd.length > 80) {
25834 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25836 innerHTML += toadd;
25839 currentElementChild = currentElement.childNodes.item(i);
25845 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25847 // Recursively traverse the tree structure of the child node
25848 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25849 lastnode = currentElementChild.nodeName;
25851 currentElementChild=currentElement.childNodes.item(i);
25857 // The remaining code is mostly for formatting the tree
25858 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25863 ret+= "</"+tagName+">";
25869 applyBlacklists : function()
25871 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25872 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25876 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25877 if (b.indexOf(tag) > -1) {
25880 this.white.push(tag);
25884 Roo.each(w, function(tag) {
25885 if (b.indexOf(tag) > -1) {
25888 if (this.white.indexOf(tag) > -1) {
25891 this.white.push(tag);
25896 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25897 if (w.indexOf(tag) > -1) {
25900 this.black.push(tag);
25904 Roo.each(b, function(tag) {
25905 if (w.indexOf(tag) > -1) {
25908 if (this.black.indexOf(tag) > -1) {
25911 this.black.push(tag);
25916 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25917 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25921 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25922 if (b.indexOf(tag) > -1) {
25925 this.cwhite.push(tag);
25929 Roo.each(w, function(tag) {
25930 if (b.indexOf(tag) > -1) {
25933 if (this.cwhite.indexOf(tag) > -1) {
25936 this.cwhite.push(tag);
25941 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25942 if (w.indexOf(tag) > -1) {
25945 this.cblack.push(tag);
25949 Roo.each(b, function(tag) {
25950 if (w.indexOf(tag) > -1) {
25953 if (this.cblack.indexOf(tag) > -1) {
25956 this.cblack.push(tag);
25961 setStylesheets : function(stylesheets)
25963 if(typeof(stylesheets) == 'string'){
25964 Roo.get(this.iframe.contentDocument.head).createChild({
25966 rel : 'stylesheet',
25975 Roo.each(stylesheets, function(s) {
25980 Roo.get(_this.iframe.contentDocument.head).createChild({
25982 rel : 'stylesheet',
25991 removeStylesheets : function()
25995 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26000 setStyle : function(style)
26002 Roo.get(this.iframe.contentDocument.head).createChild({
26011 // hide stuff that is not compatible
26025 * @event specialkey
26029 * @cfg {String} fieldClass @hide
26032 * @cfg {String} focusClass @hide
26035 * @cfg {String} autoCreate @hide
26038 * @cfg {String} inputType @hide
26041 * @cfg {String} invalidClass @hide
26044 * @cfg {String} invalidText @hide
26047 * @cfg {String} msgFx @hide
26050 * @cfg {String} validateOnBlur @hide
26054 Roo.HtmlEditorCore.white = [
26055 'area', 'br', 'img', 'input', 'hr', 'wbr',
26057 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26058 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26059 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26060 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26061 'table', 'ul', 'xmp',
26063 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26066 'dir', 'menu', 'ol', 'ul', 'dl',
26072 Roo.HtmlEditorCore.black = [
26073 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26075 'base', 'basefont', 'bgsound', 'blink', 'body',
26076 'frame', 'frameset', 'head', 'html', 'ilayer',
26077 'iframe', 'layer', 'link', 'meta', 'object',
26078 'script', 'style' ,'title', 'xml' // clean later..
26080 Roo.HtmlEditorCore.clean = [
26081 'script', 'style', 'title', 'xml'
26083 Roo.HtmlEditorCore.remove = [
26088 Roo.HtmlEditorCore.ablack = [
26092 Roo.HtmlEditorCore.aclean = [
26093 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26097 Roo.HtmlEditorCore.pwhite= [
26098 'http', 'https', 'mailto'
26101 // white listed style attributes.
26102 Roo.HtmlEditorCore.cwhite= [
26103 // 'text-align', /// default is to allow most things..
26109 // black listed style attributes.
26110 Roo.HtmlEditorCore.cblack= [
26111 // 'font-size' -- this can be set by the project
26115 Roo.HtmlEditorCore.swapCodes =[
26116 [ 8211, "–" ],
26117 [ 8212, "—" ],
26134 * @class Roo.bootstrap.HtmlEditor
26135 * @extends Roo.bootstrap.TextArea
26136 * Bootstrap HtmlEditor class
26139 * Create a new HtmlEditor
26140 * @param {Object} config The config object
26143 Roo.bootstrap.HtmlEditor = function(config){
26144 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26145 if (!this.toolbars) {
26146 this.toolbars = [];
26149 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26152 * @event initialize
26153 * Fires when the editor is fully initialized (including the iframe)
26154 * @param {HtmlEditor} this
26159 * Fires when the editor is first receives the focus. Any insertion must wait
26160 * until after this event.
26161 * @param {HtmlEditor} this
26165 * @event beforesync
26166 * Fires before the textarea is updated with content from the editor iframe. Return false
26167 * to cancel the sync.
26168 * @param {HtmlEditor} this
26169 * @param {String} html
26173 * @event beforepush
26174 * Fires before the iframe editor is updated with content from the textarea. Return false
26175 * to cancel the push.
26176 * @param {HtmlEditor} this
26177 * @param {String} html
26182 * Fires when the textarea is updated with content from the editor iframe.
26183 * @param {HtmlEditor} this
26184 * @param {String} html
26189 * Fires when the iframe editor is updated with content from the textarea.
26190 * @param {HtmlEditor} this
26191 * @param {String} html
26195 * @event editmodechange
26196 * Fires when the editor switches edit modes
26197 * @param {HtmlEditor} this
26198 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26200 editmodechange: true,
26202 * @event editorevent
26203 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26204 * @param {HtmlEditor} this
26208 * @event firstfocus
26209 * Fires when on first focus - needed by toolbars..
26210 * @param {HtmlEditor} this
26215 * Auto save the htmlEditor value as a file into Events
26216 * @param {HtmlEditor} this
26220 * @event savedpreview
26221 * preview the saved version of htmlEditor
26222 * @param {HtmlEditor} this
26229 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26233 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26238 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26243 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26248 * @cfg {Number} height (in pixels)
26252 * @cfg {Number} width (in pixels)
26257 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26260 stylesheets: false,
26265 // private properties
26266 validationEvent : false,
26268 initialized : false,
26271 onFocus : Roo.emptyFn,
26273 hideMode:'offsets',
26275 tbContainer : false,
26279 toolbarContainer :function() {
26280 return this.wrap.select('.x-html-editor-tb',true).first();
26284 * Protected method that will not generally be called directly. It
26285 * is called when the editor creates its toolbar. Override this method if you need to
26286 * add custom toolbar buttons.
26287 * @param {HtmlEditor} editor
26289 createToolbar : function(){
26290 Roo.log('renewing');
26291 Roo.log("create toolbars");
26293 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26294 this.toolbars[0].render(this.toolbarContainer());
26298 // if (!editor.toolbars || !editor.toolbars.length) {
26299 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26302 // for (var i =0 ; i < editor.toolbars.length;i++) {
26303 // editor.toolbars[i] = Roo.factory(
26304 // typeof(editor.toolbars[i]) == 'string' ?
26305 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26306 // Roo.bootstrap.HtmlEditor);
26307 // editor.toolbars[i].init(editor);
26313 onRender : function(ct, position)
26315 // Roo.log("Call onRender: " + this.xtype);
26317 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26319 this.wrap = this.inputEl().wrap({
26320 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26323 this.editorcore.onRender(ct, position);
26325 if (this.resizable) {
26326 this.resizeEl = new Roo.Resizable(this.wrap, {
26330 minHeight : this.height,
26331 height: this.height,
26332 handles : this.resizable,
26335 resize : function(r, w, h) {
26336 _t.onResize(w,h); // -something
26342 this.createToolbar(this);
26345 if(!this.width && this.resizable){
26346 this.setSize(this.wrap.getSize());
26348 if (this.resizeEl) {
26349 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26350 // should trigger onReize..
26356 onResize : function(w, h)
26358 Roo.log('resize: ' +w + ',' + h );
26359 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26363 if(this.inputEl() ){
26364 if(typeof w == 'number'){
26365 var aw = w - this.wrap.getFrameWidth('lr');
26366 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26369 if(typeof h == 'number'){
26370 var tbh = -11; // fixme it needs to tool bar size!
26371 for (var i =0; i < this.toolbars.length;i++) {
26372 // fixme - ask toolbars for heights?
26373 tbh += this.toolbars[i].el.getHeight();
26374 //if (this.toolbars[i].footer) {
26375 // tbh += this.toolbars[i].footer.el.getHeight();
26383 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26384 ah -= 5; // knock a few pixes off for look..
26385 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26389 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26390 this.editorcore.onResize(ew,eh);
26395 * Toggles the editor between standard and source edit mode.
26396 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26398 toggleSourceEdit : function(sourceEditMode)
26400 this.editorcore.toggleSourceEdit(sourceEditMode);
26402 if(this.editorcore.sourceEditMode){
26403 Roo.log('editor - showing textarea');
26406 // Roo.log(this.syncValue());
26408 this.inputEl().removeClass(['hide', 'x-hidden']);
26409 this.inputEl().dom.removeAttribute('tabIndex');
26410 this.inputEl().focus();
26412 Roo.log('editor - hiding textarea');
26414 // Roo.log(this.pushValue());
26417 this.inputEl().addClass(['hide', 'x-hidden']);
26418 this.inputEl().dom.setAttribute('tabIndex', -1);
26419 //this.deferFocus();
26422 if(this.resizable){
26423 this.setSize(this.wrap.getSize());
26426 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26429 // private (for BoxComponent)
26430 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26432 // private (for BoxComponent)
26433 getResizeEl : function(){
26437 // private (for BoxComponent)
26438 getPositionEl : function(){
26443 initEvents : function(){
26444 this.originalValue = this.getValue();
26448 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26451 // markInvalid : Roo.emptyFn,
26453 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26456 // clearInvalid : Roo.emptyFn,
26458 setValue : function(v){
26459 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26460 this.editorcore.pushValue();
26465 deferFocus : function(){
26466 this.focus.defer(10, this);
26470 focus : function(){
26471 this.editorcore.focus();
26477 onDestroy : function(){
26483 for (var i =0; i < this.toolbars.length;i++) {
26484 // fixme - ask toolbars for heights?
26485 this.toolbars[i].onDestroy();
26488 this.wrap.dom.innerHTML = '';
26489 this.wrap.remove();
26494 onFirstFocus : function(){
26495 //Roo.log("onFirstFocus");
26496 this.editorcore.onFirstFocus();
26497 for (var i =0; i < this.toolbars.length;i++) {
26498 this.toolbars[i].onFirstFocus();
26504 syncValue : function()
26506 this.editorcore.syncValue();
26509 pushValue : function()
26511 this.editorcore.pushValue();
26515 // hide stuff that is not compatible
26529 * @event specialkey
26533 * @cfg {String} fieldClass @hide
26536 * @cfg {String} focusClass @hide
26539 * @cfg {String} autoCreate @hide
26542 * @cfg {String} inputType @hide
26546 * @cfg {String} invalidText @hide
26549 * @cfg {String} msgFx @hide
26552 * @cfg {String} validateOnBlur @hide
26561 Roo.namespace('Roo.bootstrap.htmleditor');
26563 * @class Roo.bootstrap.HtmlEditorToolbar1
26569 new Roo.bootstrap.HtmlEditor({
26572 new Roo.bootstrap.HtmlEditorToolbar1({
26573 disable : { fonts: 1 , format: 1, ..., ... , ...],
26579 * @cfg {Object} disable List of elements to disable..
26580 * @cfg {Array} btns List of additional buttons.
26584 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26590 Roo.apply(this, config);
26592 // default disabled, based on 'good practice'..
26593 this.disable = this.disable || {};
26594 Roo.applyIf(this.disable, {
26597 specialElements : true
26599 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26601 this.editor = config.editor;
26602 this.editorcore = config.editor.editorcore;
26604 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26606 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26607 // dont call parent... till later.
26609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26614 editorcore : false,
26619 "h1","h2","h3","h4","h5","h6",
26621 "abbr", "acronym", "address", "cite", "samp", "var",
26625 onRender : function(ct, position)
26627 // Roo.log("Call onRender: " + this.xtype);
26629 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26631 this.el.dom.style.marginBottom = '0';
26633 var editorcore = this.editorcore;
26634 var editor= this.editor;
26637 var btn = function(id,cmd , toggle, handler, html){
26639 var event = toggle ? 'toggle' : 'click';
26644 xns: Roo.bootstrap,
26648 enableToggle:toggle !== false,
26650 pressed : toggle ? false : null,
26653 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26654 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26660 // var cb_box = function...
26665 xns: Roo.bootstrap,
26670 xns: Roo.bootstrap,
26674 Roo.each(this.formats, function(f) {
26675 style.menu.items.push({
26677 xns: Roo.bootstrap,
26678 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26683 editorcore.insertTag(this.tagname);
26690 children.push(style);
26692 btn('bold',false,true);
26693 btn('italic',false,true);
26694 btn('align-left', 'justifyleft',true);
26695 btn('align-center', 'justifycenter',true);
26696 btn('align-right' , 'justifyright',true);
26697 btn('link', false, false, function(btn) {
26698 //Roo.log("create link?");
26699 var url = prompt(this.createLinkText, this.defaultLinkValue);
26700 if(url && url != 'http:/'+'/'){
26701 this.editorcore.relayCmd('createlink', url);
26704 btn('list','insertunorderedlist',true);
26705 btn('pencil', false,true, function(btn){
26707 this.toggleSourceEdit(btn.pressed);
26710 if (this.editor.btns.length > 0) {
26711 for (var i = 0; i<this.editor.btns.length; i++) {
26712 children.push(this.editor.btns[i]);
26720 xns: Roo.bootstrap,
26725 xns: Roo.bootstrap,
26730 cog.menu.items.push({
26732 xns: Roo.bootstrap,
26733 html : Clean styles,
26738 editorcore.insertTag(this.tagname);
26747 this.xtype = 'NavSimplebar';
26749 for(var i=0;i< children.length;i++) {
26751 this.buttons.add(this.addxtypeChild(children[i]));
26755 editor.on('editorevent', this.updateToolbar, this);
26757 onBtnClick : function(id)
26759 this.editorcore.relayCmd(id);
26760 this.editorcore.focus();
26764 * Protected method that will not generally be called directly. It triggers
26765 * a toolbar update by reading the markup state of the current selection in the editor.
26767 updateToolbar: function(){
26769 if(!this.editorcore.activated){
26770 this.editor.onFirstFocus(); // is this neeed?
26774 var btns = this.buttons;
26775 var doc = this.editorcore.doc;
26776 btns.get('bold').setActive(doc.queryCommandState('bold'));
26777 btns.get('italic').setActive(doc.queryCommandState('italic'));
26778 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26780 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26781 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26782 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26784 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26785 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26788 var ans = this.editorcore.getAllAncestors();
26789 if (this.formatCombo) {
26792 var store = this.formatCombo.store;
26793 this.formatCombo.setValue("");
26794 for (var i =0; i < ans.length;i++) {
26795 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26797 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26805 // hides menus... - so this cant be on a menu...
26806 Roo.bootstrap.MenuMgr.hideAll();
26808 Roo.bootstrap.MenuMgr.hideAll();
26809 //this.editorsyncValue();
26811 onFirstFocus: function() {
26812 this.buttons.each(function(item){
26816 toggleSourceEdit : function(sourceEditMode){
26819 if(sourceEditMode){
26820 Roo.log("disabling buttons");
26821 this.buttons.each( function(item){
26822 if(item.cmd != 'pencil'){
26828 Roo.log("enabling buttons");
26829 if(this.editorcore.initialized){
26830 this.buttons.each( function(item){
26836 Roo.log("calling toggole on editor");
26837 // tell the editor that it's been pressed..
26838 this.editor.toggleSourceEdit(sourceEditMode);
26852 * @class Roo.bootstrap.Markdown
26853 * @extends Roo.bootstrap.TextArea
26854 * Bootstrap Showdown editable area
26855 * @cfg {string} content
26858 * Create a new Showdown
26861 Roo.bootstrap.Markdown = function(config){
26862 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26866 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26870 initEvents : function()
26873 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26874 this.markdownEl = this.el.createChild({
26875 cls : 'roo-markdown-area'
26877 this.inputEl().addClass('d-none');
26878 if (this.getValue() == '') {
26879 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26882 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26884 this.markdownEl.on('click', this.toggleTextEdit, this);
26885 this.on('blur', this.toggleTextEdit, this);
26886 this.on('specialkey', this.resizeTextArea, this);
26889 toggleTextEdit : function()
26891 var sh = this.markdownEl.getHeight();
26892 this.inputEl().addClass('d-none');
26893 this.markdownEl.addClass('d-none');
26894 if (!this.editing) {
26896 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26897 this.inputEl().removeClass('d-none');
26898 this.inputEl().focus();
26899 this.editing = true;
26902 // show showdown...
26903 this.updateMarkdown();
26904 this.markdownEl.removeClass('d-none');
26905 this.editing = false;
26908 updateMarkdown : function()
26910 if (this.getValue() == '') {
26911 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26915 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26918 resizeTextArea: function () {
26921 Roo.log([sh, this.getValue().split("\n").length * 30]);
26922 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26924 setValue : function(val)
26926 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26927 if (!this.editing) {
26928 this.updateMarkdown();
26934 if (!this.editing) {
26935 this.toggleTextEdit();
26943 * @class Roo.bootstrap.Table.AbstractSelectionModel
26944 * @extends Roo.util.Observable
26945 * Abstract base class for grid SelectionModels. It provides the interface that should be
26946 * implemented by descendant classes. This class should not be directly instantiated.
26949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26950 this.locked = false;
26951 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26956 /** @ignore Called by the grid automatically. Do not call directly. */
26957 init : function(grid){
26963 * Locks the selections.
26966 this.locked = true;
26970 * Unlocks the selections.
26972 unlock : function(){
26973 this.locked = false;
26977 * Returns true if the selections are locked.
26978 * @return {Boolean}
26980 isLocked : function(){
26981 return this.locked;
26985 initEvents : function ()
26991 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26992 * @class Roo.bootstrap.Table.RowSelectionModel
26993 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26994 * It supports multiple selections and keyboard selection/navigation.
26996 * @param {Object} config
26999 Roo.bootstrap.Table.RowSelectionModel = function(config){
27000 Roo.apply(this, config);
27001 this.selections = new Roo.util.MixedCollection(false, function(o){
27006 this.lastActive = false;
27010 * @event selectionchange
27011 * Fires when the selection changes
27012 * @param {SelectionModel} this
27014 "selectionchange" : true,
27016 * @event afterselectionchange
27017 * Fires after the selection changes (eg. by key press or clicking)
27018 * @param {SelectionModel} this
27020 "afterselectionchange" : true,
27022 * @event beforerowselect
27023 * Fires when a row is selected being selected, return false to cancel.
27024 * @param {SelectionModel} this
27025 * @param {Number} rowIndex The selected index
27026 * @param {Boolean} keepExisting False if other selections will be cleared
27028 "beforerowselect" : true,
27031 * Fires when a row is selected.
27032 * @param {SelectionModel} this
27033 * @param {Number} rowIndex The selected index
27034 * @param {Roo.data.Record} r The record
27036 "rowselect" : true,
27038 * @event rowdeselect
27039 * Fires when a row is deselected.
27040 * @param {SelectionModel} this
27041 * @param {Number} rowIndex The selected index
27043 "rowdeselect" : true
27045 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27046 this.locked = false;
27049 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27051 * @cfg {Boolean} singleSelect
27052 * True to allow selection of only one row at a time (defaults to false)
27054 singleSelect : false,
27057 initEvents : function()
27060 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27061 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27062 //}else{ // allow click to work like normal
27063 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27065 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27066 this.grid.on("rowclick", this.handleMouseDown, this);
27068 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27069 "up" : function(e){
27071 this.selectPrevious(e.shiftKey);
27072 }else if(this.last !== false && this.lastActive !== false){
27073 var last = this.last;
27074 this.selectRange(this.last, this.lastActive-1);
27075 this.grid.getView().focusRow(this.lastActive);
27076 if(last !== false){
27080 this.selectFirstRow();
27082 this.fireEvent("afterselectionchange", this);
27084 "down" : function(e){
27086 this.selectNext(e.shiftKey);
27087 }else if(this.last !== false && this.lastActive !== false){
27088 var last = this.last;
27089 this.selectRange(this.last, this.lastActive+1);
27090 this.grid.getView().focusRow(this.lastActive);
27091 if(last !== false){
27095 this.selectFirstRow();
27097 this.fireEvent("afterselectionchange", this);
27101 this.grid.store.on('load', function(){
27102 this.selections.clear();
27105 var view = this.grid.view;
27106 view.on("refresh", this.onRefresh, this);
27107 view.on("rowupdated", this.onRowUpdated, this);
27108 view.on("rowremoved", this.onRemove, this);
27113 onRefresh : function()
27115 var ds = this.grid.store, i, v = this.grid.view;
27116 var s = this.selections;
27117 s.each(function(r){
27118 if((i = ds.indexOfId(r.id)) != -1){
27127 onRemove : function(v, index, r){
27128 this.selections.remove(r);
27132 onRowUpdated : function(v, index, r){
27133 if(this.isSelected(r)){
27134 v.onRowSelect(index);
27140 * @param {Array} records The records to select
27141 * @param {Boolean} keepExisting (optional) True to keep existing selections
27143 selectRecords : function(records, keepExisting)
27146 this.clearSelections();
27148 var ds = this.grid.store;
27149 for(var i = 0, len = records.length; i < len; i++){
27150 this.selectRow(ds.indexOf(records[i]), true);
27155 * Gets the number of selected rows.
27158 getCount : function(){
27159 return this.selections.length;
27163 * Selects the first row in the grid.
27165 selectFirstRow : function(){
27170 * Select the last row.
27171 * @param {Boolean} keepExisting (optional) True to keep existing selections
27173 selectLastRow : function(keepExisting){
27174 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27175 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27179 * Selects the row immediately following the last selected row.
27180 * @param {Boolean} keepExisting (optional) True to keep existing selections
27182 selectNext : function(keepExisting)
27184 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27185 this.selectRow(this.last+1, keepExisting);
27186 this.grid.getView().focusRow(this.last);
27191 * Selects the row that precedes the last selected row.
27192 * @param {Boolean} keepExisting (optional) True to keep existing selections
27194 selectPrevious : function(keepExisting){
27196 this.selectRow(this.last-1, keepExisting);
27197 this.grid.getView().focusRow(this.last);
27202 * Returns the selected records
27203 * @return {Array} Array of selected records
27205 getSelections : function(){
27206 return [].concat(this.selections.items);
27210 * Returns the first selected record.
27213 getSelected : function(){
27214 return this.selections.itemAt(0);
27219 * Clears all selections.
27221 clearSelections : function(fast)
27227 var ds = this.grid.store;
27228 var s = this.selections;
27229 s.each(function(r){
27230 this.deselectRow(ds.indexOfId(r.id));
27234 this.selections.clear();
27241 * Selects all rows.
27243 selectAll : function(){
27247 this.selections.clear();
27248 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27249 this.selectRow(i, true);
27254 * Returns True if there is a selection.
27255 * @return {Boolean}
27257 hasSelection : function(){
27258 return this.selections.length > 0;
27262 * Returns True if the specified row is selected.
27263 * @param {Number/Record} record The record or index of the record to check
27264 * @return {Boolean}
27266 isSelected : function(index){
27267 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27268 return (r && this.selections.key(r.id) ? true : false);
27272 * Returns True if the specified record id is selected.
27273 * @param {String} id The id of record to check
27274 * @return {Boolean}
27276 isIdSelected : function(id){
27277 return (this.selections.key(id) ? true : false);
27282 handleMouseDBClick : function(e, t){
27286 handleMouseDown : function(e, t)
27288 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27289 if(this.isLocked() || rowIndex < 0 ){
27292 if(e.shiftKey && this.last !== false){
27293 var last = this.last;
27294 this.selectRange(last, rowIndex, e.ctrlKey);
27295 this.last = last; // reset the last
27299 var isSelected = this.isSelected(rowIndex);
27300 //Roo.log("select row:" + rowIndex);
27302 this.deselectRow(rowIndex);
27304 this.selectRow(rowIndex, true);
27308 if(e.button !== 0 && isSelected){
27309 alert('rowIndex 2: ' + rowIndex);
27310 view.focusRow(rowIndex);
27311 }else if(e.ctrlKey && isSelected){
27312 this.deselectRow(rowIndex);
27313 }else if(!isSelected){
27314 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27315 view.focusRow(rowIndex);
27319 this.fireEvent("afterselectionchange", this);
27322 handleDragableRowClick : function(grid, rowIndex, e)
27324 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27325 this.selectRow(rowIndex, false);
27326 grid.view.focusRow(rowIndex);
27327 this.fireEvent("afterselectionchange", this);
27332 * Selects multiple rows.
27333 * @param {Array} rows Array of the indexes of the row to select
27334 * @param {Boolean} keepExisting (optional) True to keep existing selections
27336 selectRows : function(rows, keepExisting){
27338 this.clearSelections();
27340 for(var i = 0, len = rows.length; i < len; i++){
27341 this.selectRow(rows[i], true);
27346 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27347 * @param {Number} startRow The index of the first row in the range
27348 * @param {Number} endRow The index of the last row in the range
27349 * @param {Boolean} keepExisting (optional) True to retain existing selections
27351 selectRange : function(startRow, endRow, keepExisting){
27356 this.clearSelections();
27358 if(startRow <= endRow){
27359 for(var i = startRow; i <= endRow; i++){
27360 this.selectRow(i, true);
27363 for(var i = startRow; i >= endRow; i--){
27364 this.selectRow(i, true);
27370 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27371 * @param {Number} startRow The index of the first row in the range
27372 * @param {Number} endRow The index of the last row in the range
27374 deselectRange : function(startRow, endRow, preventViewNotify){
27378 for(var i = startRow; i <= endRow; i++){
27379 this.deselectRow(i, preventViewNotify);
27385 * @param {Number} row The index of the row to select
27386 * @param {Boolean} keepExisting (optional) True to keep existing selections
27388 selectRow : function(index, keepExisting, preventViewNotify)
27390 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27393 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27394 if(!keepExisting || this.singleSelect){
27395 this.clearSelections();
27398 var r = this.grid.store.getAt(index);
27399 //console.log('selectRow - record id :' + r.id);
27401 this.selections.add(r);
27402 this.last = this.lastActive = index;
27403 if(!preventViewNotify){
27404 var proxy = new Roo.Element(
27405 this.grid.getRowDom(index)
27407 proxy.addClass('bg-info info');
27409 this.fireEvent("rowselect", this, index, r);
27410 this.fireEvent("selectionchange", this);
27416 * @param {Number} row The index of the row to deselect
27418 deselectRow : function(index, preventViewNotify)
27423 if(this.last == index){
27426 if(this.lastActive == index){
27427 this.lastActive = false;
27430 var r = this.grid.store.getAt(index);
27435 this.selections.remove(r);
27436 //.console.log('deselectRow - record id :' + r.id);
27437 if(!preventViewNotify){
27439 var proxy = new Roo.Element(
27440 this.grid.getRowDom(index)
27442 proxy.removeClass('bg-info info');
27444 this.fireEvent("rowdeselect", this, index);
27445 this.fireEvent("selectionchange", this);
27449 restoreLast : function(){
27451 this.last = this._last;
27456 acceptsNav : function(row, col, cm){
27457 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27461 onEditorKey : function(field, e){
27462 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27467 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27469 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27471 }else if(k == e.ENTER && !e.ctrlKey){
27475 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27477 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27479 }else if(k == e.ESC){
27483 g.startEditing(newCell[0], newCell[1]);
27489 * Ext JS Library 1.1.1
27490 * Copyright(c) 2006-2007, Ext JS, LLC.
27492 * Originally Released Under LGPL - original licence link has changed is not relivant.
27495 * <script type="text/javascript">
27499 * @class Roo.bootstrap.PagingToolbar
27500 * @extends Roo.bootstrap.NavSimplebar
27501 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27503 * Create a new PagingToolbar
27504 * @param {Object} config The config object
27505 * @param {Roo.data.Store} store
27507 Roo.bootstrap.PagingToolbar = function(config)
27509 // old args format still supported... - xtype is prefered..
27510 // created from xtype...
27512 this.ds = config.dataSource;
27514 if (config.store && !this.ds) {
27515 this.store= Roo.factory(config.store, Roo.data);
27516 this.ds = this.store;
27517 this.ds.xmodule = this.xmodule || false;
27520 this.toolbarItems = [];
27521 if (config.items) {
27522 this.toolbarItems = config.items;
27525 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27530 this.bind(this.ds);
27533 if (Roo.bootstrap.version == 4) {
27534 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27536 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27543 * @cfg {Roo.data.Store} dataSource
27544 * The underlying data store providing the paged data
27547 * @cfg {String/HTMLElement/Element} container
27548 * container The id or element that will contain the toolbar
27551 * @cfg {Boolean} displayInfo
27552 * True to display the displayMsg (defaults to false)
27555 * @cfg {Number} pageSize
27556 * The number of records to display per page (defaults to 20)
27560 * @cfg {String} displayMsg
27561 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27563 displayMsg : 'Displaying {0} - {1} of {2}',
27565 * @cfg {String} emptyMsg
27566 * The message to display when no records are found (defaults to "No data to display")
27568 emptyMsg : 'No data to display',
27570 * Customizable piece of the default paging text (defaults to "Page")
27573 beforePageText : "Page",
27575 * Customizable piece of the default paging text (defaults to "of %0")
27578 afterPageText : "of {0}",
27580 * Customizable piece of the default paging text (defaults to "First Page")
27583 firstText : "First Page",
27585 * Customizable piece of the default paging text (defaults to "Previous Page")
27588 prevText : "Previous Page",
27590 * Customizable piece of the default paging text (defaults to "Next Page")
27593 nextText : "Next Page",
27595 * Customizable piece of the default paging text (defaults to "Last Page")
27598 lastText : "Last Page",
27600 * Customizable piece of the default paging text (defaults to "Refresh")
27603 refreshText : "Refresh",
27607 onRender : function(ct, position)
27609 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27610 this.navgroup.parentId = this.id;
27611 this.navgroup.onRender(this.el, null);
27612 // add the buttons to the navgroup
27614 if(this.displayInfo){
27615 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27616 this.displayEl = this.el.select('.x-paging-info', true).first();
27617 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27618 // this.displayEl = navel.el.select('span',true).first();
27624 Roo.each(_this.buttons, function(e){ // this might need to use render????
27625 Roo.factory(e).render(_this.el);
27629 Roo.each(_this.toolbarItems, function(e) {
27630 _this.navgroup.addItem(e);
27634 this.first = this.navgroup.addItem({
27635 tooltip: this.firstText,
27636 cls: "prev btn-outline-secondary",
27637 html : ' <i class="fa fa-step-backward"></i>',
27639 preventDefault: true,
27640 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27643 this.prev = this.navgroup.addItem({
27644 tooltip: this.prevText,
27645 cls: "prev btn-outline-secondary",
27646 html : ' <i class="fa fa-backward"></i>',
27648 preventDefault: true,
27649 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27651 //this.addSeparator();
27654 var field = this.navgroup.addItem( {
27656 cls : 'x-paging-position btn-outline-secondary',
27658 html : this.beforePageText +
27659 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27660 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27663 this.field = field.el.select('input', true).first();
27664 this.field.on("keydown", this.onPagingKeydown, this);
27665 this.field.on("focus", function(){this.dom.select();});
27668 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27669 //this.field.setHeight(18);
27670 //this.addSeparator();
27671 this.next = this.navgroup.addItem({
27672 tooltip: this.nextText,
27673 cls: "next btn-outline-secondary",
27674 html : ' <i class="fa fa-forward"></i>',
27676 preventDefault: true,
27677 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27679 this.last = this.navgroup.addItem({
27680 tooltip: this.lastText,
27681 html : ' <i class="fa fa-step-forward"></i>',
27682 cls: "next btn-outline-secondary",
27684 preventDefault: true,
27685 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27687 //this.addSeparator();
27688 this.loading = this.navgroup.addItem({
27689 tooltip: this.refreshText,
27690 cls: "btn-outline-secondary",
27691 html : ' <i class="fa fa-refresh"></i>',
27692 preventDefault: true,
27693 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27699 updateInfo : function(){
27700 if(this.displayEl){
27701 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27702 var msg = count == 0 ?
27706 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27708 this.displayEl.update(msg);
27713 onLoad : function(ds, r, o)
27715 this.cursor = o.params && o.params.start ? o.params.start : 0;
27717 var d = this.getPageData(),
27722 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27723 this.field.dom.value = ap;
27724 this.first.setDisabled(ap == 1);
27725 this.prev.setDisabled(ap == 1);
27726 this.next.setDisabled(ap == ps);
27727 this.last.setDisabled(ap == ps);
27728 this.loading.enable();
27733 getPageData : function(){
27734 var total = this.ds.getTotalCount();
27737 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27738 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27743 onLoadError : function(){
27744 this.loading.enable();
27748 onPagingKeydown : function(e){
27749 var k = e.getKey();
27750 var d = this.getPageData();
27752 var v = this.field.dom.value, pageNum;
27753 if(!v || isNaN(pageNum = parseInt(v, 10))){
27754 this.field.dom.value = d.activePage;
27757 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27758 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27761 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))
27763 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27764 this.field.dom.value = pageNum;
27765 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27768 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27770 var v = this.field.dom.value, pageNum;
27771 var increment = (e.shiftKey) ? 10 : 1;
27772 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27775 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27776 this.field.dom.value = d.activePage;
27779 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27781 this.field.dom.value = parseInt(v, 10) + increment;
27782 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27783 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27790 beforeLoad : function(){
27792 this.loading.disable();
27797 onClick : function(which){
27806 ds.load({params:{start: 0, limit: this.pageSize}});
27809 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27812 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27815 var total = ds.getTotalCount();
27816 var extra = total % this.pageSize;
27817 var lastStart = extra ? (total - extra) : total-this.pageSize;
27818 ds.load({params:{start: lastStart, limit: this.pageSize}});
27821 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27827 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27828 * @param {Roo.data.Store} store The data store to unbind
27830 unbind : function(ds){
27831 ds.un("beforeload", this.beforeLoad, this);
27832 ds.un("load", this.onLoad, this);
27833 ds.un("loadexception", this.onLoadError, this);
27834 ds.un("remove", this.updateInfo, this);
27835 ds.un("add", this.updateInfo, this);
27836 this.ds = undefined;
27840 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27841 * @param {Roo.data.Store} store The data store to bind
27843 bind : function(ds){
27844 ds.on("beforeload", this.beforeLoad, this);
27845 ds.on("load", this.onLoad, this);
27846 ds.on("loadexception", this.onLoadError, this);
27847 ds.on("remove", this.updateInfo, this);
27848 ds.on("add", this.updateInfo, this);
27859 * @class Roo.bootstrap.MessageBar
27860 * @extends Roo.bootstrap.Component
27861 * Bootstrap MessageBar class
27862 * @cfg {String} html contents of the MessageBar
27863 * @cfg {String} weight (info | success | warning | danger) default info
27864 * @cfg {String} beforeClass insert the bar before the given class
27865 * @cfg {Boolean} closable (true | false) default false
27866 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27869 * Create a new Element
27870 * @param {Object} config The config object
27873 Roo.bootstrap.MessageBar = function(config){
27874 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27877 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27883 beforeClass: 'bootstrap-sticky-wrap',
27885 getAutoCreate : function(){
27889 cls: 'alert alert-dismissable alert-' + this.weight,
27894 html: this.html || ''
27900 cfg.cls += ' alert-messages-fixed';
27914 onRender : function(ct, position)
27916 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27919 var cfg = Roo.apply({}, this.getAutoCreate());
27923 cfg.cls += ' ' + this.cls;
27926 cfg.style = this.style;
27928 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27930 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27933 this.el.select('>button.close').on('click', this.hide, this);
27939 if (!this.rendered) {
27945 this.fireEvent('show', this);
27951 if (!this.rendered) {
27957 this.fireEvent('hide', this);
27960 update : function()
27962 // var e = this.el.dom.firstChild;
27964 // if(this.closable){
27965 // e = e.nextSibling;
27968 // e.data = this.html || '';
27970 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27986 * @class Roo.bootstrap.Graph
27987 * @extends Roo.bootstrap.Component
27988 * Bootstrap Graph class
27992 @cfg {String} graphtype bar | vbar | pie
27993 @cfg {number} g_x coodinator | centre x (pie)
27994 @cfg {number} g_y coodinator | centre y (pie)
27995 @cfg {number} g_r radius (pie)
27996 @cfg {number} g_height height of the chart (respected by all elements in the set)
27997 @cfg {number} g_width width of the chart (respected by all elements in the set)
27998 @cfg {Object} title The title of the chart
28001 -opts (object) options for the chart
28003 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28004 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28006 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.
28007 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28009 o stretch (boolean)
28011 -opts (object) options for the pie
28014 o startAngle (number)
28015 o endAngle (number)
28019 * Create a new Input
28020 * @param {Object} config The config object
28023 Roo.bootstrap.Graph = function(config){
28024 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28030 * The img click event for the img.
28031 * @param {Roo.EventObject} e
28037 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28048 //g_colors: this.colors,
28055 getAutoCreate : function(){
28066 onRender : function(ct,position){
28069 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28071 if (typeof(Raphael) == 'undefined') {
28072 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28076 this.raphael = Raphael(this.el.dom);
28078 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28079 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28080 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28081 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28083 r.text(160, 10, "Single Series Chart").attr(txtattr);
28084 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28085 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28086 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28088 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28089 r.barchart(330, 10, 300, 220, data1);
28090 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28091 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28094 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28095 // r.barchart(30, 30, 560, 250, xdata, {
28096 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28097 // axis : "0 0 1 1",
28098 // axisxlabels : xdata
28099 // //yvalues : cols,
28102 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28104 // this.load(null,xdata,{
28105 // axis : "0 0 1 1",
28106 // axisxlabels : xdata
28111 load : function(graphtype,xdata,opts)
28113 this.raphael.clear();
28115 graphtype = this.graphtype;
28120 var r = this.raphael,
28121 fin = function () {
28122 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28124 fout = function () {
28125 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28127 pfin = function() {
28128 this.sector.stop();
28129 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28132 this.label[0].stop();
28133 this.label[0].attr({ r: 7.5 });
28134 this.label[1].attr({ "font-weight": 800 });
28137 pfout = function() {
28138 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28141 this.label[0].animate({ r: 5 }, 500, "bounce");
28142 this.label[1].attr({ "font-weight": 400 });
28148 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28151 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28154 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28155 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28157 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28164 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28169 setTitle: function(o)
28174 initEvents: function() {
28177 this.el.on('click', this.onClick, this);
28181 onClick : function(e)
28183 Roo.log('img onclick');
28184 this.fireEvent('click', this, e);
28196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28199 * @class Roo.bootstrap.dash.NumberBox
28200 * @extends Roo.bootstrap.Component
28201 * Bootstrap NumberBox class
28202 * @cfg {String} headline Box headline
28203 * @cfg {String} content Box content
28204 * @cfg {String} icon Box icon
28205 * @cfg {String} footer Footer text
28206 * @cfg {String} fhref Footer href
28209 * Create a new NumberBox
28210 * @param {Object} config The config object
28214 Roo.bootstrap.dash.NumberBox = function(config){
28215 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28219 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28228 getAutoCreate : function(){
28232 cls : 'small-box ',
28240 cls : 'roo-headline',
28241 html : this.headline
28245 cls : 'roo-content',
28246 html : this.content
28260 cls : 'ion ' + this.icon
28269 cls : 'small-box-footer',
28270 href : this.fhref || '#',
28274 cfg.cn.push(footer);
28281 onRender : function(ct,position){
28282 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28289 setHeadline: function (value)
28291 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28294 setFooter: function (value, href)
28296 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28299 this.el.select('a.small-box-footer',true).first().attr('href', href);
28304 setContent: function (value)
28306 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28309 initEvents: function()
28323 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28326 * @class Roo.bootstrap.dash.TabBox
28327 * @extends Roo.bootstrap.Component
28328 * Bootstrap TabBox class
28329 * @cfg {String} title Title of the TabBox
28330 * @cfg {String} icon Icon of the TabBox
28331 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28332 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28335 * Create a new TabBox
28336 * @param {Object} config The config object
28340 Roo.bootstrap.dash.TabBox = function(config){
28341 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28346 * When a pane is added
28347 * @param {Roo.bootstrap.dash.TabPane} pane
28351 * @event activatepane
28352 * When a pane is activated
28353 * @param {Roo.bootstrap.dash.TabPane} pane
28355 "activatepane" : true
28363 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28368 tabScrollable : false,
28370 getChildContainer : function()
28372 return this.el.select('.tab-content', true).first();
28375 getAutoCreate : function(){
28379 cls: 'pull-left header',
28387 cls: 'fa ' + this.icon
28393 cls: 'nav nav-tabs pull-right',
28399 if(this.tabScrollable){
28406 cls: 'nav nav-tabs pull-right',
28417 cls: 'nav-tabs-custom',
28422 cls: 'tab-content no-padding',
28430 initEvents : function()
28432 //Roo.log('add add pane handler');
28433 this.on('addpane', this.onAddPane, this);
28436 * Updates the box title
28437 * @param {String} html to set the title to.
28439 setTitle : function(value)
28441 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28443 onAddPane : function(pane)
28445 this.panes.push(pane);
28446 //Roo.log('addpane');
28448 // tabs are rendere left to right..
28449 if(!this.showtabs){
28453 var ctr = this.el.select('.nav-tabs', true).first();
28456 var existing = ctr.select('.nav-tab',true);
28457 var qty = existing.getCount();;
28460 var tab = ctr.createChild({
28462 cls : 'nav-tab' + (qty ? '' : ' active'),
28470 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28473 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28475 pane.el.addClass('active');
28480 onTabClick : function(ev,un,ob,pane)
28482 //Roo.log('tab - prev default');
28483 ev.preventDefault();
28486 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28487 pane.tab.addClass('active');
28488 //Roo.log(pane.title);
28489 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28490 // technically we should have a deactivate event.. but maybe add later.
28491 // and it should not de-activate the selected tab...
28492 this.fireEvent('activatepane', pane);
28493 pane.el.addClass('active');
28494 pane.fireEvent('activate');
28499 getActivePane : function()
28502 Roo.each(this.panes, function(p) {
28503 if(p.el.hasClass('active')){
28524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28526 * @class Roo.bootstrap.TabPane
28527 * @extends Roo.bootstrap.Component
28528 * Bootstrap TabPane class
28529 * @cfg {Boolean} active (false | true) Default false
28530 * @cfg {String} title title of panel
28534 * Create a new TabPane
28535 * @param {Object} config The config object
28538 Roo.bootstrap.dash.TabPane = function(config){
28539 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28545 * When a pane is activated
28546 * @param {Roo.bootstrap.dash.TabPane} pane
28553 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28558 // the tabBox that this is attached to.
28561 getAutoCreate : function()
28569 cfg.cls += ' active';
28574 initEvents : function()
28576 //Roo.log('trigger add pane handler');
28577 this.parent().fireEvent('addpane', this)
28581 * Updates the tab title
28582 * @param {String} html to set the title to.
28584 setTitle: function(str)
28590 this.tab.select('a', true).first().dom.innerHTML = str;
28607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28610 * @class Roo.bootstrap.menu.Menu
28611 * @extends Roo.bootstrap.Component
28612 * Bootstrap Menu class - container for Menu
28613 * @cfg {String} html Text of the menu
28614 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28615 * @cfg {String} icon Font awesome icon
28616 * @cfg {String} pos Menu align to (top | bottom) default bottom
28620 * Create a new Menu
28621 * @param {Object} config The config object
28625 Roo.bootstrap.menu.Menu = function(config){
28626 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28630 * @event beforeshow
28631 * Fires before this menu is displayed
28632 * @param {Roo.bootstrap.menu.Menu} this
28636 * @event beforehide
28637 * Fires before this menu is hidden
28638 * @param {Roo.bootstrap.menu.Menu} this
28643 * Fires after this menu is displayed
28644 * @param {Roo.bootstrap.menu.Menu} this
28649 * Fires after this menu is hidden
28650 * @param {Roo.bootstrap.menu.Menu} this
28655 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28656 * @param {Roo.bootstrap.menu.Menu} this
28657 * @param {Roo.EventObject} e
28664 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28668 weight : 'default',
28673 getChildContainer : function() {
28674 if(this.isSubMenu){
28678 return this.el.select('ul.dropdown-menu', true).first();
28681 getAutoCreate : function()
28686 cls : 'roo-menu-text',
28694 cls : 'fa ' + this.icon
28705 cls : 'dropdown-button btn btn-' + this.weight,
28710 cls : 'dropdown-toggle btn btn-' + this.weight,
28720 cls : 'dropdown-menu'
28726 if(this.pos == 'top'){
28727 cfg.cls += ' dropup';
28730 if(this.isSubMenu){
28733 cls : 'dropdown-menu'
28740 onRender : function(ct, position)
28742 this.isSubMenu = ct.hasClass('dropdown-submenu');
28744 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28747 initEvents : function()
28749 if(this.isSubMenu){
28753 this.hidden = true;
28755 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28756 this.triggerEl.on('click', this.onTriggerPress, this);
28758 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28759 this.buttonEl.on('click', this.onClick, this);
28765 if(this.isSubMenu){
28769 return this.el.select('ul.dropdown-menu', true).first();
28772 onClick : function(e)
28774 this.fireEvent("click", this, e);
28777 onTriggerPress : function(e)
28779 if (this.isVisible()) {
28786 isVisible : function(){
28787 return !this.hidden;
28792 this.fireEvent("beforeshow", this);
28794 this.hidden = false;
28795 this.el.addClass('open');
28797 Roo.get(document).on("mouseup", this.onMouseUp, this);
28799 this.fireEvent("show", this);
28806 this.fireEvent("beforehide", this);
28808 this.hidden = true;
28809 this.el.removeClass('open');
28811 Roo.get(document).un("mouseup", this.onMouseUp);
28813 this.fireEvent("hide", this);
28816 onMouseUp : function()
28830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28833 * @class Roo.bootstrap.menu.Item
28834 * @extends Roo.bootstrap.Component
28835 * Bootstrap MenuItem class
28836 * @cfg {Boolean} submenu (true | false) default false
28837 * @cfg {String} html text of the item
28838 * @cfg {String} href the link
28839 * @cfg {Boolean} disable (true | false) default false
28840 * @cfg {Boolean} preventDefault (true | false) default true
28841 * @cfg {String} icon Font awesome icon
28842 * @cfg {String} pos Submenu align to (left | right) default right
28846 * Create a new Item
28847 * @param {Object} config The config object
28851 Roo.bootstrap.menu.Item = function(config){
28852 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28856 * Fires when the mouse is hovering over this menu
28857 * @param {Roo.bootstrap.menu.Item} this
28858 * @param {Roo.EventObject} e
28863 * Fires when the mouse exits this menu
28864 * @param {Roo.bootstrap.menu.Item} this
28865 * @param {Roo.EventObject} e
28871 * The raw click event for the entire grid.
28872 * @param {Roo.EventObject} e
28878 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28883 preventDefault: true,
28888 getAutoCreate : function()
28893 cls : 'roo-menu-item-text',
28901 cls : 'fa ' + this.icon
28910 href : this.href || '#',
28917 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28921 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28923 if(this.pos == 'left'){
28924 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28931 initEvents : function()
28933 this.el.on('mouseover', this.onMouseOver, this);
28934 this.el.on('mouseout', this.onMouseOut, this);
28936 this.el.select('a', true).first().on('click', this.onClick, this);
28940 onClick : function(e)
28942 if(this.preventDefault){
28943 e.preventDefault();
28946 this.fireEvent("click", this, e);
28949 onMouseOver : function(e)
28951 if(this.submenu && this.pos == 'left'){
28952 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28955 this.fireEvent("mouseover", this, e);
28958 onMouseOut : function(e)
28960 this.fireEvent("mouseout", this, e);
28972 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28975 * @class Roo.bootstrap.menu.Separator
28976 * @extends Roo.bootstrap.Component
28977 * Bootstrap Separator class
28980 * Create a new Separator
28981 * @param {Object} config The config object
28985 Roo.bootstrap.menu.Separator = function(config){
28986 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28989 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28991 getAutoCreate : function(){
28994 cls: 'dropdown-divider divider'
29012 * @class Roo.bootstrap.Tooltip
29013 * Bootstrap Tooltip class
29014 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29015 * to determine which dom element triggers the tooltip.
29017 * It needs to add support for additional attributes like tooltip-position
29020 * Create a new Toolti
29021 * @param {Object} config The config object
29024 Roo.bootstrap.Tooltip = function(config){
29025 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29027 this.alignment = Roo.bootstrap.Tooltip.alignment;
29029 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29030 this.alignment = config.alignment;
29035 Roo.apply(Roo.bootstrap.Tooltip, {
29037 * @function init initialize tooltip monitoring.
29041 currentTip : false,
29042 currentRegion : false,
29048 Roo.get(document).on('mouseover', this.enter ,this);
29049 Roo.get(document).on('mouseout', this.leave, this);
29052 this.currentTip = new Roo.bootstrap.Tooltip();
29055 enter : function(ev)
29057 var dom = ev.getTarget();
29059 //Roo.log(['enter',dom]);
29060 var el = Roo.fly(dom);
29061 if (this.currentEl) {
29063 //Roo.log(this.currentEl);
29064 //Roo.log(this.currentEl.contains(dom));
29065 if (this.currentEl == el) {
29068 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29074 if (this.currentTip.el) {
29075 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29079 if(!el || el.dom == document){
29085 // you can not look for children, as if el is the body.. then everythign is the child..
29086 if (!el.attr('tooltip')) { //
29087 if (!el.select("[tooltip]").elements.length) {
29090 // is the mouse over this child...?
29091 bindEl = el.select("[tooltip]").first();
29092 var xy = ev.getXY();
29093 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29094 //Roo.log("not in region.");
29097 //Roo.log("child element over..");
29100 this.currentEl = bindEl;
29101 this.currentTip.bind(bindEl);
29102 this.currentRegion = Roo.lib.Region.getRegion(dom);
29103 this.currentTip.enter();
29106 leave : function(ev)
29108 var dom = ev.getTarget();
29109 //Roo.log(['leave',dom]);
29110 if (!this.currentEl) {
29115 if (dom != this.currentEl.dom) {
29118 var xy = ev.getXY();
29119 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29122 // only activate leave if mouse cursor is outside... bounding box..
29127 if (this.currentTip) {
29128 this.currentTip.leave();
29130 //Roo.log('clear currentEl');
29131 this.currentEl = false;
29136 'left' : ['r-l', [-2,0], 'right'],
29137 'right' : ['l-r', [2,0], 'left'],
29138 'bottom' : ['t-b', [0,2], 'top'],
29139 'top' : [ 'b-t', [0,-2], 'bottom']
29145 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29150 delay : null, // can be { show : 300 , hide: 500}
29154 hoverState : null, //???
29156 placement : 'bottom',
29160 getAutoCreate : function(){
29167 cls : 'tooltip-arrow arrow'
29170 cls : 'tooltip-inner'
29177 bind : function(el)
29182 initEvents : function()
29184 this.arrowEl = this.el.select('.arrow', true).first();
29185 this.innerEl = this.el.select('.tooltip-inner', true).first();
29188 enter : function () {
29190 if (this.timeout != null) {
29191 clearTimeout(this.timeout);
29194 this.hoverState = 'in';
29195 //Roo.log("enter - show");
29196 if (!this.delay || !this.delay.show) {
29201 this.timeout = setTimeout(function () {
29202 if (_t.hoverState == 'in') {
29205 }, this.delay.show);
29209 clearTimeout(this.timeout);
29211 this.hoverState = 'out';
29212 if (!this.delay || !this.delay.hide) {
29218 this.timeout = setTimeout(function () {
29219 //Roo.log("leave - timeout");
29221 if (_t.hoverState == 'out') {
29223 Roo.bootstrap.Tooltip.currentEl = false;
29228 show : function (msg)
29231 this.render(document.body);
29234 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29236 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29238 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29240 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29241 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29243 var placement = typeof this.placement == 'function' ?
29244 this.placement.call(this, this.el, on_el) :
29247 var autoToken = /\s?auto?\s?/i;
29248 var autoPlace = autoToken.test(placement);
29250 placement = placement.replace(autoToken, '') || 'top';
29254 //this.el.setXY([0,0]);
29256 //this.el.dom.style.display='block';
29258 //this.el.appendTo(on_el);
29260 var p = this.getPosition();
29261 var box = this.el.getBox();
29267 var align = this.alignment[placement];
29269 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29271 if(placement == 'top' || placement == 'bottom'){
29273 placement = 'right';
29276 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29277 placement = 'left';
29280 var scroll = Roo.select('body', true).first().getScroll();
29282 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29286 align = this.alignment[placement];
29288 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29292 this.el.alignTo(this.bindEl, align[0],align[1]);
29293 //var arrow = this.el.select('.arrow',true).first();
29294 //arrow.set(align[2],
29296 this.el.addClass(placement);
29297 this.el.addClass("bs-tooltip-"+ placement);
29299 this.el.addClass('in fade show');
29301 this.hoverState = null;
29303 if (this.el.hasClass('fade')) {
29318 //this.el.setXY([0,0]);
29319 this.el.removeClass(['show', 'in']);
29335 * @class Roo.bootstrap.LocationPicker
29336 * @extends Roo.bootstrap.Component
29337 * Bootstrap LocationPicker class
29338 * @cfg {Number} latitude Position when init default 0
29339 * @cfg {Number} longitude Position when init default 0
29340 * @cfg {Number} zoom default 15
29341 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29342 * @cfg {Boolean} mapTypeControl default false
29343 * @cfg {Boolean} disableDoubleClickZoom default false
29344 * @cfg {Boolean} scrollwheel default true
29345 * @cfg {Boolean} streetViewControl default false
29346 * @cfg {Number} radius default 0
29347 * @cfg {String} locationName
29348 * @cfg {Boolean} draggable default true
29349 * @cfg {Boolean} enableAutocomplete default false
29350 * @cfg {Boolean} enableReverseGeocode default true
29351 * @cfg {String} markerTitle
29354 * Create a new LocationPicker
29355 * @param {Object} config The config object
29359 Roo.bootstrap.LocationPicker = function(config){
29361 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29366 * Fires when the picker initialized.
29367 * @param {Roo.bootstrap.LocationPicker} this
29368 * @param {Google Location} location
29372 * @event positionchanged
29373 * Fires when the picker position changed.
29374 * @param {Roo.bootstrap.LocationPicker} this
29375 * @param {Google Location} location
29377 positionchanged : true,
29380 * Fires when the map resize.
29381 * @param {Roo.bootstrap.LocationPicker} this
29386 * Fires when the map show.
29387 * @param {Roo.bootstrap.LocationPicker} this
29392 * Fires when the map hide.
29393 * @param {Roo.bootstrap.LocationPicker} this
29398 * Fires when click the map.
29399 * @param {Roo.bootstrap.LocationPicker} this
29400 * @param {Map event} e
29404 * @event mapRightClick
29405 * Fires when right click the map.
29406 * @param {Roo.bootstrap.LocationPicker} this
29407 * @param {Map event} e
29409 mapRightClick : true,
29411 * @event markerClick
29412 * Fires when click the marker.
29413 * @param {Roo.bootstrap.LocationPicker} this
29414 * @param {Map event} e
29416 markerClick : true,
29418 * @event markerRightClick
29419 * Fires when right click the marker.
29420 * @param {Roo.bootstrap.LocationPicker} this
29421 * @param {Map event} e
29423 markerRightClick : true,
29425 * @event OverlayViewDraw
29426 * Fires when OverlayView Draw
29427 * @param {Roo.bootstrap.LocationPicker} this
29429 OverlayViewDraw : true,
29431 * @event OverlayViewOnAdd
29432 * Fires when OverlayView Draw
29433 * @param {Roo.bootstrap.LocationPicker} this
29435 OverlayViewOnAdd : true,
29437 * @event OverlayViewOnRemove
29438 * Fires when OverlayView Draw
29439 * @param {Roo.bootstrap.LocationPicker} this
29441 OverlayViewOnRemove : true,
29443 * @event OverlayViewShow
29444 * Fires when OverlayView Draw
29445 * @param {Roo.bootstrap.LocationPicker} this
29446 * @param {Pixel} cpx
29448 OverlayViewShow : true,
29450 * @event OverlayViewHide
29451 * Fires when OverlayView Draw
29452 * @param {Roo.bootstrap.LocationPicker} this
29454 OverlayViewHide : true,
29456 * @event loadexception
29457 * Fires when load google lib failed.
29458 * @param {Roo.bootstrap.LocationPicker} this
29460 loadexception : true
29465 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29467 gMapContext: false,
29473 mapTypeControl: false,
29474 disableDoubleClickZoom: false,
29476 streetViewControl: false,
29480 enableAutocomplete: false,
29481 enableReverseGeocode: true,
29484 getAutoCreate: function()
29489 cls: 'roo-location-picker'
29495 initEvents: function(ct, position)
29497 if(!this.el.getWidth() || this.isApplied()){
29501 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29506 initial: function()
29508 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29509 this.fireEvent('loadexception', this);
29513 if(!this.mapTypeId){
29514 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29517 this.gMapContext = this.GMapContext();
29519 this.initOverlayView();
29521 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29525 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29526 _this.setPosition(_this.gMapContext.marker.position);
29529 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29530 _this.fireEvent('mapClick', this, event);
29534 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29535 _this.fireEvent('mapRightClick', this, event);
29539 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29540 _this.fireEvent('markerClick', this, event);
29544 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29545 _this.fireEvent('markerRightClick', this, event);
29549 this.setPosition(this.gMapContext.location);
29551 this.fireEvent('initial', this, this.gMapContext.location);
29554 initOverlayView: function()
29558 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29562 _this.fireEvent('OverlayViewDraw', _this);
29567 _this.fireEvent('OverlayViewOnAdd', _this);
29570 onRemove: function()
29572 _this.fireEvent('OverlayViewOnRemove', _this);
29575 show: function(cpx)
29577 _this.fireEvent('OverlayViewShow', _this, cpx);
29582 _this.fireEvent('OverlayViewHide', _this);
29588 fromLatLngToContainerPixel: function(event)
29590 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29593 isApplied: function()
29595 return this.getGmapContext() == false ? false : true;
29598 getGmapContext: function()
29600 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29603 GMapContext: function()
29605 var position = new google.maps.LatLng(this.latitude, this.longitude);
29607 var _map = new google.maps.Map(this.el.dom, {
29610 mapTypeId: this.mapTypeId,
29611 mapTypeControl: this.mapTypeControl,
29612 disableDoubleClickZoom: this.disableDoubleClickZoom,
29613 scrollwheel: this.scrollwheel,
29614 streetViewControl: this.streetViewControl,
29615 locationName: this.locationName,
29616 draggable: this.draggable,
29617 enableAutocomplete: this.enableAutocomplete,
29618 enableReverseGeocode: this.enableReverseGeocode
29621 var _marker = new google.maps.Marker({
29622 position: position,
29624 title: this.markerTitle,
29625 draggable: this.draggable
29632 location: position,
29633 radius: this.radius,
29634 locationName: this.locationName,
29635 addressComponents: {
29636 formatted_address: null,
29637 addressLine1: null,
29638 addressLine2: null,
29640 streetNumber: null,
29644 stateOrProvince: null
29647 domContainer: this.el.dom,
29648 geodecoder: new google.maps.Geocoder()
29652 drawCircle: function(center, radius, options)
29654 if (this.gMapContext.circle != null) {
29655 this.gMapContext.circle.setMap(null);
29659 options = Roo.apply({}, options, {
29660 strokeColor: "#0000FF",
29661 strokeOpacity: .35,
29663 fillColor: "#0000FF",
29667 options.map = this.gMapContext.map;
29668 options.radius = radius;
29669 options.center = center;
29670 this.gMapContext.circle = new google.maps.Circle(options);
29671 return this.gMapContext.circle;
29677 setPosition: function(location)
29679 this.gMapContext.location = location;
29680 this.gMapContext.marker.setPosition(location);
29681 this.gMapContext.map.panTo(location);
29682 this.drawCircle(location, this.gMapContext.radius, {});
29686 if (this.gMapContext.settings.enableReverseGeocode) {
29687 this.gMapContext.geodecoder.geocode({
29688 latLng: this.gMapContext.location
29689 }, function(results, status) {
29691 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29692 _this.gMapContext.locationName = results[0].formatted_address;
29693 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29695 _this.fireEvent('positionchanged', this, location);
29702 this.fireEvent('positionchanged', this, location);
29707 google.maps.event.trigger(this.gMapContext.map, "resize");
29709 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29711 this.fireEvent('resize', this);
29714 setPositionByLatLng: function(latitude, longitude)
29716 this.setPosition(new google.maps.LatLng(latitude, longitude));
29719 getCurrentPosition: function()
29722 latitude: this.gMapContext.location.lat(),
29723 longitude: this.gMapContext.location.lng()
29727 getAddressName: function()
29729 return this.gMapContext.locationName;
29732 getAddressComponents: function()
29734 return this.gMapContext.addressComponents;
29737 address_component_from_google_geocode: function(address_components)
29741 for (var i = 0; i < address_components.length; i++) {
29742 var component = address_components[i];
29743 if (component.types.indexOf("postal_code") >= 0) {
29744 result.postalCode = component.short_name;
29745 } else if (component.types.indexOf("street_number") >= 0) {
29746 result.streetNumber = component.short_name;
29747 } else if (component.types.indexOf("route") >= 0) {
29748 result.streetName = component.short_name;
29749 } else if (component.types.indexOf("neighborhood") >= 0) {
29750 result.city = component.short_name;
29751 } else if (component.types.indexOf("locality") >= 0) {
29752 result.city = component.short_name;
29753 } else if (component.types.indexOf("sublocality") >= 0) {
29754 result.district = component.short_name;
29755 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29756 result.stateOrProvince = component.short_name;
29757 } else if (component.types.indexOf("country") >= 0) {
29758 result.country = component.short_name;
29762 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29763 result.addressLine2 = "";
29767 setZoomLevel: function(zoom)
29769 this.gMapContext.map.setZoom(zoom);
29782 this.fireEvent('show', this);
29793 this.fireEvent('hide', this);
29798 Roo.apply(Roo.bootstrap.LocationPicker, {
29800 OverlayView : function(map, options)
29802 options = options || {};
29809 * @class Roo.bootstrap.Alert
29810 * @extends Roo.bootstrap.Component
29811 * Bootstrap Alert class - shows an alert area box
29813 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29814 Enter a valid email address
29817 * @cfg {String} title The title of alert
29818 * @cfg {String} html The content of alert
29819 * @cfg {String} weight ( success | info | warning | danger )
29820 * @cfg {String} faicon font-awesomeicon
29823 * Create a new alert
29824 * @param {Object} config The config object
29828 Roo.bootstrap.Alert = function(config){
29829 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29833 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29840 getAutoCreate : function()
29849 cls : 'roo-alert-icon'
29854 cls : 'roo-alert-title',
29859 cls : 'roo-alert-text',
29866 cfg.cn[0].cls += ' fa ' + this.faicon;
29870 cfg.cls += ' alert-' + this.weight;
29876 initEvents: function()
29878 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29881 setTitle : function(str)
29883 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29886 setText : function(str)
29888 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29891 setWeight : function(weight)
29894 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29897 this.weight = weight;
29899 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29902 setIcon : function(icon)
29905 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29908 this.faicon = icon;
29910 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29931 * @class Roo.bootstrap.UploadCropbox
29932 * @extends Roo.bootstrap.Component
29933 * Bootstrap UploadCropbox class
29934 * @cfg {String} emptyText show when image has been loaded
29935 * @cfg {String} rotateNotify show when image too small to rotate
29936 * @cfg {Number} errorTimeout default 3000
29937 * @cfg {Number} minWidth default 300
29938 * @cfg {Number} minHeight default 300
29939 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29940 * @cfg {Boolean} isDocument (true|false) default false
29941 * @cfg {String} url action url
29942 * @cfg {String} paramName default 'imageUpload'
29943 * @cfg {String} method default POST
29944 * @cfg {Boolean} loadMask (true|false) default true
29945 * @cfg {Boolean} loadingText default 'Loading...'
29948 * Create a new UploadCropbox
29949 * @param {Object} config The config object
29952 Roo.bootstrap.UploadCropbox = function(config){
29953 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29957 * @event beforeselectfile
29958 * Fire before select file
29959 * @param {Roo.bootstrap.UploadCropbox} this
29961 "beforeselectfile" : true,
29964 * Fire after initEvent
29965 * @param {Roo.bootstrap.UploadCropbox} this
29970 * Fire after initEvent
29971 * @param {Roo.bootstrap.UploadCropbox} this
29972 * @param {String} data
29977 * Fire when preparing the file data
29978 * @param {Roo.bootstrap.UploadCropbox} this
29979 * @param {Object} file
29984 * Fire when get exception
29985 * @param {Roo.bootstrap.UploadCropbox} this
29986 * @param {XMLHttpRequest} xhr
29988 "exception" : true,
29990 * @event beforeloadcanvas
29991 * Fire before load the canvas
29992 * @param {Roo.bootstrap.UploadCropbox} this
29993 * @param {String} src
29995 "beforeloadcanvas" : true,
29998 * Fire when trash image
29999 * @param {Roo.bootstrap.UploadCropbox} this
30004 * Fire when download the image
30005 * @param {Roo.bootstrap.UploadCropbox} this
30009 * @event footerbuttonclick
30010 * Fire when footerbuttonclick
30011 * @param {Roo.bootstrap.UploadCropbox} this
30012 * @param {String} type
30014 "footerbuttonclick" : true,
30018 * @param {Roo.bootstrap.UploadCropbox} this
30023 * Fire when rotate the image
30024 * @param {Roo.bootstrap.UploadCropbox} this
30025 * @param {String} pos
30030 * Fire when inspect the file
30031 * @param {Roo.bootstrap.UploadCropbox} this
30032 * @param {Object} file
30037 * Fire when xhr upload the file
30038 * @param {Roo.bootstrap.UploadCropbox} this
30039 * @param {Object} data
30044 * Fire when arrange the file data
30045 * @param {Roo.bootstrap.UploadCropbox} this
30046 * @param {Object} formData
30051 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30054 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30056 emptyText : 'Click to upload image',
30057 rotateNotify : 'Image is too small to rotate',
30058 errorTimeout : 3000,
30072 cropType : 'image/jpeg',
30074 canvasLoaded : false,
30075 isDocument : false,
30077 paramName : 'imageUpload',
30079 loadingText : 'Loading...',
30082 getAutoCreate : function()
30086 cls : 'roo-upload-cropbox',
30090 cls : 'roo-upload-cropbox-selector',
30095 cls : 'roo-upload-cropbox-body',
30096 style : 'cursor:pointer',
30100 cls : 'roo-upload-cropbox-preview'
30104 cls : 'roo-upload-cropbox-thumb'
30108 cls : 'roo-upload-cropbox-empty-notify',
30109 html : this.emptyText
30113 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30114 html : this.rotateNotify
30120 cls : 'roo-upload-cropbox-footer',
30123 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30133 onRender : function(ct, position)
30135 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30137 if (this.buttons.length) {
30139 Roo.each(this.buttons, function(bb) {
30141 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30143 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30149 this.maskEl = this.el;
30153 initEvents : function()
30155 this.urlAPI = (window.createObjectURL && window) ||
30156 (window.URL && URL.revokeObjectURL && URL) ||
30157 (window.webkitURL && webkitURL);
30159 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30160 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30162 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30163 this.selectorEl.hide();
30165 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30166 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30168 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30169 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30170 this.thumbEl.hide();
30172 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30173 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30175 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30176 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30177 this.errorEl.hide();
30179 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30180 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30181 this.footerEl.hide();
30183 this.setThumbBoxSize();
30189 this.fireEvent('initial', this);
30196 window.addEventListener("resize", function() { _this.resize(); } );
30198 this.bodyEl.on('click', this.beforeSelectFile, this);
30201 this.bodyEl.on('touchstart', this.onTouchStart, this);
30202 this.bodyEl.on('touchmove', this.onTouchMove, this);
30203 this.bodyEl.on('touchend', this.onTouchEnd, this);
30207 this.bodyEl.on('mousedown', this.onMouseDown, this);
30208 this.bodyEl.on('mousemove', this.onMouseMove, this);
30209 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30210 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30211 Roo.get(document).on('mouseup', this.onMouseUp, this);
30214 this.selectorEl.on('change', this.onFileSelected, this);
30220 this.baseScale = 1;
30222 this.baseRotate = 1;
30223 this.dragable = false;
30224 this.pinching = false;
30227 this.cropData = false;
30228 this.notifyEl.dom.innerHTML = this.emptyText;
30230 this.selectorEl.dom.value = '';
30234 resize : function()
30236 if(this.fireEvent('resize', this) != false){
30237 this.setThumbBoxPosition();
30238 this.setCanvasPosition();
30242 onFooterButtonClick : function(e, el, o, type)
30245 case 'rotate-left' :
30246 this.onRotateLeft(e);
30248 case 'rotate-right' :
30249 this.onRotateRight(e);
30252 this.beforeSelectFile(e);
30267 this.fireEvent('footerbuttonclick', this, type);
30270 beforeSelectFile : function(e)
30272 e.preventDefault();
30274 if(this.fireEvent('beforeselectfile', this) != false){
30275 this.selectorEl.dom.click();
30279 onFileSelected : function(e)
30281 e.preventDefault();
30283 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30287 var file = this.selectorEl.dom.files[0];
30289 if(this.fireEvent('inspect', this, file) != false){
30290 this.prepare(file);
30295 trash : function(e)
30297 this.fireEvent('trash', this);
30300 download : function(e)
30302 this.fireEvent('download', this);
30305 loadCanvas : function(src)
30307 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30311 this.imageEl = document.createElement('img');
30315 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30317 this.imageEl.src = src;
30321 onLoadCanvas : function()
30323 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30324 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30326 this.bodyEl.un('click', this.beforeSelectFile, this);
30328 this.notifyEl.hide();
30329 this.thumbEl.show();
30330 this.footerEl.show();
30332 this.baseRotateLevel();
30334 if(this.isDocument){
30335 this.setThumbBoxSize();
30338 this.setThumbBoxPosition();
30340 this.baseScaleLevel();
30346 this.canvasLoaded = true;
30349 this.maskEl.unmask();
30354 setCanvasPosition : function()
30356 if(!this.canvasEl){
30360 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30361 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30363 this.previewEl.setLeft(pw);
30364 this.previewEl.setTop(ph);
30368 onMouseDown : function(e)
30372 this.dragable = true;
30373 this.pinching = false;
30375 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30376 this.dragable = false;
30380 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30381 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30385 onMouseMove : function(e)
30389 if(!this.canvasLoaded){
30393 if (!this.dragable){
30397 var minX = Math.ceil(this.thumbEl.getLeft(true));
30398 var minY = Math.ceil(this.thumbEl.getTop(true));
30400 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30401 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30403 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30404 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30406 x = x - this.mouseX;
30407 y = y - this.mouseY;
30409 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30410 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30412 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30413 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30415 this.previewEl.setLeft(bgX);
30416 this.previewEl.setTop(bgY);
30418 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30419 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30422 onMouseUp : function(e)
30426 this.dragable = false;
30429 onMouseWheel : function(e)
30433 this.startScale = this.scale;
30435 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30437 if(!this.zoomable()){
30438 this.scale = this.startScale;
30447 zoomable : function()
30449 var minScale = this.thumbEl.getWidth() / this.minWidth;
30451 if(this.minWidth < this.minHeight){
30452 minScale = this.thumbEl.getHeight() / this.minHeight;
30455 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30456 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30460 (this.rotate == 0 || this.rotate == 180) &&
30462 width > this.imageEl.OriginWidth ||
30463 height > this.imageEl.OriginHeight ||
30464 (width < this.minWidth && height < this.minHeight)
30472 (this.rotate == 90 || this.rotate == 270) &&
30474 width > this.imageEl.OriginWidth ||
30475 height > this.imageEl.OriginHeight ||
30476 (width < this.minHeight && height < this.minWidth)
30483 !this.isDocument &&
30484 (this.rotate == 0 || this.rotate == 180) &&
30486 width < this.minWidth ||
30487 width > this.imageEl.OriginWidth ||
30488 height < this.minHeight ||
30489 height > this.imageEl.OriginHeight
30496 !this.isDocument &&
30497 (this.rotate == 90 || this.rotate == 270) &&
30499 width < this.minHeight ||
30500 width > this.imageEl.OriginWidth ||
30501 height < this.minWidth ||
30502 height > this.imageEl.OriginHeight
30512 onRotateLeft : function(e)
30514 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30516 var minScale = this.thumbEl.getWidth() / this.minWidth;
30518 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30519 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30521 this.startScale = this.scale;
30523 while (this.getScaleLevel() < minScale){
30525 this.scale = this.scale + 1;
30527 if(!this.zoomable()){
30532 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30533 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30538 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30545 this.scale = this.startScale;
30547 this.onRotateFail();
30552 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30554 if(this.isDocument){
30555 this.setThumbBoxSize();
30556 this.setThumbBoxPosition();
30557 this.setCanvasPosition();
30562 this.fireEvent('rotate', this, 'left');
30566 onRotateRight : function(e)
30568 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30570 var minScale = this.thumbEl.getWidth() / this.minWidth;
30572 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30573 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30575 this.startScale = this.scale;
30577 while (this.getScaleLevel() < minScale){
30579 this.scale = this.scale + 1;
30581 if(!this.zoomable()){
30586 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30587 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30592 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30599 this.scale = this.startScale;
30601 this.onRotateFail();
30606 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30608 if(this.isDocument){
30609 this.setThumbBoxSize();
30610 this.setThumbBoxPosition();
30611 this.setCanvasPosition();
30616 this.fireEvent('rotate', this, 'right');
30619 onRotateFail : function()
30621 this.errorEl.show(true);
30625 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30630 this.previewEl.dom.innerHTML = '';
30632 var canvasEl = document.createElement("canvas");
30634 var contextEl = canvasEl.getContext("2d");
30636 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30637 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30638 var center = this.imageEl.OriginWidth / 2;
30640 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30641 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30642 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30643 center = this.imageEl.OriginHeight / 2;
30646 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30648 contextEl.translate(center, center);
30649 contextEl.rotate(this.rotate * Math.PI / 180);
30651 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30653 this.canvasEl = document.createElement("canvas");
30655 this.contextEl = this.canvasEl.getContext("2d");
30657 switch (this.rotate) {
30660 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30661 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30663 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30668 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30669 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30671 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30672 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);
30676 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30681 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30682 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30684 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30685 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);
30689 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);
30694 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30695 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30697 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30698 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30702 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);
30709 this.previewEl.appendChild(this.canvasEl);
30711 this.setCanvasPosition();
30716 if(!this.canvasLoaded){
30720 var imageCanvas = document.createElement("canvas");
30722 var imageContext = imageCanvas.getContext("2d");
30724 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30725 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30727 var center = imageCanvas.width / 2;
30729 imageContext.translate(center, center);
30731 imageContext.rotate(this.rotate * Math.PI / 180);
30733 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30735 var canvas = document.createElement("canvas");
30737 var context = canvas.getContext("2d");
30739 canvas.width = this.minWidth;
30740 canvas.height = this.minHeight;
30742 switch (this.rotate) {
30745 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30746 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30748 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30749 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30751 var targetWidth = this.minWidth - 2 * x;
30752 var targetHeight = this.minHeight - 2 * y;
30756 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30757 scale = targetWidth / width;
30760 if(x > 0 && y == 0){
30761 scale = targetHeight / height;
30764 if(x > 0 && y > 0){
30765 scale = targetWidth / width;
30767 if(width < height){
30768 scale = targetHeight / height;
30772 context.scale(scale, scale);
30774 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30775 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30777 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30778 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30780 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30785 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30786 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30788 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30789 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30791 var targetWidth = this.minWidth - 2 * x;
30792 var targetHeight = this.minHeight - 2 * y;
30796 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30797 scale = targetWidth / width;
30800 if(x > 0 && y == 0){
30801 scale = targetHeight / height;
30804 if(x > 0 && y > 0){
30805 scale = targetWidth / width;
30807 if(width < height){
30808 scale = targetHeight / height;
30812 context.scale(scale, scale);
30814 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30815 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30817 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30818 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30820 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30822 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30827 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30828 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30830 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30831 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30833 var targetWidth = this.minWidth - 2 * x;
30834 var targetHeight = this.minHeight - 2 * y;
30838 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30839 scale = targetWidth / width;
30842 if(x > 0 && y == 0){
30843 scale = targetHeight / height;
30846 if(x > 0 && y > 0){
30847 scale = targetWidth / width;
30849 if(width < height){
30850 scale = targetHeight / height;
30854 context.scale(scale, scale);
30856 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30857 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30859 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30860 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30862 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30863 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30865 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30870 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30871 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30873 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30874 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30876 var targetWidth = this.minWidth - 2 * x;
30877 var targetHeight = this.minHeight - 2 * y;
30881 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30882 scale = targetWidth / width;
30885 if(x > 0 && y == 0){
30886 scale = targetHeight / height;
30889 if(x > 0 && y > 0){
30890 scale = targetWidth / width;
30892 if(width < height){
30893 scale = targetHeight / height;
30897 context.scale(scale, scale);
30899 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30900 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30902 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30903 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30905 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30907 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30914 this.cropData = canvas.toDataURL(this.cropType);
30916 if(this.fireEvent('crop', this, this.cropData) !== false){
30917 this.process(this.file, this.cropData);
30924 setThumbBoxSize : function()
30928 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30929 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30930 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30932 this.minWidth = width;
30933 this.minHeight = height;
30935 if(this.rotate == 90 || this.rotate == 270){
30936 this.minWidth = height;
30937 this.minHeight = width;
30942 width = Math.ceil(this.minWidth * height / this.minHeight);
30944 if(this.minWidth > this.minHeight){
30946 height = Math.ceil(this.minHeight * width / this.minWidth);
30949 this.thumbEl.setStyle({
30950 width : width + 'px',
30951 height : height + 'px'
30958 setThumbBoxPosition : function()
30960 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30961 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30963 this.thumbEl.setLeft(x);
30964 this.thumbEl.setTop(y);
30968 baseRotateLevel : function()
30970 this.baseRotate = 1;
30973 typeof(this.exif) != 'undefined' &&
30974 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30975 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30977 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30980 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30984 baseScaleLevel : function()
30988 if(this.isDocument){
30990 if(this.baseRotate == 6 || this.baseRotate == 8){
30992 height = this.thumbEl.getHeight();
30993 this.baseScale = height / this.imageEl.OriginWidth;
30995 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30996 width = this.thumbEl.getWidth();
30997 this.baseScale = width / this.imageEl.OriginHeight;
31003 height = this.thumbEl.getHeight();
31004 this.baseScale = height / this.imageEl.OriginHeight;
31006 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31007 width = this.thumbEl.getWidth();
31008 this.baseScale = width / this.imageEl.OriginWidth;
31014 if(this.baseRotate == 6 || this.baseRotate == 8){
31016 width = this.thumbEl.getHeight();
31017 this.baseScale = width / this.imageEl.OriginHeight;
31019 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31020 height = this.thumbEl.getWidth();
31021 this.baseScale = height / this.imageEl.OriginHeight;
31024 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31025 height = this.thumbEl.getWidth();
31026 this.baseScale = height / this.imageEl.OriginHeight;
31028 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31029 width = this.thumbEl.getHeight();
31030 this.baseScale = width / this.imageEl.OriginWidth;
31037 width = this.thumbEl.getWidth();
31038 this.baseScale = width / this.imageEl.OriginWidth;
31040 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31041 height = this.thumbEl.getHeight();
31042 this.baseScale = height / this.imageEl.OriginHeight;
31045 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31047 height = this.thumbEl.getHeight();
31048 this.baseScale = height / this.imageEl.OriginHeight;
31050 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31051 width = this.thumbEl.getWidth();
31052 this.baseScale = width / this.imageEl.OriginWidth;
31060 getScaleLevel : function()
31062 return this.baseScale * Math.pow(1.1, this.scale);
31065 onTouchStart : function(e)
31067 if(!this.canvasLoaded){
31068 this.beforeSelectFile(e);
31072 var touches = e.browserEvent.touches;
31078 if(touches.length == 1){
31079 this.onMouseDown(e);
31083 if(touches.length != 2){
31089 for(var i = 0, finger; finger = touches[i]; i++){
31090 coords.push(finger.pageX, finger.pageY);
31093 var x = Math.pow(coords[0] - coords[2], 2);
31094 var y = Math.pow(coords[1] - coords[3], 2);
31096 this.startDistance = Math.sqrt(x + y);
31098 this.startScale = this.scale;
31100 this.pinching = true;
31101 this.dragable = false;
31105 onTouchMove : function(e)
31107 if(!this.pinching && !this.dragable){
31111 var touches = e.browserEvent.touches;
31118 this.onMouseMove(e);
31124 for(var i = 0, finger; finger = touches[i]; i++){
31125 coords.push(finger.pageX, finger.pageY);
31128 var x = Math.pow(coords[0] - coords[2], 2);
31129 var y = Math.pow(coords[1] - coords[3], 2);
31131 this.endDistance = Math.sqrt(x + y);
31133 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31135 if(!this.zoomable()){
31136 this.scale = this.startScale;
31144 onTouchEnd : function(e)
31146 this.pinching = false;
31147 this.dragable = false;
31151 process : function(file, crop)
31154 this.maskEl.mask(this.loadingText);
31157 this.xhr = new XMLHttpRequest();
31159 file.xhr = this.xhr;
31161 this.xhr.open(this.method, this.url, true);
31164 "Accept": "application/json",
31165 "Cache-Control": "no-cache",
31166 "X-Requested-With": "XMLHttpRequest"
31169 for (var headerName in headers) {
31170 var headerValue = headers[headerName];
31172 this.xhr.setRequestHeader(headerName, headerValue);
31178 this.xhr.onload = function()
31180 _this.xhrOnLoad(_this.xhr);
31183 this.xhr.onerror = function()
31185 _this.xhrOnError(_this.xhr);
31188 var formData = new FormData();
31190 formData.append('returnHTML', 'NO');
31193 formData.append('crop', crop);
31196 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31197 formData.append(this.paramName, file, file.name);
31200 if(typeof(file.filename) != 'undefined'){
31201 formData.append('filename', file.filename);
31204 if(typeof(file.mimetype) != 'undefined'){
31205 formData.append('mimetype', file.mimetype);
31208 if(this.fireEvent('arrange', this, formData) != false){
31209 this.xhr.send(formData);
31213 xhrOnLoad : function(xhr)
31216 this.maskEl.unmask();
31219 if (xhr.readyState !== 4) {
31220 this.fireEvent('exception', this, xhr);
31224 var response = Roo.decode(xhr.responseText);
31226 if(!response.success){
31227 this.fireEvent('exception', this, xhr);
31231 var response = Roo.decode(xhr.responseText);
31233 this.fireEvent('upload', this, response);
31237 xhrOnError : function()
31240 this.maskEl.unmask();
31243 Roo.log('xhr on error');
31245 var response = Roo.decode(xhr.responseText);
31251 prepare : function(file)
31254 this.maskEl.mask(this.loadingText);
31260 if(typeof(file) === 'string'){
31261 this.loadCanvas(file);
31265 if(!file || !this.urlAPI){
31270 this.cropType = file.type;
31274 if(this.fireEvent('prepare', this, this.file) != false){
31276 var reader = new FileReader();
31278 reader.onload = function (e) {
31279 if (e.target.error) {
31280 Roo.log(e.target.error);
31284 var buffer = e.target.result,
31285 dataView = new DataView(buffer),
31287 maxOffset = dataView.byteLength - 4,
31291 if (dataView.getUint16(0) === 0xffd8) {
31292 while (offset < maxOffset) {
31293 markerBytes = dataView.getUint16(offset);
31295 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31296 markerLength = dataView.getUint16(offset + 2) + 2;
31297 if (offset + markerLength > dataView.byteLength) {
31298 Roo.log('Invalid meta data: Invalid segment size.');
31302 if(markerBytes == 0xffe1){
31303 _this.parseExifData(
31310 offset += markerLength;
31320 var url = _this.urlAPI.createObjectURL(_this.file);
31322 _this.loadCanvas(url);
31327 reader.readAsArrayBuffer(this.file);
31333 parseExifData : function(dataView, offset, length)
31335 var tiffOffset = offset + 10,
31339 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31340 // No Exif data, might be XMP data instead
31344 // Check for the ASCII code for "Exif" (0x45786966):
31345 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31346 // No Exif data, might be XMP data instead
31349 if (tiffOffset + 8 > dataView.byteLength) {
31350 Roo.log('Invalid Exif data: Invalid segment size.');
31353 // Check for the two null bytes:
31354 if (dataView.getUint16(offset + 8) !== 0x0000) {
31355 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31358 // Check the byte alignment:
31359 switch (dataView.getUint16(tiffOffset)) {
31361 littleEndian = true;
31364 littleEndian = false;
31367 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31370 // Check for the TIFF tag marker (0x002A):
31371 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31372 Roo.log('Invalid Exif data: Missing TIFF marker.');
31375 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31376 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31378 this.parseExifTags(
31381 tiffOffset + dirOffset,
31386 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31391 if (dirOffset + 6 > dataView.byteLength) {
31392 Roo.log('Invalid Exif data: Invalid directory offset.');
31395 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31396 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31397 if (dirEndOffset + 4 > dataView.byteLength) {
31398 Roo.log('Invalid Exif data: Invalid directory size.');
31401 for (i = 0; i < tagsNumber; i += 1) {
31405 dirOffset + 2 + 12 * i, // tag offset
31409 // Return the offset to the next directory:
31410 return dataView.getUint32(dirEndOffset, littleEndian);
31413 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31415 var tag = dataView.getUint16(offset, littleEndian);
31417 this.exif[tag] = this.getExifValue(
31421 dataView.getUint16(offset + 2, littleEndian), // tag type
31422 dataView.getUint32(offset + 4, littleEndian), // tag length
31427 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31429 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31438 Roo.log('Invalid Exif data: Invalid tag type.');
31442 tagSize = tagType.size * length;
31443 // Determine if the value is contained in the dataOffset bytes,
31444 // or if the value at the dataOffset is a pointer to the actual data:
31445 dataOffset = tagSize > 4 ?
31446 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31447 if (dataOffset + tagSize > dataView.byteLength) {
31448 Roo.log('Invalid Exif data: Invalid data offset.');
31451 if (length === 1) {
31452 return tagType.getValue(dataView, dataOffset, littleEndian);
31455 for (i = 0; i < length; i += 1) {
31456 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31459 if (tagType.ascii) {
31461 // Concatenate the chars:
31462 for (i = 0; i < values.length; i += 1) {
31464 // Ignore the terminating NULL byte(s):
31465 if (c === '\u0000') {
31477 Roo.apply(Roo.bootstrap.UploadCropbox, {
31479 'Orientation': 0x0112
31483 1: 0, //'top-left',
31485 3: 180, //'bottom-right',
31486 // 4: 'bottom-left',
31488 6: 90, //'right-top',
31489 // 7: 'right-bottom',
31490 8: 270 //'left-bottom'
31494 // byte, 8-bit unsigned int:
31496 getValue: function (dataView, dataOffset) {
31497 return dataView.getUint8(dataOffset);
31501 // ascii, 8-bit byte:
31503 getValue: function (dataView, dataOffset) {
31504 return String.fromCharCode(dataView.getUint8(dataOffset));
31509 // short, 16 bit int:
31511 getValue: function (dataView, dataOffset, littleEndian) {
31512 return dataView.getUint16(dataOffset, littleEndian);
31516 // long, 32 bit int:
31518 getValue: function (dataView, dataOffset, littleEndian) {
31519 return dataView.getUint32(dataOffset, littleEndian);
31523 // rational = two long values, first is numerator, second is denominator:
31525 getValue: function (dataView, dataOffset, littleEndian) {
31526 return dataView.getUint32(dataOffset, littleEndian) /
31527 dataView.getUint32(dataOffset + 4, littleEndian);
31531 // slong, 32 bit signed int:
31533 getValue: function (dataView, dataOffset, littleEndian) {
31534 return dataView.getInt32(dataOffset, littleEndian);
31538 // srational, two slongs, first is numerator, second is denominator:
31540 getValue: function (dataView, dataOffset, littleEndian) {
31541 return dataView.getInt32(dataOffset, littleEndian) /
31542 dataView.getInt32(dataOffset + 4, littleEndian);
31552 cls : 'btn-group roo-upload-cropbox-rotate-left',
31553 action : 'rotate-left',
31557 cls : 'btn btn-default',
31558 html : '<i class="fa fa-undo"></i>'
31564 cls : 'btn-group roo-upload-cropbox-picture',
31565 action : 'picture',
31569 cls : 'btn btn-default',
31570 html : '<i class="fa fa-picture-o"></i>'
31576 cls : 'btn-group roo-upload-cropbox-rotate-right',
31577 action : 'rotate-right',
31581 cls : 'btn btn-default',
31582 html : '<i class="fa fa-repeat"></i>'
31590 cls : 'btn-group roo-upload-cropbox-rotate-left',
31591 action : 'rotate-left',
31595 cls : 'btn btn-default',
31596 html : '<i class="fa fa-undo"></i>'
31602 cls : 'btn-group roo-upload-cropbox-download',
31603 action : 'download',
31607 cls : 'btn btn-default',
31608 html : '<i class="fa fa-download"></i>'
31614 cls : 'btn-group roo-upload-cropbox-crop',
31619 cls : 'btn btn-default',
31620 html : '<i class="fa fa-crop"></i>'
31626 cls : 'btn-group roo-upload-cropbox-trash',
31631 cls : 'btn btn-default',
31632 html : '<i class="fa fa-trash"></i>'
31638 cls : 'btn-group roo-upload-cropbox-rotate-right',
31639 action : 'rotate-right',
31643 cls : 'btn btn-default',
31644 html : '<i class="fa fa-repeat"></i>'
31652 cls : 'btn-group roo-upload-cropbox-rotate-left',
31653 action : 'rotate-left',
31657 cls : 'btn btn-default',
31658 html : '<i class="fa fa-undo"></i>'
31664 cls : 'btn-group roo-upload-cropbox-rotate-right',
31665 action : 'rotate-right',
31669 cls : 'btn btn-default',
31670 html : '<i class="fa fa-repeat"></i>'
31683 * @class Roo.bootstrap.DocumentManager
31684 * @extends Roo.bootstrap.Component
31685 * Bootstrap DocumentManager class
31686 * @cfg {String} paramName default 'imageUpload'
31687 * @cfg {String} toolTipName default 'filename'
31688 * @cfg {String} method default POST
31689 * @cfg {String} url action url
31690 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31691 * @cfg {Boolean} multiple multiple upload default true
31692 * @cfg {Number} thumbSize default 300
31693 * @cfg {String} fieldLabel
31694 * @cfg {Number} labelWidth default 4
31695 * @cfg {String} labelAlign (left|top) default left
31696 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31697 * @cfg {Number} labellg set the width of label (1-12)
31698 * @cfg {Number} labelmd set the width of label (1-12)
31699 * @cfg {Number} labelsm set the width of label (1-12)
31700 * @cfg {Number} labelxs set the width of label (1-12)
31703 * Create a new DocumentManager
31704 * @param {Object} config The config object
31707 Roo.bootstrap.DocumentManager = function(config){
31708 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31711 this.delegates = [];
31716 * Fire when initial the DocumentManager
31717 * @param {Roo.bootstrap.DocumentManager} this
31722 * inspect selected file
31723 * @param {Roo.bootstrap.DocumentManager} this
31724 * @param {File} file
31729 * Fire when xhr load exception
31730 * @param {Roo.bootstrap.DocumentManager} this
31731 * @param {XMLHttpRequest} xhr
31733 "exception" : true,
31735 * @event afterupload
31736 * Fire when xhr load exception
31737 * @param {Roo.bootstrap.DocumentManager} this
31738 * @param {XMLHttpRequest} xhr
31740 "afterupload" : true,
31743 * prepare the form data
31744 * @param {Roo.bootstrap.DocumentManager} this
31745 * @param {Object} formData
31750 * Fire when remove the file
31751 * @param {Roo.bootstrap.DocumentManager} this
31752 * @param {Object} file
31757 * Fire after refresh the file
31758 * @param {Roo.bootstrap.DocumentManager} this
31763 * Fire after click the image
31764 * @param {Roo.bootstrap.DocumentManager} this
31765 * @param {Object} file
31770 * Fire when upload a image and editable set to true
31771 * @param {Roo.bootstrap.DocumentManager} this
31772 * @param {Object} file
31776 * @event beforeselectfile
31777 * Fire before select file
31778 * @param {Roo.bootstrap.DocumentManager} this
31780 "beforeselectfile" : true,
31783 * Fire before process file
31784 * @param {Roo.bootstrap.DocumentManager} this
31785 * @param {Object} file
31789 * @event previewrendered
31790 * Fire when preview rendered
31791 * @param {Roo.bootstrap.DocumentManager} this
31792 * @param {Object} file
31794 "previewrendered" : true,
31797 "previewResize" : true
31802 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31811 paramName : 'imageUpload',
31812 toolTipName : 'filename',
31815 labelAlign : 'left',
31825 getAutoCreate : function()
31827 var managerWidget = {
31829 cls : 'roo-document-manager',
31833 cls : 'roo-document-manager-selector',
31838 cls : 'roo-document-manager-uploader',
31842 cls : 'roo-document-manager-upload-btn',
31843 html : '<i class="fa fa-plus"></i>'
31854 cls : 'column col-md-12',
31859 if(this.fieldLabel.length){
31864 cls : 'column col-md-12',
31865 html : this.fieldLabel
31869 cls : 'column col-md-12',
31874 if(this.labelAlign == 'left'){
31879 html : this.fieldLabel
31888 if(this.labelWidth > 12){
31889 content[0].style = "width: " + this.labelWidth + 'px';
31892 if(this.labelWidth < 13 && this.labelmd == 0){
31893 this.labelmd = this.labelWidth;
31896 if(this.labellg > 0){
31897 content[0].cls += ' col-lg-' + this.labellg;
31898 content[1].cls += ' col-lg-' + (12 - this.labellg);
31901 if(this.labelmd > 0){
31902 content[0].cls += ' col-md-' + this.labelmd;
31903 content[1].cls += ' col-md-' + (12 - this.labelmd);
31906 if(this.labelsm > 0){
31907 content[0].cls += ' col-sm-' + this.labelsm;
31908 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31911 if(this.labelxs > 0){
31912 content[0].cls += ' col-xs-' + this.labelxs;
31913 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31921 cls : 'row clearfix',
31929 initEvents : function()
31931 this.managerEl = this.el.select('.roo-document-manager', true).first();
31932 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31934 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31935 this.selectorEl.hide();
31938 this.selectorEl.attr('multiple', 'multiple');
31941 this.selectorEl.on('change', this.onFileSelected, this);
31943 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31944 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31946 this.uploader.on('click', this.onUploaderClick, this);
31948 this.renderProgressDialog();
31952 window.addEventListener("resize", function() { _this.refresh(); } );
31954 this.fireEvent('initial', this);
31957 renderProgressDialog : function()
31961 this.progressDialog = new Roo.bootstrap.Modal({
31962 cls : 'roo-document-manager-progress-dialog',
31963 allow_close : false,
31974 btnclick : function() {
31975 _this.uploadCancel();
31981 this.progressDialog.render(Roo.get(document.body));
31983 this.progress = new Roo.bootstrap.Progress({
31984 cls : 'roo-document-manager-progress',
31989 this.progress.render(this.progressDialog.getChildContainer());
31991 this.progressBar = new Roo.bootstrap.ProgressBar({
31992 cls : 'roo-document-manager-progress-bar',
31995 aria_valuemax : 12,
31999 this.progressBar.render(this.progress.getChildContainer());
32002 onUploaderClick : function(e)
32004 e.preventDefault();
32006 if(this.fireEvent('beforeselectfile', this) != false){
32007 this.selectorEl.dom.click();
32012 onFileSelected : function(e)
32014 e.preventDefault();
32016 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32020 Roo.each(this.selectorEl.dom.files, function(file){
32021 if(this.fireEvent('inspect', this, file) != false){
32022 this.files.push(file);
32032 this.selectorEl.dom.value = '';
32034 if(!this.files || !this.files.length){
32038 if(this.boxes > 0 && this.files.length > this.boxes){
32039 this.files = this.files.slice(0, this.boxes);
32042 this.uploader.show();
32044 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32045 this.uploader.hide();
32054 Roo.each(this.files, function(file){
32056 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32057 var f = this.renderPreview(file);
32062 if(file.type.indexOf('image') != -1){
32063 this.delegates.push(
32065 _this.process(file);
32066 }).createDelegate(this)
32074 _this.process(file);
32075 }).createDelegate(this)
32080 this.files = files;
32082 this.delegates = this.delegates.concat(docs);
32084 if(!this.delegates.length){
32089 this.progressBar.aria_valuemax = this.delegates.length;
32096 arrange : function()
32098 if(!this.delegates.length){
32099 this.progressDialog.hide();
32104 var delegate = this.delegates.shift();
32106 this.progressDialog.show();
32108 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32110 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32115 refresh : function()
32117 this.uploader.show();
32119 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32120 this.uploader.hide();
32123 Roo.isTouch ? this.closable(false) : this.closable(true);
32125 this.fireEvent('refresh', this);
32128 onRemove : function(e, el, o)
32130 e.preventDefault();
32132 this.fireEvent('remove', this, o);
32136 remove : function(o)
32140 Roo.each(this.files, function(file){
32141 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32150 this.files = files;
32157 Roo.each(this.files, function(file){
32162 file.target.remove();
32171 onClick : function(e, el, o)
32173 e.preventDefault();
32175 this.fireEvent('click', this, o);
32179 closable : function(closable)
32181 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32183 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32195 xhrOnLoad : function(xhr)
32197 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32201 if (xhr.readyState !== 4) {
32203 this.fireEvent('exception', this, xhr);
32207 var response = Roo.decode(xhr.responseText);
32209 if(!response.success){
32211 this.fireEvent('exception', this, xhr);
32215 var file = this.renderPreview(response.data);
32217 this.files.push(file);
32221 this.fireEvent('afterupload', this, xhr);
32225 xhrOnError : function(xhr)
32227 Roo.log('xhr on error');
32229 var response = Roo.decode(xhr.responseText);
32236 process : function(file)
32238 if(this.fireEvent('process', this, file) !== false){
32239 if(this.editable && file.type.indexOf('image') != -1){
32240 this.fireEvent('edit', this, file);
32244 this.uploadStart(file, false);
32251 uploadStart : function(file, crop)
32253 this.xhr = new XMLHttpRequest();
32255 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32260 file.xhr = this.xhr;
32262 this.managerEl.createChild({
32264 cls : 'roo-document-manager-loading',
32268 tooltip : file.name,
32269 cls : 'roo-document-manager-thumb',
32270 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32276 this.xhr.open(this.method, this.url, true);
32279 "Accept": "application/json",
32280 "Cache-Control": "no-cache",
32281 "X-Requested-With": "XMLHttpRequest"
32284 for (var headerName in headers) {
32285 var headerValue = headers[headerName];
32287 this.xhr.setRequestHeader(headerName, headerValue);
32293 this.xhr.onload = function()
32295 _this.xhrOnLoad(_this.xhr);
32298 this.xhr.onerror = function()
32300 _this.xhrOnError(_this.xhr);
32303 var formData = new FormData();
32305 formData.append('returnHTML', 'NO');
32308 formData.append('crop', crop);
32311 formData.append(this.paramName, file, file.name);
32318 if(this.fireEvent('prepare', this, formData, options) != false){
32320 if(options.manually){
32324 this.xhr.send(formData);
32328 this.uploadCancel();
32331 uploadCancel : function()
32337 this.delegates = [];
32339 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32346 renderPreview : function(file)
32348 if(typeof(file.target) != 'undefined' && file.target){
32352 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32354 var previewEl = this.managerEl.createChild({
32356 cls : 'roo-document-manager-preview',
32360 tooltip : file[this.toolTipName],
32361 cls : 'roo-document-manager-thumb',
32362 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32367 html : '<i class="fa fa-times-circle"></i>'
32372 var close = previewEl.select('button.close', true).first();
32374 close.on('click', this.onRemove, this, file);
32376 file.target = previewEl;
32378 var image = previewEl.select('img', true).first();
32382 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32384 image.on('click', this.onClick, this, file);
32386 this.fireEvent('previewrendered', this, file);
32392 onPreviewLoad : function(file, image)
32394 if(typeof(file.target) == 'undefined' || !file.target){
32398 var width = image.dom.naturalWidth || image.dom.width;
32399 var height = image.dom.naturalHeight || image.dom.height;
32401 if(!this.previewResize) {
32405 if(width > height){
32406 file.target.addClass('wide');
32410 file.target.addClass('tall');
32415 uploadFromSource : function(file, crop)
32417 this.xhr = new XMLHttpRequest();
32419 this.managerEl.createChild({
32421 cls : 'roo-document-manager-loading',
32425 tooltip : file.name,
32426 cls : 'roo-document-manager-thumb',
32427 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32433 this.xhr.open(this.method, this.url, true);
32436 "Accept": "application/json",
32437 "Cache-Control": "no-cache",
32438 "X-Requested-With": "XMLHttpRequest"
32441 for (var headerName in headers) {
32442 var headerValue = headers[headerName];
32444 this.xhr.setRequestHeader(headerName, headerValue);
32450 this.xhr.onload = function()
32452 _this.xhrOnLoad(_this.xhr);
32455 this.xhr.onerror = function()
32457 _this.xhrOnError(_this.xhr);
32460 var formData = new FormData();
32462 formData.append('returnHTML', 'NO');
32464 formData.append('crop', crop);
32466 if(typeof(file.filename) != 'undefined'){
32467 formData.append('filename', file.filename);
32470 if(typeof(file.mimetype) != 'undefined'){
32471 formData.append('mimetype', file.mimetype);
32476 if(this.fireEvent('prepare', this, formData) != false){
32477 this.xhr.send(formData);
32487 * @class Roo.bootstrap.DocumentViewer
32488 * @extends Roo.bootstrap.Component
32489 * Bootstrap DocumentViewer class
32490 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32491 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32494 * Create a new DocumentViewer
32495 * @param {Object} config The config object
32498 Roo.bootstrap.DocumentViewer = function(config){
32499 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32504 * Fire after initEvent
32505 * @param {Roo.bootstrap.DocumentViewer} this
32511 * @param {Roo.bootstrap.DocumentViewer} this
32516 * Fire after download button
32517 * @param {Roo.bootstrap.DocumentViewer} this
32522 * Fire after trash button
32523 * @param {Roo.bootstrap.DocumentViewer} this
32530 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32532 showDownload : true,
32536 getAutoCreate : function()
32540 cls : 'roo-document-viewer',
32544 cls : 'roo-document-viewer-body',
32548 cls : 'roo-document-viewer-thumb',
32552 cls : 'roo-document-viewer-image'
32560 cls : 'roo-document-viewer-footer',
32563 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32567 cls : 'btn-group roo-document-viewer-download',
32571 cls : 'btn btn-default',
32572 html : '<i class="fa fa-download"></i>'
32578 cls : 'btn-group roo-document-viewer-trash',
32582 cls : 'btn btn-default',
32583 html : '<i class="fa fa-trash"></i>'
32596 initEvents : function()
32598 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32599 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32601 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32602 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32604 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32605 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32607 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32608 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32610 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32611 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32613 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32614 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32616 this.bodyEl.on('click', this.onClick, this);
32617 this.downloadBtn.on('click', this.onDownload, this);
32618 this.trashBtn.on('click', this.onTrash, this);
32620 this.downloadBtn.hide();
32621 this.trashBtn.hide();
32623 if(this.showDownload){
32624 this.downloadBtn.show();
32627 if(this.showTrash){
32628 this.trashBtn.show();
32631 if(!this.showDownload && !this.showTrash) {
32632 this.footerEl.hide();
32637 initial : function()
32639 this.fireEvent('initial', this);
32643 onClick : function(e)
32645 e.preventDefault();
32647 this.fireEvent('click', this);
32650 onDownload : function(e)
32652 e.preventDefault();
32654 this.fireEvent('download', this);
32657 onTrash : function(e)
32659 e.preventDefault();
32661 this.fireEvent('trash', this);
32673 * @class Roo.bootstrap.NavProgressBar
32674 * @extends Roo.bootstrap.Component
32675 * Bootstrap NavProgressBar class
32678 * Create a new nav progress bar
32679 * @param {Object} config The config object
32682 Roo.bootstrap.NavProgressBar = function(config){
32683 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32685 this.bullets = this.bullets || [];
32687 // Roo.bootstrap.NavProgressBar.register(this);
32691 * Fires when the active item changes
32692 * @param {Roo.bootstrap.NavProgressBar} this
32693 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32694 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32701 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32706 getAutoCreate : function()
32708 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32712 cls : 'roo-navigation-bar-group',
32716 cls : 'roo-navigation-top-bar'
32720 cls : 'roo-navigation-bullets-bar',
32724 cls : 'roo-navigation-bar'
32731 cls : 'roo-navigation-bottom-bar'
32741 initEvents: function()
32746 onRender : function(ct, position)
32748 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32750 if(this.bullets.length){
32751 Roo.each(this.bullets, function(b){
32760 addItem : function(cfg)
32762 var item = new Roo.bootstrap.NavProgressItem(cfg);
32764 item.parentId = this.id;
32765 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32768 var top = new Roo.bootstrap.Element({
32770 cls : 'roo-navigation-bar-text'
32773 var bottom = new Roo.bootstrap.Element({
32775 cls : 'roo-navigation-bar-text'
32778 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32779 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32781 var topText = new Roo.bootstrap.Element({
32783 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32786 var bottomText = new Roo.bootstrap.Element({
32788 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32791 topText.onRender(top.el, null);
32792 bottomText.onRender(bottom.el, null);
32795 item.bottomEl = bottom;
32798 this.barItems.push(item);
32803 getActive : function()
32805 var active = false;
32807 Roo.each(this.barItems, function(v){
32809 if (!v.isActive()) {
32821 setActiveItem : function(item)
32825 Roo.each(this.barItems, function(v){
32826 if (v.rid == item.rid) {
32830 if (v.isActive()) {
32831 v.setActive(false);
32836 item.setActive(true);
32838 this.fireEvent('changed', this, item, prev);
32841 getBarItem: function(rid)
32845 Roo.each(this.barItems, function(e) {
32846 if (e.rid != rid) {
32857 indexOfItem : function(item)
32861 Roo.each(this.barItems, function(v, i){
32863 if (v.rid != item.rid) {
32874 setActiveNext : function()
32876 var i = this.indexOfItem(this.getActive());
32878 if (i > this.barItems.length) {
32882 this.setActiveItem(this.barItems[i+1]);
32885 setActivePrev : function()
32887 var i = this.indexOfItem(this.getActive());
32893 this.setActiveItem(this.barItems[i-1]);
32896 format : function()
32898 if(!this.barItems.length){
32902 var width = 100 / this.barItems.length;
32904 Roo.each(this.barItems, function(i){
32905 i.el.setStyle('width', width + '%');
32906 i.topEl.el.setStyle('width', width + '%');
32907 i.bottomEl.el.setStyle('width', width + '%');
32916 * Nav Progress Item
32921 * @class Roo.bootstrap.NavProgressItem
32922 * @extends Roo.bootstrap.Component
32923 * Bootstrap NavProgressItem class
32924 * @cfg {String} rid the reference id
32925 * @cfg {Boolean} active (true|false) Is item active default false
32926 * @cfg {Boolean} disabled (true|false) Is item active default false
32927 * @cfg {String} html
32928 * @cfg {String} position (top|bottom) text position default bottom
32929 * @cfg {String} icon show icon instead of number
32932 * Create a new NavProgressItem
32933 * @param {Object} config The config object
32935 Roo.bootstrap.NavProgressItem = function(config){
32936 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32941 * The raw click event for the entire grid.
32942 * @param {Roo.bootstrap.NavProgressItem} this
32943 * @param {Roo.EventObject} e
32950 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32956 position : 'bottom',
32959 getAutoCreate : function()
32961 var iconCls = 'roo-navigation-bar-item-icon';
32963 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32967 cls: 'roo-navigation-bar-item',
32977 cfg.cls += ' active';
32980 cfg.cls += ' disabled';
32986 disable : function()
32988 this.setDisabled(true);
32991 enable : function()
32993 this.setDisabled(false);
32996 initEvents: function()
32998 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33000 this.iconEl.on('click', this.onClick, this);
33003 onClick : function(e)
33005 e.preventDefault();
33011 if(this.fireEvent('click', this, e) === false){
33015 this.parent().setActiveItem(this);
33018 isActive: function ()
33020 return this.active;
33023 setActive : function(state)
33025 if(this.active == state){
33029 this.active = state;
33032 this.el.addClass('active');
33036 this.el.removeClass('active');
33041 setDisabled : function(state)
33043 if(this.disabled == state){
33047 this.disabled = state;
33050 this.el.addClass('disabled');
33054 this.el.removeClass('disabled');
33057 tooltipEl : function()
33059 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33072 * @class Roo.bootstrap.FieldLabel
33073 * @extends Roo.bootstrap.Component
33074 * Bootstrap FieldLabel class
33075 * @cfg {String} html contents of the element
33076 * @cfg {String} tag tag of the element default label
33077 * @cfg {String} cls class of the element
33078 * @cfg {String} target label target
33079 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33080 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33081 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33082 * @cfg {String} iconTooltip default "This field is required"
33083 * @cfg {String} indicatorpos (left|right) default left
33086 * Create a new FieldLabel
33087 * @param {Object} config The config object
33090 Roo.bootstrap.FieldLabel = function(config){
33091 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33096 * Fires after the field has been marked as invalid.
33097 * @param {Roo.form.FieldLabel} this
33098 * @param {String} msg The validation message
33103 * Fires after the field has been validated with no errors.
33104 * @param {Roo.form.FieldLabel} this
33110 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33117 invalidClass : 'has-warning',
33118 validClass : 'has-success',
33119 iconTooltip : 'This field is required',
33120 indicatorpos : 'left',
33122 getAutoCreate : function(){
33125 if (!this.allowBlank) {
33131 cls : 'roo-bootstrap-field-label ' + this.cls,
33136 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33137 tooltip : this.iconTooltip
33146 if(this.indicatorpos == 'right'){
33149 cls : 'roo-bootstrap-field-label ' + this.cls,
33158 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33159 tooltip : this.iconTooltip
33168 initEvents: function()
33170 Roo.bootstrap.Element.superclass.initEvents.call(this);
33172 this.indicator = this.indicatorEl();
33174 if(this.indicator){
33175 this.indicator.removeClass('visible');
33176 this.indicator.addClass('invisible');
33179 Roo.bootstrap.FieldLabel.register(this);
33182 indicatorEl : function()
33184 var indicator = this.el.select('i.roo-required-indicator',true).first();
33195 * Mark this field as valid
33197 markValid : function()
33199 if(this.indicator){
33200 this.indicator.removeClass('visible');
33201 this.indicator.addClass('invisible');
33203 if (Roo.bootstrap.version == 3) {
33204 this.el.removeClass(this.invalidClass);
33205 this.el.addClass(this.validClass);
33207 this.el.removeClass('is-invalid');
33208 this.el.addClass('is-valid');
33212 this.fireEvent('valid', this);
33216 * Mark this field as invalid
33217 * @param {String} msg The validation message
33219 markInvalid : function(msg)
33221 if(this.indicator){
33222 this.indicator.removeClass('invisible');
33223 this.indicator.addClass('visible');
33225 if (Roo.bootstrap.version == 3) {
33226 this.el.removeClass(this.validClass);
33227 this.el.addClass(this.invalidClass);
33229 this.el.removeClass('is-valid');
33230 this.el.addClass('is-invalid');
33234 this.fireEvent('invalid', this, msg);
33240 Roo.apply(Roo.bootstrap.FieldLabel, {
33245 * register a FieldLabel Group
33246 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33248 register : function(label)
33250 if(this.groups.hasOwnProperty(label.target)){
33254 this.groups[label.target] = label;
33258 * fetch a FieldLabel Group based on the target
33259 * @param {string} target
33260 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33262 get: function(target) {
33263 if (typeof(this.groups[target]) == 'undefined') {
33267 return this.groups[target] ;
33276 * page DateSplitField.
33282 * @class Roo.bootstrap.DateSplitField
33283 * @extends Roo.bootstrap.Component
33284 * Bootstrap DateSplitField class
33285 * @cfg {string} fieldLabel - the label associated
33286 * @cfg {Number} labelWidth set the width of label (0-12)
33287 * @cfg {String} labelAlign (top|left)
33288 * @cfg {Boolean} dayAllowBlank (true|false) default false
33289 * @cfg {Boolean} monthAllowBlank (true|false) default false
33290 * @cfg {Boolean} yearAllowBlank (true|false) default false
33291 * @cfg {string} dayPlaceholder
33292 * @cfg {string} monthPlaceholder
33293 * @cfg {string} yearPlaceholder
33294 * @cfg {string} dayFormat default 'd'
33295 * @cfg {string} monthFormat default 'm'
33296 * @cfg {string} yearFormat default 'Y'
33297 * @cfg {Number} labellg set the width of label (1-12)
33298 * @cfg {Number} labelmd set the width of label (1-12)
33299 * @cfg {Number} labelsm set the width of label (1-12)
33300 * @cfg {Number} labelxs set the width of label (1-12)
33304 * Create a new DateSplitField
33305 * @param {Object} config The config object
33308 Roo.bootstrap.DateSplitField = function(config){
33309 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33315 * getting the data of years
33316 * @param {Roo.bootstrap.DateSplitField} this
33317 * @param {Object} years
33322 * getting the data of days
33323 * @param {Roo.bootstrap.DateSplitField} this
33324 * @param {Object} days
33329 * Fires after the field has been marked as invalid.
33330 * @param {Roo.form.Field} this
33331 * @param {String} msg The validation message
33336 * Fires after the field has been validated with no errors.
33337 * @param {Roo.form.Field} this
33343 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33346 labelAlign : 'top',
33348 dayAllowBlank : false,
33349 monthAllowBlank : false,
33350 yearAllowBlank : false,
33351 dayPlaceholder : '',
33352 monthPlaceholder : '',
33353 yearPlaceholder : '',
33357 isFormField : true,
33363 getAutoCreate : function()
33367 cls : 'row roo-date-split-field-group',
33372 cls : 'form-hidden-field roo-date-split-field-group-value',
33378 var labelCls = 'col-md-12';
33379 var contentCls = 'col-md-4';
33381 if(this.fieldLabel){
33385 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33389 html : this.fieldLabel
33394 if(this.labelAlign == 'left'){
33396 if(this.labelWidth > 12){
33397 label.style = "width: " + this.labelWidth + 'px';
33400 if(this.labelWidth < 13 && this.labelmd == 0){
33401 this.labelmd = this.labelWidth;
33404 if(this.labellg > 0){
33405 labelCls = ' col-lg-' + this.labellg;
33406 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33409 if(this.labelmd > 0){
33410 labelCls = ' col-md-' + this.labelmd;
33411 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33414 if(this.labelsm > 0){
33415 labelCls = ' col-sm-' + this.labelsm;
33416 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33419 if(this.labelxs > 0){
33420 labelCls = ' col-xs-' + this.labelxs;
33421 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33425 label.cls += ' ' + labelCls;
33427 cfg.cn.push(label);
33430 Roo.each(['day', 'month', 'year'], function(t){
33433 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33440 inputEl: function ()
33442 return this.el.select('.roo-date-split-field-group-value', true).first();
33445 onRender : function(ct, position)
33449 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33451 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33453 this.dayField = new Roo.bootstrap.ComboBox({
33454 allowBlank : this.dayAllowBlank,
33455 alwaysQuery : true,
33456 displayField : 'value',
33459 forceSelection : true,
33461 placeholder : this.dayPlaceholder,
33462 selectOnFocus : true,
33463 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33464 triggerAction : 'all',
33466 valueField : 'value',
33467 store : new Roo.data.SimpleStore({
33468 data : (function() {
33470 _this.fireEvent('days', _this, days);
33473 fields : [ 'value' ]
33476 select : function (_self, record, index)
33478 _this.setValue(_this.getValue());
33483 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33485 this.monthField = new Roo.bootstrap.MonthField({
33486 after : '<i class=\"fa fa-calendar\"></i>',
33487 allowBlank : this.monthAllowBlank,
33488 placeholder : this.monthPlaceholder,
33491 render : function (_self)
33493 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33494 e.preventDefault();
33498 select : function (_self, oldvalue, newvalue)
33500 _this.setValue(_this.getValue());
33505 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33507 this.yearField = new Roo.bootstrap.ComboBox({
33508 allowBlank : this.yearAllowBlank,
33509 alwaysQuery : true,
33510 displayField : 'value',
33513 forceSelection : true,
33515 placeholder : this.yearPlaceholder,
33516 selectOnFocus : true,
33517 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33518 triggerAction : 'all',
33520 valueField : 'value',
33521 store : new Roo.data.SimpleStore({
33522 data : (function() {
33524 _this.fireEvent('years', _this, years);
33527 fields : [ 'value' ]
33530 select : function (_self, record, index)
33532 _this.setValue(_this.getValue());
33537 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33540 setValue : function(v, format)
33542 this.inputEl.dom.value = v;
33544 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33546 var d = Date.parseDate(v, f);
33553 this.setDay(d.format(this.dayFormat));
33554 this.setMonth(d.format(this.monthFormat));
33555 this.setYear(d.format(this.yearFormat));
33562 setDay : function(v)
33564 this.dayField.setValue(v);
33565 this.inputEl.dom.value = this.getValue();
33570 setMonth : function(v)
33572 this.monthField.setValue(v, true);
33573 this.inputEl.dom.value = this.getValue();
33578 setYear : function(v)
33580 this.yearField.setValue(v);
33581 this.inputEl.dom.value = this.getValue();
33586 getDay : function()
33588 return this.dayField.getValue();
33591 getMonth : function()
33593 return this.monthField.getValue();
33596 getYear : function()
33598 return this.yearField.getValue();
33601 getValue : function()
33603 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33605 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33615 this.inputEl.dom.value = '';
33620 validate : function()
33622 var d = this.dayField.validate();
33623 var m = this.monthField.validate();
33624 var y = this.yearField.validate();
33629 (!this.dayAllowBlank && !d) ||
33630 (!this.monthAllowBlank && !m) ||
33631 (!this.yearAllowBlank && !y)
33636 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33645 this.markInvalid();
33650 markValid : function()
33653 var label = this.el.select('label', true).first();
33654 var icon = this.el.select('i.fa-star', true).first();
33660 this.fireEvent('valid', this);
33664 * Mark this field as invalid
33665 * @param {String} msg The validation message
33667 markInvalid : function(msg)
33670 var label = this.el.select('label', true).first();
33671 var icon = this.el.select('i.fa-star', true).first();
33673 if(label && !icon){
33674 this.el.select('.roo-date-split-field-label', true).createChild({
33676 cls : 'text-danger fa fa-lg fa-star',
33677 tooltip : 'This field is required',
33678 style : 'margin-right:5px;'
33682 this.fireEvent('invalid', this, msg);
33685 clearInvalid : function()
33687 var label = this.el.select('label', true).first();
33688 var icon = this.el.select('i.fa-star', true).first();
33694 this.fireEvent('valid', this);
33697 getName: function()
33707 * http://masonry.desandro.com
33709 * The idea is to render all the bricks based on vertical width...
33711 * The original code extends 'outlayer' - we might need to use that....
33717 * @class Roo.bootstrap.LayoutMasonry
33718 * @extends Roo.bootstrap.Component
33719 * Bootstrap Layout Masonry class
33722 * Create a new Element
33723 * @param {Object} config The config object
33726 Roo.bootstrap.LayoutMasonry = function(config){
33728 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33732 Roo.bootstrap.LayoutMasonry.register(this);
33738 * Fire after layout the items
33739 * @param {Roo.bootstrap.LayoutMasonry} this
33740 * @param {Roo.EventObject} e
33747 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33750 * @cfg {Boolean} isLayoutInstant = no animation?
33752 isLayoutInstant : false, // needed?
33755 * @cfg {Number} boxWidth width of the columns
33760 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33765 * @cfg {Number} padWidth padding below box..
33770 * @cfg {Number} gutter gutter width..
33775 * @cfg {Number} maxCols maximum number of columns
33781 * @cfg {Boolean} isAutoInitial defalut true
33783 isAutoInitial : true,
33788 * @cfg {Boolean} isHorizontal defalut false
33790 isHorizontal : false,
33792 currentSize : null,
33798 bricks: null, //CompositeElement
33802 _isLayoutInited : false,
33804 // isAlternative : false, // only use for vertical layout...
33807 * @cfg {Number} alternativePadWidth padding below box..
33809 alternativePadWidth : 50,
33811 selectedBrick : [],
33813 getAutoCreate : function(){
33815 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33819 cls: 'blog-masonary-wrapper ' + this.cls,
33821 cls : 'mas-boxes masonary'
33828 getChildContainer: function( )
33830 if (this.boxesEl) {
33831 return this.boxesEl;
33834 this.boxesEl = this.el.select('.mas-boxes').first();
33836 return this.boxesEl;
33840 initEvents : function()
33844 if(this.isAutoInitial){
33845 Roo.log('hook children rendered');
33846 this.on('childrenrendered', function() {
33847 Roo.log('children rendered');
33853 initial : function()
33855 this.selectedBrick = [];
33857 this.currentSize = this.el.getBox(true);
33859 Roo.EventManager.onWindowResize(this.resize, this);
33861 if(!this.isAutoInitial){
33869 //this.layout.defer(500,this);
33873 resize : function()
33875 var cs = this.el.getBox(true);
33878 this.currentSize.width == cs.width &&
33879 this.currentSize.x == cs.x &&
33880 this.currentSize.height == cs.height &&
33881 this.currentSize.y == cs.y
33883 Roo.log("no change in with or X or Y");
33887 this.currentSize = cs;
33893 layout : function()
33895 this._resetLayout();
33897 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33899 this.layoutItems( isInstant );
33901 this._isLayoutInited = true;
33903 this.fireEvent('layout', this);
33907 _resetLayout : function()
33909 if(this.isHorizontal){
33910 this.horizontalMeasureColumns();
33914 this.verticalMeasureColumns();
33918 verticalMeasureColumns : function()
33920 this.getContainerWidth();
33922 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33923 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33927 var boxWidth = this.boxWidth + this.padWidth;
33929 if(this.containerWidth < this.boxWidth){
33930 boxWidth = this.containerWidth
33933 var containerWidth = this.containerWidth;
33935 var cols = Math.floor(containerWidth / boxWidth);
33937 this.cols = Math.max( cols, 1 );
33939 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33941 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33943 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33945 this.colWidth = boxWidth + avail - this.padWidth;
33947 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33948 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33951 horizontalMeasureColumns : function()
33953 this.getContainerWidth();
33955 var boxWidth = this.boxWidth;
33957 if(this.containerWidth < boxWidth){
33958 boxWidth = this.containerWidth;
33961 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33963 this.el.setHeight(boxWidth);
33967 getContainerWidth : function()
33969 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33972 layoutItems : function( isInstant )
33974 Roo.log(this.bricks);
33976 var items = Roo.apply([], this.bricks);
33978 if(this.isHorizontal){
33979 this._horizontalLayoutItems( items , isInstant );
33983 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33984 // this._verticalAlternativeLayoutItems( items , isInstant );
33988 this._verticalLayoutItems( items , isInstant );
33992 _verticalLayoutItems : function ( items , isInstant)
33994 if ( !items || !items.length ) {
33999 ['xs', 'xs', 'xs', 'tall'],
34000 ['xs', 'xs', 'tall'],
34001 ['xs', 'xs', 'sm'],
34002 ['xs', 'xs', 'xs'],
34008 ['sm', 'xs', 'xs'],
34012 ['tall', 'xs', 'xs', 'xs'],
34013 ['tall', 'xs', 'xs'],
34025 Roo.each(items, function(item, k){
34027 switch (item.size) {
34028 // these layouts take up a full box,
34039 boxes.push([item]);
34062 var filterPattern = function(box, length)
34070 var pattern = box.slice(0, length);
34074 Roo.each(pattern, function(i){
34075 format.push(i.size);
34078 Roo.each(standard, function(s){
34080 if(String(s) != String(format)){
34089 if(!match && length == 1){
34094 filterPattern(box, length - 1);
34098 queue.push(pattern);
34100 box = box.slice(length, box.length);
34102 filterPattern(box, 4);
34108 Roo.each(boxes, function(box, k){
34114 if(box.length == 1){
34119 filterPattern(box, 4);
34123 this._processVerticalLayoutQueue( queue, isInstant );
34127 // _verticalAlternativeLayoutItems : function( items , isInstant )
34129 // if ( !items || !items.length ) {
34133 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34137 _horizontalLayoutItems : function ( items , isInstant)
34139 if ( !items || !items.length || items.length < 3) {
34145 var eItems = items.slice(0, 3);
34147 items = items.slice(3, items.length);
34150 ['xs', 'xs', 'xs', 'wide'],
34151 ['xs', 'xs', 'wide'],
34152 ['xs', 'xs', 'sm'],
34153 ['xs', 'xs', 'xs'],
34159 ['sm', 'xs', 'xs'],
34163 ['wide', 'xs', 'xs', 'xs'],
34164 ['wide', 'xs', 'xs'],
34177 Roo.each(items, function(item, k){
34179 switch (item.size) {
34190 boxes.push([item]);
34214 var filterPattern = function(box, length)
34222 var pattern = box.slice(0, length);
34226 Roo.each(pattern, function(i){
34227 format.push(i.size);
34230 Roo.each(standard, function(s){
34232 if(String(s) != String(format)){
34241 if(!match && length == 1){
34246 filterPattern(box, length - 1);
34250 queue.push(pattern);
34252 box = box.slice(length, box.length);
34254 filterPattern(box, 4);
34260 Roo.each(boxes, function(box, k){
34266 if(box.length == 1){
34271 filterPattern(box, 4);
34278 var pos = this.el.getBox(true);
34282 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34284 var hit_end = false;
34286 Roo.each(queue, function(box){
34290 Roo.each(box, function(b){
34292 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34302 Roo.each(box, function(b){
34304 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34307 mx = Math.max(mx, b.x);
34311 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34315 Roo.each(box, function(b){
34317 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34331 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34334 /** Sets position of item in DOM
34335 * @param {Element} item
34336 * @param {Number} x - horizontal position
34337 * @param {Number} y - vertical position
34338 * @param {Boolean} isInstant - disables transitions
34340 _processVerticalLayoutQueue : function( queue, isInstant )
34342 var pos = this.el.getBox(true);
34347 for (var i = 0; i < this.cols; i++){
34351 Roo.each(queue, function(box, k){
34353 var col = k % this.cols;
34355 Roo.each(box, function(b,kk){
34357 b.el.position('absolute');
34359 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34360 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34362 if(b.size == 'md-left' || b.size == 'md-right'){
34363 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34364 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34367 b.el.setWidth(width);
34368 b.el.setHeight(height);
34370 b.el.select('iframe',true).setSize(width,height);
34374 for (var i = 0; i < this.cols; i++){
34376 if(maxY[i] < maxY[col]){
34381 col = Math.min(col, i);
34385 x = pos.x + col * (this.colWidth + this.padWidth);
34389 var positions = [];
34391 switch (box.length){
34393 positions = this.getVerticalOneBoxColPositions(x, y, box);
34396 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34399 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34402 positions = this.getVerticalFourBoxColPositions(x, y, box);
34408 Roo.each(box, function(b,kk){
34410 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34412 var sz = b.el.getSize();
34414 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34422 for (var i = 0; i < this.cols; i++){
34423 mY = Math.max(mY, maxY[i]);
34426 this.el.setHeight(mY - pos.y);
34430 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34432 // var pos = this.el.getBox(true);
34435 // var maxX = pos.right;
34437 // var maxHeight = 0;
34439 // Roo.each(items, function(item, k){
34443 // item.el.position('absolute');
34445 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34447 // item.el.setWidth(width);
34449 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34451 // item.el.setHeight(height);
34454 // item.el.setXY([x, y], isInstant ? false : true);
34456 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34459 // y = y + height + this.alternativePadWidth;
34461 // maxHeight = maxHeight + height + this.alternativePadWidth;
34465 // this.el.setHeight(maxHeight);
34469 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34471 var pos = this.el.getBox(true);
34476 var maxX = pos.right;
34478 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34480 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34482 Roo.each(queue, function(box, k){
34484 Roo.each(box, function(b, kk){
34486 b.el.position('absolute');
34488 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34489 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34491 if(b.size == 'md-left' || b.size == 'md-right'){
34492 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34493 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34496 b.el.setWidth(width);
34497 b.el.setHeight(height);
34505 var positions = [];
34507 switch (box.length){
34509 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34512 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34515 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34518 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34524 Roo.each(box, function(b,kk){
34526 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34528 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34536 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34538 Roo.each(eItems, function(b,k){
34540 b.size = (k == 0) ? 'sm' : 'xs';
34541 b.x = (k == 0) ? 2 : 1;
34542 b.y = (k == 0) ? 2 : 1;
34544 b.el.position('absolute');
34546 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34548 b.el.setWidth(width);
34550 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34552 b.el.setHeight(height);
34556 var positions = [];
34559 x : maxX - this.unitWidth * 2 - this.gutter,
34564 x : maxX - this.unitWidth,
34565 y : minY + (this.unitWidth + this.gutter) * 2
34569 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34573 Roo.each(eItems, function(b,k){
34575 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34581 getVerticalOneBoxColPositions : function(x, y, box)
34585 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34587 if(box[0].size == 'md-left'){
34591 if(box[0].size == 'md-right'){
34596 x : x + (this.unitWidth + this.gutter) * rand,
34603 getVerticalTwoBoxColPositions : function(x, y, box)
34607 if(box[0].size == 'xs'){
34611 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34615 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34629 x : x + (this.unitWidth + this.gutter) * 2,
34630 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34637 getVerticalThreeBoxColPositions : function(x, y, box)
34641 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34649 x : x + (this.unitWidth + this.gutter) * 1,
34654 x : x + (this.unitWidth + this.gutter) * 2,
34662 if(box[0].size == 'xs' && box[1].size == 'xs'){
34671 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34675 x : x + (this.unitWidth + this.gutter) * 1,
34689 x : x + (this.unitWidth + this.gutter) * 2,
34694 x : x + (this.unitWidth + this.gutter) * 2,
34695 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34702 getVerticalFourBoxColPositions : function(x, y, box)
34706 if(box[0].size == 'xs'){
34715 y : y + (this.unitHeight + this.gutter) * 1
34720 y : y + (this.unitHeight + this.gutter) * 2
34724 x : x + (this.unitWidth + this.gutter) * 1,
34738 x : x + (this.unitWidth + this.gutter) * 2,
34743 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34744 y : y + (this.unitHeight + this.gutter) * 1
34748 x : x + (this.unitWidth + this.gutter) * 2,
34749 y : y + (this.unitWidth + this.gutter) * 2
34756 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34760 if(box[0].size == 'md-left'){
34762 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34769 if(box[0].size == 'md-right'){
34771 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34772 y : minY + (this.unitWidth + this.gutter) * 1
34778 var rand = Math.floor(Math.random() * (4 - box[0].y));
34781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34782 y : minY + (this.unitWidth + this.gutter) * rand
34789 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34793 if(box[0].size == 'xs'){
34796 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34801 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34802 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34810 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34815 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34816 y : minY + (this.unitWidth + this.gutter) * 2
34823 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34827 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34830 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34835 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34836 y : minY + (this.unitWidth + this.gutter) * 1
34840 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34841 y : minY + (this.unitWidth + this.gutter) * 2
34848 if(box[0].size == 'xs' && box[1].size == 'xs'){
34851 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34856 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34861 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34862 y : minY + (this.unitWidth + this.gutter) * 1
34870 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34875 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34876 y : minY + (this.unitWidth + this.gutter) * 2
34880 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34881 y : minY + (this.unitWidth + this.gutter) * 2
34888 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34892 if(box[0].size == 'xs'){
34895 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34900 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34905 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),
34910 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34911 y : minY + (this.unitWidth + this.gutter) * 1
34919 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34924 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34925 y : minY + (this.unitWidth + this.gutter) * 2
34929 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34930 y : minY + (this.unitWidth + this.gutter) * 2
34934 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),
34935 y : minY + (this.unitWidth + this.gutter) * 2
34943 * remove a Masonry Brick
34944 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34946 removeBrick : function(brick_id)
34952 for (var i = 0; i<this.bricks.length; i++) {
34953 if (this.bricks[i].id == brick_id) {
34954 this.bricks.splice(i,1);
34955 this.el.dom.removeChild(Roo.get(brick_id).dom);
34962 * adds a Masonry Brick
34963 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34965 addBrick : function(cfg)
34967 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34968 //this.register(cn);
34969 cn.parentId = this.id;
34970 cn.render(this.el);
34975 * register a Masonry Brick
34976 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34979 register : function(brick)
34981 this.bricks.push(brick);
34982 brick.masonryId = this.id;
34986 * clear all the Masonry Brick
34988 clearAll : function()
34991 //this.getChildContainer().dom.innerHTML = "";
34992 this.el.dom.innerHTML = '';
34995 getSelected : function()
34997 if (!this.selectedBrick) {
35001 return this.selectedBrick;
35005 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35009 * register a Masonry Layout
35010 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35013 register : function(layout)
35015 this.groups[layout.id] = layout;
35018 * fetch a Masonry Layout based on the masonry layout ID
35019 * @param {string} the masonry layout to add
35020 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35023 get: function(layout_id) {
35024 if (typeof(this.groups[layout_id]) == 'undefined') {
35027 return this.groups[layout_id] ;
35039 * http://masonry.desandro.com
35041 * The idea is to render all the bricks based on vertical width...
35043 * The original code extends 'outlayer' - we might need to use that....
35049 * @class Roo.bootstrap.LayoutMasonryAuto
35050 * @extends Roo.bootstrap.Component
35051 * Bootstrap Layout Masonry class
35054 * Create a new Element
35055 * @param {Object} config The config object
35058 Roo.bootstrap.LayoutMasonryAuto = function(config){
35059 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35062 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35065 * @cfg {Boolean} isFitWidth - resize the width..
35067 isFitWidth : false, // options..
35069 * @cfg {Boolean} isOriginLeft = left align?
35071 isOriginLeft : true,
35073 * @cfg {Boolean} isOriginTop = top align?
35075 isOriginTop : false,
35077 * @cfg {Boolean} isLayoutInstant = no animation?
35079 isLayoutInstant : false, // needed?
35081 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35083 isResizingContainer : true,
35085 * @cfg {Number} columnWidth width of the columns
35091 * @cfg {Number} maxCols maximum number of columns
35096 * @cfg {Number} padHeight padding below box..
35102 * @cfg {Boolean} isAutoInitial defalut true
35105 isAutoInitial : true,
35111 initialColumnWidth : 0,
35112 currentSize : null,
35114 colYs : null, // array.
35121 bricks: null, //CompositeElement
35122 cols : 0, // array?
35123 // element : null, // wrapped now this.el
35124 _isLayoutInited : null,
35127 getAutoCreate : function(){
35131 cls: 'blog-masonary-wrapper ' + this.cls,
35133 cls : 'mas-boxes masonary'
35140 getChildContainer: function( )
35142 if (this.boxesEl) {
35143 return this.boxesEl;
35146 this.boxesEl = this.el.select('.mas-boxes').first();
35148 return this.boxesEl;
35152 initEvents : function()
35156 if(this.isAutoInitial){
35157 Roo.log('hook children rendered');
35158 this.on('childrenrendered', function() {
35159 Roo.log('children rendered');
35166 initial : function()
35168 this.reloadItems();
35170 this.currentSize = this.el.getBox(true);
35172 /// was window resize... - let's see if this works..
35173 Roo.EventManager.onWindowResize(this.resize, this);
35175 if(!this.isAutoInitial){
35180 this.layout.defer(500,this);
35183 reloadItems: function()
35185 this.bricks = this.el.select('.masonry-brick', true);
35187 this.bricks.each(function(b) {
35188 //Roo.log(b.getSize());
35189 if (!b.attr('originalwidth')) {
35190 b.attr('originalwidth', b.getSize().width);
35195 Roo.log(this.bricks.elements.length);
35198 resize : function()
35201 var cs = this.el.getBox(true);
35203 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35204 Roo.log("no change in with or X");
35207 this.currentSize = cs;
35211 layout : function()
35214 this._resetLayout();
35215 //this._manageStamps();
35217 // don't animate first layout
35218 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35219 this.layoutItems( isInstant );
35221 // flag for initalized
35222 this._isLayoutInited = true;
35225 layoutItems : function( isInstant )
35227 //var items = this._getItemsForLayout( this.items );
35228 // original code supports filtering layout items.. we just ignore it..
35230 this._layoutItems( this.bricks , isInstant );
35232 this._postLayout();
35234 _layoutItems : function ( items , isInstant)
35236 //this.fireEvent( 'layout', this, items );
35239 if ( !items || !items.elements.length ) {
35240 // no items, emit event with empty array
35245 items.each(function(item) {
35246 Roo.log("layout item");
35248 // get x/y object from method
35249 var position = this._getItemLayoutPosition( item );
35251 position.item = item;
35252 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35253 queue.push( position );
35256 this._processLayoutQueue( queue );
35258 /** Sets position of item in DOM
35259 * @param {Element} item
35260 * @param {Number} x - horizontal position
35261 * @param {Number} y - vertical position
35262 * @param {Boolean} isInstant - disables transitions
35264 _processLayoutQueue : function( queue )
35266 for ( var i=0, len = queue.length; i < len; i++ ) {
35267 var obj = queue[i];
35268 obj.item.position('absolute');
35269 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35275 * Any logic you want to do after each layout,
35276 * i.e. size the container
35278 _postLayout : function()
35280 this.resizeContainer();
35283 resizeContainer : function()
35285 if ( !this.isResizingContainer ) {
35288 var size = this._getContainerSize();
35290 this.el.setSize(size.width,size.height);
35291 this.boxesEl.setSize(size.width,size.height);
35297 _resetLayout : function()
35299 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35300 this.colWidth = this.el.getWidth();
35301 //this.gutter = this.el.getWidth();
35303 this.measureColumns();
35309 this.colYs.push( 0 );
35315 measureColumns : function()
35317 this.getContainerWidth();
35318 // if columnWidth is 0, default to outerWidth of first item
35319 if ( !this.columnWidth ) {
35320 var firstItem = this.bricks.first();
35321 Roo.log(firstItem);
35322 this.columnWidth = this.containerWidth;
35323 if (firstItem && firstItem.attr('originalwidth') ) {
35324 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35326 // columnWidth fall back to item of first element
35327 Roo.log("set column width?");
35328 this.initialColumnWidth = this.columnWidth ;
35330 // if first elem has no width, default to size of container
35335 if (this.initialColumnWidth) {
35336 this.columnWidth = this.initialColumnWidth;
35341 // column width is fixed at the top - however if container width get's smaller we should
35344 // this bit calcs how man columns..
35346 var columnWidth = this.columnWidth += this.gutter;
35348 // calculate columns
35349 var containerWidth = this.containerWidth + this.gutter;
35351 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35352 // fix rounding errors, typically with gutters
35353 var excess = columnWidth - containerWidth % columnWidth;
35356 // if overshoot is less than a pixel, round up, otherwise floor it
35357 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35358 cols = Math[ mathMethod ]( cols );
35359 this.cols = Math.max( cols, 1 );
35360 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35362 // padding positioning..
35363 var totalColWidth = this.cols * this.columnWidth;
35364 var padavail = this.containerWidth - totalColWidth;
35365 // so for 2 columns - we need 3 'pads'
35367 var padNeeded = (1+this.cols) * this.padWidth;
35369 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35371 this.columnWidth += padExtra
35372 //this.padWidth = Math.floor(padavail / ( this.cols));
35374 // adjust colum width so that padding is fixed??
35376 // we have 3 columns ... total = width * 3
35377 // we have X left over... that should be used by
35379 //if (this.expandC) {
35387 getContainerWidth : function()
35389 /* // container is parent if fit width
35390 var container = this.isFitWidth ? this.element.parentNode : this.element;
35391 // check that this.size and size are there
35392 // IE8 triggers resize on body size change, so they might not be
35394 var size = getSize( container ); //FIXME
35395 this.containerWidth = size && size.innerWidth; //FIXME
35398 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35402 _getItemLayoutPosition : function( item ) // what is item?
35404 // we resize the item to our columnWidth..
35406 item.setWidth(this.columnWidth);
35407 item.autoBoxAdjust = false;
35409 var sz = item.getSize();
35411 // how many columns does this brick span
35412 var remainder = this.containerWidth % this.columnWidth;
35414 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35415 // round if off by 1 pixel, otherwise use ceil
35416 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35417 colSpan = Math.min( colSpan, this.cols );
35419 // normally this should be '1' as we dont' currently allow multi width columns..
35421 var colGroup = this._getColGroup( colSpan );
35422 // get the minimum Y value from the columns
35423 var minimumY = Math.min.apply( Math, colGroup );
35424 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35426 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35428 // position the brick
35430 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35431 y: this.currentSize.y + minimumY + this.padHeight
35435 // apply setHeight to necessary columns
35436 var setHeight = minimumY + sz.height + this.padHeight;
35437 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35439 var setSpan = this.cols + 1 - colGroup.length;
35440 for ( var i = 0; i < setSpan; i++ ) {
35441 this.colYs[ shortColIndex + i ] = setHeight ;
35448 * @param {Number} colSpan - number of columns the element spans
35449 * @returns {Array} colGroup
35451 _getColGroup : function( colSpan )
35453 if ( colSpan < 2 ) {
35454 // if brick spans only one column, use all the column Ys
35459 // how many different places could this brick fit horizontally
35460 var groupCount = this.cols + 1 - colSpan;
35461 // for each group potential horizontal position
35462 for ( var i = 0; i < groupCount; i++ ) {
35463 // make an array of colY values for that one group
35464 var groupColYs = this.colYs.slice( i, i + colSpan );
35465 // and get the max value of the array
35466 colGroup[i] = Math.max.apply( Math, groupColYs );
35471 _manageStamp : function( stamp )
35473 var stampSize = stamp.getSize();
35474 var offset = stamp.getBox();
35475 // get the columns that this stamp affects
35476 var firstX = this.isOriginLeft ? offset.x : offset.right;
35477 var lastX = firstX + stampSize.width;
35478 var firstCol = Math.floor( firstX / this.columnWidth );
35479 firstCol = Math.max( 0, firstCol );
35481 var lastCol = Math.floor( lastX / this.columnWidth );
35482 // lastCol should not go over if multiple of columnWidth #425
35483 lastCol -= lastX % this.columnWidth ? 0 : 1;
35484 lastCol = Math.min( this.cols - 1, lastCol );
35486 // set colYs to bottom of the stamp
35487 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35490 for ( var i = firstCol; i <= lastCol; i++ ) {
35491 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35496 _getContainerSize : function()
35498 this.maxY = Math.max.apply( Math, this.colYs );
35503 if ( this.isFitWidth ) {
35504 size.width = this._getContainerFitWidth();
35510 _getContainerFitWidth : function()
35512 var unusedCols = 0;
35513 // count unused columns
35516 if ( this.colYs[i] !== 0 ) {
35521 // fit container to columns that have been used
35522 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35525 needsResizeLayout : function()
35527 var previousWidth = this.containerWidth;
35528 this.getContainerWidth();
35529 return previousWidth !== this.containerWidth;
35544 * @class Roo.bootstrap.MasonryBrick
35545 * @extends Roo.bootstrap.Component
35546 * Bootstrap MasonryBrick class
35549 * Create a new MasonryBrick
35550 * @param {Object} config The config object
35553 Roo.bootstrap.MasonryBrick = function(config){
35555 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35557 Roo.bootstrap.MasonryBrick.register(this);
35563 * When a MasonryBrick is clcik
35564 * @param {Roo.bootstrap.MasonryBrick} this
35565 * @param {Roo.EventObject} e
35571 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35574 * @cfg {String} title
35578 * @cfg {String} html
35582 * @cfg {String} bgimage
35586 * @cfg {String} videourl
35590 * @cfg {String} cls
35594 * @cfg {String} href
35598 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35603 * @cfg {String} placetitle (center|bottom)
35608 * @cfg {Boolean} isFitContainer defalut true
35610 isFitContainer : true,
35613 * @cfg {Boolean} preventDefault defalut false
35615 preventDefault : false,
35618 * @cfg {Boolean} inverse defalut false
35620 maskInverse : false,
35622 getAutoCreate : function()
35624 if(!this.isFitContainer){
35625 return this.getSplitAutoCreate();
35628 var cls = 'masonry-brick masonry-brick-full';
35630 if(this.href.length){
35631 cls += ' masonry-brick-link';
35634 if(this.bgimage.length){
35635 cls += ' masonry-brick-image';
35638 if(this.maskInverse){
35639 cls += ' mask-inverse';
35642 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35643 cls += ' enable-mask';
35647 cls += ' masonry-' + this.size + '-brick';
35650 if(this.placetitle.length){
35652 switch (this.placetitle) {
35654 cls += ' masonry-center-title';
35657 cls += ' masonry-bottom-title';
35664 if(!this.html.length && !this.bgimage.length){
35665 cls += ' masonry-center-title';
35668 if(!this.html.length && this.bgimage.length){
35669 cls += ' masonry-bottom-title';
35674 cls += ' ' + this.cls;
35678 tag: (this.href.length) ? 'a' : 'div',
35683 cls: 'masonry-brick-mask'
35687 cls: 'masonry-brick-paragraph',
35693 if(this.href.length){
35694 cfg.href = this.href;
35697 var cn = cfg.cn[1].cn;
35699 if(this.title.length){
35702 cls: 'masonry-brick-title',
35707 if(this.html.length){
35710 cls: 'masonry-brick-text',
35715 if (!this.title.length && !this.html.length) {
35716 cfg.cn[1].cls += ' hide';
35719 if(this.bgimage.length){
35722 cls: 'masonry-brick-image-view',
35727 if(this.videourl.length){
35728 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35729 // youtube support only?
35732 cls: 'masonry-brick-image-view',
35735 allowfullscreen : true
35743 getSplitAutoCreate : function()
35745 var cls = 'masonry-brick masonry-brick-split';
35747 if(this.href.length){
35748 cls += ' masonry-brick-link';
35751 if(this.bgimage.length){
35752 cls += ' masonry-brick-image';
35756 cls += ' masonry-' + this.size + '-brick';
35759 switch (this.placetitle) {
35761 cls += ' masonry-center-title';
35764 cls += ' masonry-bottom-title';
35767 if(!this.bgimage.length){
35768 cls += ' masonry-center-title';
35771 if(this.bgimage.length){
35772 cls += ' masonry-bottom-title';
35778 cls += ' ' + this.cls;
35782 tag: (this.href.length) ? 'a' : 'div',
35787 cls: 'masonry-brick-split-head',
35791 cls: 'masonry-brick-paragraph',
35798 cls: 'masonry-brick-split-body',
35804 if(this.href.length){
35805 cfg.href = this.href;
35808 if(this.title.length){
35809 cfg.cn[0].cn[0].cn.push({
35811 cls: 'masonry-brick-title',
35816 if(this.html.length){
35817 cfg.cn[1].cn.push({
35819 cls: 'masonry-brick-text',
35824 if(this.bgimage.length){
35825 cfg.cn[0].cn.push({
35827 cls: 'masonry-brick-image-view',
35832 if(this.videourl.length){
35833 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35834 // youtube support only?
35835 cfg.cn[0].cn.cn.push({
35837 cls: 'masonry-brick-image-view',
35840 allowfullscreen : true
35847 initEvents: function()
35849 switch (this.size) {
35882 this.el.on('touchstart', this.onTouchStart, this);
35883 this.el.on('touchmove', this.onTouchMove, this);
35884 this.el.on('touchend', this.onTouchEnd, this);
35885 this.el.on('contextmenu', this.onContextMenu, this);
35887 this.el.on('mouseenter' ,this.enter, this);
35888 this.el.on('mouseleave', this.leave, this);
35889 this.el.on('click', this.onClick, this);
35892 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35893 this.parent().bricks.push(this);
35898 onClick: function(e, el)
35900 var time = this.endTimer - this.startTimer;
35901 // Roo.log(e.preventDefault());
35904 e.preventDefault();
35909 if(!this.preventDefault){
35913 e.preventDefault();
35915 if (this.activeClass != '') {
35916 this.selectBrick();
35919 this.fireEvent('click', this, e);
35922 enter: function(e, el)
35924 e.preventDefault();
35926 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35930 if(this.bgimage.length && this.html.length){
35931 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35935 leave: function(e, el)
35937 e.preventDefault();
35939 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35943 if(this.bgimage.length && this.html.length){
35944 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35948 onTouchStart: function(e, el)
35950 // e.preventDefault();
35952 this.touchmoved = false;
35954 if(!this.isFitContainer){
35958 if(!this.bgimage.length || !this.html.length){
35962 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35964 this.timer = new Date().getTime();
35968 onTouchMove: function(e, el)
35970 this.touchmoved = true;
35973 onContextMenu : function(e,el)
35975 e.preventDefault();
35976 e.stopPropagation();
35980 onTouchEnd: function(e, el)
35982 // e.preventDefault();
35984 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35991 if(!this.bgimage.length || !this.html.length){
35993 if(this.href.length){
35994 window.location.href = this.href;
36000 if(!this.isFitContainer){
36004 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36006 window.location.href = this.href;
36009 //selection on single brick only
36010 selectBrick : function() {
36012 if (!this.parentId) {
36016 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36017 var index = m.selectedBrick.indexOf(this.id);
36020 m.selectedBrick.splice(index,1);
36021 this.el.removeClass(this.activeClass);
36025 for(var i = 0; i < m.selectedBrick.length; i++) {
36026 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36027 b.el.removeClass(b.activeClass);
36030 m.selectedBrick = [];
36032 m.selectedBrick.push(this.id);
36033 this.el.addClass(this.activeClass);
36037 isSelected : function(){
36038 return this.el.hasClass(this.activeClass);
36043 Roo.apply(Roo.bootstrap.MasonryBrick, {
36046 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36048 * register a Masonry Brick
36049 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36052 register : function(brick)
36054 //this.groups[brick.id] = brick;
36055 this.groups.add(brick.id, brick);
36058 * fetch a masonry brick based on the masonry brick ID
36059 * @param {string} the masonry brick to add
36060 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36063 get: function(brick_id)
36065 // if (typeof(this.groups[brick_id]) == 'undefined') {
36068 // return this.groups[brick_id] ;
36070 if(this.groups.key(brick_id)) {
36071 return this.groups.key(brick_id);
36089 * @class Roo.bootstrap.Brick
36090 * @extends Roo.bootstrap.Component
36091 * Bootstrap Brick class
36094 * Create a new Brick
36095 * @param {Object} config The config object
36098 Roo.bootstrap.Brick = function(config){
36099 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36105 * When a Brick is click
36106 * @param {Roo.bootstrap.Brick} this
36107 * @param {Roo.EventObject} e
36113 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36116 * @cfg {String} title
36120 * @cfg {String} html
36124 * @cfg {String} bgimage
36128 * @cfg {String} cls
36132 * @cfg {String} href
36136 * @cfg {String} video
36140 * @cfg {Boolean} square
36144 getAutoCreate : function()
36146 var cls = 'roo-brick';
36148 if(this.href.length){
36149 cls += ' roo-brick-link';
36152 if(this.bgimage.length){
36153 cls += ' roo-brick-image';
36156 if(!this.html.length && !this.bgimage.length){
36157 cls += ' roo-brick-center-title';
36160 if(!this.html.length && this.bgimage.length){
36161 cls += ' roo-brick-bottom-title';
36165 cls += ' ' + this.cls;
36169 tag: (this.href.length) ? 'a' : 'div',
36174 cls: 'roo-brick-paragraph',
36180 if(this.href.length){
36181 cfg.href = this.href;
36184 var cn = cfg.cn[0].cn;
36186 if(this.title.length){
36189 cls: 'roo-brick-title',
36194 if(this.html.length){
36197 cls: 'roo-brick-text',
36204 if(this.bgimage.length){
36207 cls: 'roo-brick-image-view',
36215 initEvents: function()
36217 if(this.title.length || this.html.length){
36218 this.el.on('mouseenter' ,this.enter, this);
36219 this.el.on('mouseleave', this.leave, this);
36222 Roo.EventManager.onWindowResize(this.resize, this);
36224 if(this.bgimage.length){
36225 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36226 this.imageEl.on('load', this.onImageLoad, this);
36233 onImageLoad : function()
36238 resize : function()
36240 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36242 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36244 if(this.bgimage.length){
36245 var image = this.el.select('.roo-brick-image-view', true).first();
36247 image.setWidth(paragraph.getWidth());
36250 image.setHeight(paragraph.getWidth());
36253 this.el.setHeight(image.getHeight());
36254 paragraph.setHeight(image.getHeight());
36260 enter: function(e, el)
36262 e.preventDefault();
36264 if(this.bgimage.length){
36265 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36266 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36270 leave: function(e, el)
36272 e.preventDefault();
36274 if(this.bgimage.length){
36275 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36276 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36291 * @class Roo.bootstrap.NumberField
36292 * @extends Roo.bootstrap.Input
36293 * Bootstrap NumberField class
36299 * Create a new NumberField
36300 * @param {Object} config The config object
36303 Roo.bootstrap.NumberField = function(config){
36304 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36307 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36310 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36312 allowDecimals : true,
36314 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36316 decimalSeparator : ".",
36318 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36320 decimalPrecision : 2,
36322 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36324 allowNegative : true,
36327 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36331 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36333 minValue : Number.NEGATIVE_INFINITY,
36335 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36337 maxValue : Number.MAX_VALUE,
36339 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36341 minText : "The minimum value for this field is {0}",
36343 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36345 maxText : "The maximum value for this field is {0}",
36347 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36348 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36350 nanText : "{0} is not a valid number",
36352 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36354 thousandsDelimiter : false,
36356 * @cfg {String} valueAlign alignment of value
36358 valueAlign : "left",
36360 getAutoCreate : function()
36362 var hiddenInput = {
36366 cls: 'hidden-number-input'
36370 hiddenInput.name = this.name;
36375 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36377 this.name = hiddenInput.name;
36379 if(cfg.cn.length > 0) {
36380 cfg.cn.push(hiddenInput);
36387 initEvents : function()
36389 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36391 var allowed = "0123456789";
36393 if(this.allowDecimals){
36394 allowed += this.decimalSeparator;
36397 if(this.allowNegative){
36401 if(this.thousandsDelimiter) {
36405 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36407 var keyPress = function(e){
36409 var k = e.getKey();
36411 var c = e.getCharCode();
36414 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36415 allowed.indexOf(String.fromCharCode(c)) === -1
36421 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36425 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36430 this.el.on("keypress", keyPress, this);
36433 validateValue : function(value)
36436 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36440 var num = this.parseValue(value);
36443 this.markInvalid(String.format(this.nanText, value));
36447 if(num < this.minValue){
36448 this.markInvalid(String.format(this.minText, this.minValue));
36452 if(num > this.maxValue){
36453 this.markInvalid(String.format(this.maxText, this.maxValue));
36460 getValue : function()
36462 var v = this.hiddenEl().getValue();
36464 return this.fixPrecision(this.parseValue(v));
36467 parseValue : function(value)
36469 if(this.thousandsDelimiter) {
36471 r = new RegExp(",", "g");
36472 value = value.replace(r, "");
36475 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36476 return isNaN(value) ? '' : value;
36479 fixPrecision : function(value)
36481 if(this.thousandsDelimiter) {
36483 r = new RegExp(",", "g");
36484 value = value.replace(r, "");
36487 var nan = isNaN(value);
36489 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36490 return nan ? '' : value;
36492 return parseFloat(value).toFixed(this.decimalPrecision);
36495 setValue : function(v)
36497 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36503 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36505 this.inputEl().dom.value = (v == '') ? '' :
36506 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36508 if(!this.allowZero && v === '0') {
36509 this.hiddenEl().dom.value = '';
36510 this.inputEl().dom.value = '';
36517 decimalPrecisionFcn : function(v)
36519 return Math.floor(v);
36522 beforeBlur : function()
36524 var v = this.parseValue(this.getRawValue());
36526 if(v || v === 0 || v === ''){
36531 hiddenEl : function()
36533 return this.el.select('input.hidden-number-input',true).first();
36545 * @class Roo.bootstrap.DocumentSlider
36546 * @extends Roo.bootstrap.Component
36547 * Bootstrap DocumentSlider class
36550 * Create a new DocumentViewer
36551 * @param {Object} config The config object
36554 Roo.bootstrap.DocumentSlider = function(config){
36555 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36562 * Fire after initEvent
36563 * @param {Roo.bootstrap.DocumentSlider} this
36568 * Fire after update
36569 * @param {Roo.bootstrap.DocumentSlider} this
36575 * @param {Roo.bootstrap.DocumentSlider} this
36581 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36587 getAutoCreate : function()
36591 cls : 'roo-document-slider',
36595 cls : 'roo-document-slider-header',
36599 cls : 'roo-document-slider-header-title'
36605 cls : 'roo-document-slider-body',
36609 cls : 'roo-document-slider-prev',
36613 cls : 'fa fa-chevron-left'
36619 cls : 'roo-document-slider-thumb',
36623 cls : 'roo-document-slider-image'
36629 cls : 'roo-document-slider-next',
36633 cls : 'fa fa-chevron-right'
36645 initEvents : function()
36647 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36648 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36650 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36651 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36653 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36654 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36656 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36657 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36659 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36660 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36662 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36663 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36665 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36666 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36668 this.thumbEl.on('click', this.onClick, this);
36670 this.prevIndicator.on('click', this.prev, this);
36672 this.nextIndicator.on('click', this.next, this);
36676 initial : function()
36678 if(this.files.length){
36679 this.indicator = 1;
36683 this.fireEvent('initial', this);
36686 update : function()
36688 this.imageEl.attr('src', this.files[this.indicator - 1]);
36690 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36692 this.prevIndicator.show();
36694 if(this.indicator == 1){
36695 this.prevIndicator.hide();
36698 this.nextIndicator.show();
36700 if(this.indicator == this.files.length){
36701 this.nextIndicator.hide();
36704 this.thumbEl.scrollTo('top');
36706 this.fireEvent('update', this);
36709 onClick : function(e)
36711 e.preventDefault();
36713 this.fireEvent('click', this);
36718 e.preventDefault();
36720 this.indicator = Math.max(1, this.indicator - 1);
36727 e.preventDefault();
36729 this.indicator = Math.min(this.files.length, this.indicator + 1);
36743 * @class Roo.bootstrap.RadioSet
36744 * @extends Roo.bootstrap.Input
36745 * Bootstrap RadioSet class
36746 * @cfg {String} indicatorpos (left|right) default left
36747 * @cfg {Boolean} inline (true|false) inline the element (default true)
36748 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36750 * Create a new RadioSet
36751 * @param {Object} config The config object
36754 Roo.bootstrap.RadioSet = function(config){
36756 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36760 Roo.bootstrap.RadioSet.register(this);
36765 * Fires when the element is checked or unchecked.
36766 * @param {Roo.bootstrap.RadioSet} this This radio
36767 * @param {Roo.bootstrap.Radio} item The checked item
36772 * Fires when the element is click.
36773 * @param {Roo.bootstrap.RadioSet} this This radio set
36774 * @param {Roo.bootstrap.Radio} item The checked item
36775 * @param {Roo.EventObject} e The event object
36782 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36790 indicatorpos : 'left',
36792 getAutoCreate : function()
36796 cls : 'roo-radio-set-label',
36800 html : this.fieldLabel
36804 if (Roo.bootstrap.version == 3) {
36807 if(this.indicatorpos == 'left'){
36810 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36811 tooltip : 'This field is required'
36816 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36817 tooltip : 'This field is required'
36823 cls : 'roo-radio-set-items'
36826 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36828 if (align === 'left' && this.fieldLabel.length) {
36831 cls : "roo-radio-set-right",
36837 if(this.labelWidth > 12){
36838 label.style = "width: " + this.labelWidth + 'px';
36841 if(this.labelWidth < 13 && this.labelmd == 0){
36842 this.labelmd = this.labelWidth;
36845 if(this.labellg > 0){
36846 label.cls += ' col-lg-' + this.labellg;
36847 items.cls += ' col-lg-' + (12 - this.labellg);
36850 if(this.labelmd > 0){
36851 label.cls += ' col-md-' + this.labelmd;
36852 items.cls += ' col-md-' + (12 - this.labelmd);
36855 if(this.labelsm > 0){
36856 label.cls += ' col-sm-' + this.labelsm;
36857 items.cls += ' col-sm-' + (12 - this.labelsm);
36860 if(this.labelxs > 0){
36861 label.cls += ' col-xs-' + this.labelxs;
36862 items.cls += ' col-xs-' + (12 - this.labelxs);
36868 cls : 'roo-radio-set',
36872 cls : 'roo-radio-set-input',
36875 value : this.value ? this.value : ''
36882 if(this.weight.length){
36883 cfg.cls += ' roo-radio-' + this.weight;
36887 cfg.cls += ' roo-radio-set-inline';
36891 ['xs','sm','md','lg'].map(function(size){
36892 if (settings[size]) {
36893 cfg.cls += ' col-' + size + '-' + settings[size];
36901 initEvents : function()
36903 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36904 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36906 if(!this.fieldLabel.length){
36907 this.labelEl.hide();
36910 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36911 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36913 this.indicator = this.indicatorEl();
36915 if(this.indicator){
36916 this.indicator.addClass('invisible');
36919 this.originalValue = this.getValue();
36923 inputEl: function ()
36925 return this.el.select('.roo-radio-set-input', true).first();
36928 getChildContainer : function()
36930 return this.itemsEl;
36933 register : function(item)
36935 this.radioes.push(item);
36939 validate : function()
36941 if(this.getVisibilityEl().hasClass('hidden')){
36947 Roo.each(this.radioes, function(i){
36956 if(this.allowBlank) {
36960 if(this.disabled || valid){
36965 this.markInvalid();
36970 markValid : function()
36972 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36973 this.indicatorEl().removeClass('visible');
36974 this.indicatorEl().addClass('invisible');
36978 if (Roo.bootstrap.version == 3) {
36979 this.el.removeClass([this.invalidClass, this.validClass]);
36980 this.el.addClass(this.validClass);
36982 this.el.removeClass(['is-invalid','is-valid']);
36983 this.el.addClass(['is-valid']);
36985 this.fireEvent('valid', this);
36988 markInvalid : function(msg)
36990 if(this.allowBlank || this.disabled){
36994 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36995 this.indicatorEl().removeClass('invisible');
36996 this.indicatorEl().addClass('visible');
36998 if (Roo.bootstrap.version == 3) {
36999 this.el.removeClass([this.invalidClass, this.validClass]);
37000 this.el.addClass(this.invalidClass);
37002 this.el.removeClass(['is-invalid','is-valid']);
37003 this.el.addClass(['is-invalid']);
37006 this.fireEvent('invalid', this, msg);
37010 setValue : function(v, suppressEvent)
37012 if(this.value === v){
37019 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37022 Roo.each(this.radioes, function(i){
37024 i.el.removeClass('checked');
37027 Roo.each(this.radioes, function(i){
37029 if(i.value === v || i.value.toString() === v.toString()){
37031 i.el.addClass('checked');
37033 if(suppressEvent !== true){
37034 this.fireEvent('check', this, i);
37045 clearInvalid : function(){
37047 if(!this.el || this.preventMark){
37051 this.el.removeClass([this.invalidClass]);
37053 this.fireEvent('valid', this);
37058 Roo.apply(Roo.bootstrap.RadioSet, {
37062 register : function(set)
37064 this.groups[set.name] = set;
37067 get: function(name)
37069 if (typeof(this.groups[name]) == 'undefined') {
37073 return this.groups[name] ;
37079 * Ext JS Library 1.1.1
37080 * Copyright(c) 2006-2007, Ext JS, LLC.
37082 * Originally Released Under LGPL - original licence link has changed is not relivant.
37085 * <script type="text/javascript">
37090 * @class Roo.bootstrap.SplitBar
37091 * @extends Roo.util.Observable
37092 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37096 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37097 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37098 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37099 split.minSize = 100;
37100 split.maxSize = 600;
37101 split.animate = true;
37102 split.on('moved', splitterMoved);
37105 * Create a new SplitBar
37106 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37107 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37108 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37109 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37110 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37111 position of the SplitBar).
37113 Roo.bootstrap.SplitBar = function(cfg){
37118 // dragElement : elm
37119 // resizingElement: el,
37121 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37122 // placement : Roo.bootstrap.SplitBar.LEFT ,
37123 // existingProxy ???
37126 this.el = Roo.get(cfg.dragElement, true);
37127 this.el.dom.unselectable = "on";
37129 this.resizingEl = Roo.get(cfg.resizingElement, true);
37133 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37134 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37137 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37140 * The minimum size of the resizing element. (Defaults to 0)
37146 * The maximum size of the resizing element. (Defaults to 2000)
37149 this.maxSize = 2000;
37152 * Whether to animate the transition to the new size
37155 this.animate = false;
37158 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37161 this.useShim = false;
37166 if(!cfg.existingProxy){
37168 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37170 this.proxy = Roo.get(cfg.existingProxy).dom;
37173 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37176 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37179 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37182 this.dragSpecs = {};
37185 * @private The adapter to use to positon and resize elements
37187 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37188 this.adapter.init(this);
37190 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37192 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37193 this.el.addClass("roo-splitbar-h");
37196 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37197 this.el.addClass("roo-splitbar-v");
37203 * Fires when the splitter is moved (alias for {@link #event-moved})
37204 * @param {Roo.bootstrap.SplitBar} this
37205 * @param {Number} newSize the new width or height
37210 * Fires when the splitter is moved
37211 * @param {Roo.bootstrap.SplitBar} this
37212 * @param {Number} newSize the new width or height
37216 * @event beforeresize
37217 * Fires before the splitter is dragged
37218 * @param {Roo.bootstrap.SplitBar} this
37220 "beforeresize" : true,
37222 "beforeapply" : true
37225 Roo.util.Observable.call(this);
37228 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37229 onStartProxyDrag : function(x, y){
37230 this.fireEvent("beforeresize", this);
37232 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37234 o.enableDisplayMode("block");
37235 // all splitbars share the same overlay
37236 Roo.bootstrap.SplitBar.prototype.overlay = o;
37238 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37239 this.overlay.show();
37240 Roo.get(this.proxy).setDisplayed("block");
37241 var size = this.adapter.getElementSize(this);
37242 this.activeMinSize = this.getMinimumSize();;
37243 this.activeMaxSize = this.getMaximumSize();;
37244 var c1 = size - this.activeMinSize;
37245 var c2 = Math.max(this.activeMaxSize - size, 0);
37246 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37247 this.dd.resetConstraints();
37248 this.dd.setXConstraint(
37249 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37250 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37252 this.dd.setYConstraint(0, 0);
37254 this.dd.resetConstraints();
37255 this.dd.setXConstraint(0, 0);
37256 this.dd.setYConstraint(
37257 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37258 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37261 this.dragSpecs.startSize = size;
37262 this.dragSpecs.startPoint = [x, y];
37263 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37267 * @private Called after the drag operation by the DDProxy
37269 onEndProxyDrag : function(e){
37270 Roo.get(this.proxy).setDisplayed(false);
37271 var endPoint = Roo.lib.Event.getXY(e);
37273 this.overlay.hide();
37276 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37277 newSize = this.dragSpecs.startSize +
37278 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37279 endPoint[0] - this.dragSpecs.startPoint[0] :
37280 this.dragSpecs.startPoint[0] - endPoint[0]
37283 newSize = this.dragSpecs.startSize +
37284 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37285 endPoint[1] - this.dragSpecs.startPoint[1] :
37286 this.dragSpecs.startPoint[1] - endPoint[1]
37289 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37290 if(newSize != this.dragSpecs.startSize){
37291 if(this.fireEvent('beforeapply', this, newSize) !== false){
37292 this.adapter.setElementSize(this, newSize);
37293 this.fireEvent("moved", this, newSize);
37294 this.fireEvent("resize", this, newSize);
37300 * Get the adapter this SplitBar uses
37301 * @return The adapter object
37303 getAdapter : function(){
37304 return this.adapter;
37308 * Set the adapter this SplitBar uses
37309 * @param {Object} adapter A SplitBar adapter object
37311 setAdapter : function(adapter){
37312 this.adapter = adapter;
37313 this.adapter.init(this);
37317 * Gets the minimum size for the resizing element
37318 * @return {Number} The minimum size
37320 getMinimumSize : function(){
37321 return this.minSize;
37325 * Sets the minimum size for the resizing element
37326 * @param {Number} minSize The minimum size
37328 setMinimumSize : function(minSize){
37329 this.minSize = minSize;
37333 * Gets the maximum size for the resizing element
37334 * @return {Number} The maximum size
37336 getMaximumSize : function(){
37337 return this.maxSize;
37341 * Sets the maximum size for the resizing element
37342 * @param {Number} maxSize The maximum size
37344 setMaximumSize : function(maxSize){
37345 this.maxSize = maxSize;
37349 * Sets the initialize size for the resizing element
37350 * @param {Number} size The initial size
37352 setCurrentSize : function(size){
37353 var oldAnimate = this.animate;
37354 this.animate = false;
37355 this.adapter.setElementSize(this, size);
37356 this.animate = oldAnimate;
37360 * Destroy this splitbar.
37361 * @param {Boolean} removeEl True to remove the element
37363 destroy : function(removeEl){
37365 this.shim.remove();
37368 this.proxy.parentNode.removeChild(this.proxy);
37376 * @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.
37378 Roo.bootstrap.SplitBar.createProxy = function(dir){
37379 var proxy = new Roo.Element(document.createElement("div"));
37380 proxy.unselectable();
37381 var cls = 'roo-splitbar-proxy';
37382 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37383 document.body.appendChild(proxy.dom);
37388 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37389 * Default Adapter. It assumes the splitter and resizing element are not positioned
37390 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37392 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37395 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37396 // do nothing for now
37397 init : function(s){
37401 * Called before drag operations to get the current size of the resizing element.
37402 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37404 getElementSize : function(s){
37405 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37406 return s.resizingEl.getWidth();
37408 return s.resizingEl.getHeight();
37413 * Called after drag operations to set the size of the resizing element.
37414 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37415 * @param {Number} newSize The new size to set
37416 * @param {Function} onComplete A function to be invoked when resizing is complete
37418 setElementSize : function(s, newSize, onComplete){
37419 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37421 s.resizingEl.setWidth(newSize);
37423 onComplete(s, newSize);
37426 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37431 s.resizingEl.setHeight(newSize);
37433 onComplete(s, newSize);
37436 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37443 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37444 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37445 * Adapter that moves the splitter element to align with the resized sizing element.
37446 * Used with an absolute positioned SplitBar.
37447 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37448 * document.body, make sure you assign an id to the body element.
37450 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37451 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37452 this.container = Roo.get(container);
37455 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37456 init : function(s){
37457 this.basic.init(s);
37460 getElementSize : function(s){
37461 return this.basic.getElementSize(s);
37464 setElementSize : function(s, newSize, onComplete){
37465 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37468 moveSplitter : function(s){
37469 var yes = Roo.bootstrap.SplitBar;
37470 switch(s.placement){
37472 s.el.setX(s.resizingEl.getRight());
37475 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37478 s.el.setY(s.resizingEl.getBottom());
37481 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37488 * Orientation constant - Create a vertical SplitBar
37492 Roo.bootstrap.SplitBar.VERTICAL = 1;
37495 * Orientation constant - Create a horizontal SplitBar
37499 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37502 * Placement constant - The resizing element is to the left of the splitter element
37506 Roo.bootstrap.SplitBar.LEFT = 1;
37509 * Placement constant - The resizing element is to the right of the splitter element
37513 Roo.bootstrap.SplitBar.RIGHT = 2;
37516 * Placement constant - The resizing element is positioned above the splitter element
37520 Roo.bootstrap.SplitBar.TOP = 3;
37523 * Placement constant - The resizing element is positioned under splitter element
37527 Roo.bootstrap.SplitBar.BOTTOM = 4;
37528 Roo.namespace("Roo.bootstrap.layout");/*
37530 * Ext JS Library 1.1.1
37531 * Copyright(c) 2006-2007, Ext JS, LLC.
37533 * Originally Released Under LGPL - original licence link has changed is not relivant.
37536 * <script type="text/javascript">
37540 * @class Roo.bootstrap.layout.Manager
37541 * @extends Roo.bootstrap.Component
37542 * Base class for layout managers.
37544 Roo.bootstrap.layout.Manager = function(config)
37546 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37552 /** false to disable window resize monitoring @type Boolean */
37553 this.monitorWindowResize = true;
37558 * Fires when a layout is performed.
37559 * @param {Roo.LayoutManager} this
37563 * @event regionresized
37564 * Fires when the user resizes a region.
37565 * @param {Roo.LayoutRegion} region The resized region
37566 * @param {Number} newSize The new size (width for east/west, height for north/south)
37568 "regionresized" : true,
37570 * @event regioncollapsed
37571 * Fires when a region is collapsed.
37572 * @param {Roo.LayoutRegion} region The collapsed region
37574 "regioncollapsed" : true,
37576 * @event regionexpanded
37577 * Fires when a region is expanded.
37578 * @param {Roo.LayoutRegion} region The expanded region
37580 "regionexpanded" : true
37582 this.updating = false;
37585 this.el = Roo.get(config.el);
37591 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37596 monitorWindowResize : true,
37602 onRender : function(ct, position)
37605 this.el = Roo.get(ct);
37608 //this.fireEvent('render',this);
37612 initEvents: function()
37616 // ie scrollbar fix
37617 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37618 document.body.scroll = "no";
37619 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37620 this.el.position('relative');
37622 this.id = this.el.id;
37623 this.el.addClass("roo-layout-container");
37624 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37625 if(this.el.dom != document.body ) {
37626 this.el.on('resize', this.layout,this);
37627 this.el.on('show', this.layout,this);
37633 * Returns true if this layout is currently being updated
37634 * @return {Boolean}
37636 isUpdating : function(){
37637 return this.updating;
37641 * Suspend the LayoutManager from doing auto-layouts while
37642 * making multiple add or remove calls
37644 beginUpdate : function(){
37645 this.updating = true;
37649 * Restore auto-layouts and optionally disable the manager from performing a layout
37650 * @param {Boolean} noLayout true to disable a layout update
37652 endUpdate : function(noLayout){
37653 this.updating = false;
37659 layout: function(){
37663 onRegionResized : function(region, newSize){
37664 this.fireEvent("regionresized", region, newSize);
37668 onRegionCollapsed : function(region){
37669 this.fireEvent("regioncollapsed", region);
37672 onRegionExpanded : function(region){
37673 this.fireEvent("regionexpanded", region);
37677 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37678 * performs box-model adjustments.
37679 * @return {Object} The size as an object {width: (the width), height: (the height)}
37681 getViewSize : function()
37684 if(this.el.dom != document.body){
37685 size = this.el.getSize();
37687 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37689 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37690 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37695 * Returns the Element this layout is bound to.
37696 * @return {Roo.Element}
37698 getEl : function(){
37703 * Returns the specified region.
37704 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37705 * @return {Roo.LayoutRegion}
37707 getRegion : function(target){
37708 return this.regions[target.toLowerCase()];
37711 onWindowResize : function(){
37712 if(this.monitorWindowResize){
37719 * Ext JS Library 1.1.1
37720 * Copyright(c) 2006-2007, Ext JS, LLC.
37722 * Originally Released Under LGPL - original licence link has changed is not relivant.
37725 * <script type="text/javascript">
37728 * @class Roo.bootstrap.layout.Border
37729 * @extends Roo.bootstrap.layout.Manager
37730 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37731 * please see: examples/bootstrap/nested.html<br><br>
37733 <b>The container the layout is rendered into can be either the body element or any other element.
37734 If it is not the body element, the container needs to either be an absolute positioned element,
37735 or you will need to add "position:relative" to the css of the container. You will also need to specify
37736 the container size if it is not the body element.</b>
37739 * Create a new Border
37740 * @param {Object} config Configuration options
37742 Roo.bootstrap.layout.Border = function(config){
37743 config = config || {};
37744 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37748 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37749 if(config[region]){
37750 config[region].region = region;
37751 this.addRegion(config[region]);
37757 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37759 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37761 parent : false, // this might point to a 'nest' or a ???
37764 * Creates and adds a new region if it doesn't already exist.
37765 * @param {String} target The target region key (north, south, east, west or center).
37766 * @param {Object} config The regions config object
37767 * @return {BorderLayoutRegion} The new region
37769 addRegion : function(config)
37771 if(!this.regions[config.region]){
37772 var r = this.factory(config);
37773 this.bindRegion(r);
37775 return this.regions[config.region];
37779 bindRegion : function(r){
37780 this.regions[r.config.region] = r;
37782 r.on("visibilitychange", this.layout, this);
37783 r.on("paneladded", this.layout, this);
37784 r.on("panelremoved", this.layout, this);
37785 r.on("invalidated", this.layout, this);
37786 r.on("resized", this.onRegionResized, this);
37787 r.on("collapsed", this.onRegionCollapsed, this);
37788 r.on("expanded", this.onRegionExpanded, this);
37792 * Performs a layout update.
37794 layout : function()
37796 if(this.updating) {
37800 // render all the rebions if they have not been done alreayd?
37801 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37802 if(this.regions[region] && !this.regions[region].bodyEl){
37803 this.regions[region].onRender(this.el)
37807 var size = this.getViewSize();
37808 var w = size.width;
37809 var h = size.height;
37814 //var x = 0, y = 0;
37816 var rs = this.regions;
37817 var north = rs["north"];
37818 var south = rs["south"];
37819 var west = rs["west"];
37820 var east = rs["east"];
37821 var center = rs["center"];
37822 //if(this.hideOnLayout){ // not supported anymore
37823 //c.el.setStyle("display", "none");
37825 if(north && north.isVisible()){
37826 var b = north.getBox();
37827 var m = north.getMargins();
37828 b.width = w - (m.left+m.right);
37831 centerY = b.height + b.y + m.bottom;
37832 centerH -= centerY;
37833 north.updateBox(this.safeBox(b));
37835 if(south && south.isVisible()){
37836 var b = south.getBox();
37837 var m = south.getMargins();
37838 b.width = w - (m.left+m.right);
37840 var totalHeight = (b.height + m.top + m.bottom);
37841 b.y = h - totalHeight + m.top;
37842 centerH -= totalHeight;
37843 south.updateBox(this.safeBox(b));
37845 if(west && west.isVisible()){
37846 var b = west.getBox();
37847 var m = west.getMargins();
37848 b.height = centerH - (m.top+m.bottom);
37850 b.y = centerY + m.top;
37851 var totalWidth = (b.width + m.left + m.right);
37852 centerX += totalWidth;
37853 centerW -= totalWidth;
37854 west.updateBox(this.safeBox(b));
37856 if(east && east.isVisible()){
37857 var b = east.getBox();
37858 var m = east.getMargins();
37859 b.height = centerH - (m.top+m.bottom);
37860 var totalWidth = (b.width + m.left + m.right);
37861 b.x = w - totalWidth + m.left;
37862 b.y = centerY + m.top;
37863 centerW -= totalWidth;
37864 east.updateBox(this.safeBox(b));
37867 var m = center.getMargins();
37869 x: centerX + m.left,
37870 y: centerY + m.top,
37871 width: centerW - (m.left+m.right),
37872 height: centerH - (m.top+m.bottom)
37874 //if(this.hideOnLayout){
37875 //center.el.setStyle("display", "block");
37877 center.updateBox(this.safeBox(centerBox));
37880 this.fireEvent("layout", this);
37884 safeBox : function(box){
37885 box.width = Math.max(0, box.width);
37886 box.height = Math.max(0, box.height);
37891 * Adds a ContentPanel (or subclass) to this layout.
37892 * @param {String} target The target region key (north, south, east, west or center).
37893 * @param {Roo.ContentPanel} panel The panel to add
37894 * @return {Roo.ContentPanel} The added panel
37896 add : function(target, panel){
37898 target = target.toLowerCase();
37899 return this.regions[target].add(panel);
37903 * Remove a ContentPanel (or subclass) to this layout.
37904 * @param {String} target The target region key (north, south, east, west or center).
37905 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37906 * @return {Roo.ContentPanel} The removed panel
37908 remove : function(target, panel){
37909 target = target.toLowerCase();
37910 return this.regions[target].remove(panel);
37914 * Searches all regions for a panel with the specified id
37915 * @param {String} panelId
37916 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37918 findPanel : function(panelId){
37919 var rs = this.regions;
37920 for(var target in rs){
37921 if(typeof rs[target] != "function"){
37922 var p = rs[target].getPanel(panelId);
37932 * Searches all regions for a panel with the specified id and activates (shows) it.
37933 * @param {String/ContentPanel} panelId The panels id or the panel itself
37934 * @return {Roo.ContentPanel} The shown panel or null
37936 showPanel : function(panelId) {
37937 var rs = this.regions;
37938 for(var target in rs){
37939 var r = rs[target];
37940 if(typeof r != "function"){
37941 if(r.hasPanel(panelId)){
37942 return r.showPanel(panelId);
37950 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37951 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37954 restoreState : function(provider){
37956 provider = Roo.state.Manager;
37958 var sm = new Roo.LayoutStateManager();
37959 sm.init(this, provider);
37965 * Adds a xtype elements to the layout.
37969 xtype : 'ContentPanel',
37976 xtype : 'NestedLayoutPanel',
37982 items : [ ... list of content panels or nested layout panels.. ]
37986 * @param {Object} cfg Xtype definition of item to add.
37988 addxtype : function(cfg)
37990 // basically accepts a pannel...
37991 // can accept a layout region..!?!?
37992 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37995 // theory? children can only be panels??
37997 //if (!cfg.xtype.match(/Panel$/)) {
38002 if (typeof(cfg.region) == 'undefined') {
38003 Roo.log("Failed to add Panel, region was not set");
38007 var region = cfg.region;
38013 xitems = cfg.items;
38018 if ( region == 'center') {
38019 Roo.log("Center: " + cfg.title);
38025 case 'Content': // ContentPanel (el, cfg)
38026 case 'Scroll': // ContentPanel (el, cfg)
38028 cfg.autoCreate = cfg.autoCreate || true;
38029 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38031 // var el = this.el.createChild();
38032 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38035 this.add(region, ret);
38039 case 'TreePanel': // our new panel!
38040 cfg.el = this.el.createChild();
38041 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38042 this.add(region, ret);
38047 // create a new Layout (which is a Border Layout...
38049 var clayout = cfg.layout;
38050 clayout.el = this.el.createChild();
38051 clayout.items = clayout.items || [];
38055 // replace this exitems with the clayout ones..
38056 xitems = clayout.items;
38058 // force background off if it's in center...
38059 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38060 cfg.background = false;
38062 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38065 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38066 //console.log('adding nested layout panel ' + cfg.toSource());
38067 this.add(region, ret);
38068 nb = {}; /// find first...
38073 // needs grid and region
38075 //var el = this.getRegion(region).el.createChild();
38077 *var el = this.el.createChild();
38078 // create the grid first...
38079 cfg.grid.container = el;
38080 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38083 if (region == 'center' && this.active ) {
38084 cfg.background = false;
38087 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38089 this.add(region, ret);
38091 if (cfg.background) {
38092 // render grid on panel activation (if panel background)
38093 ret.on('activate', function(gp) {
38094 if (!gp.grid.rendered) {
38095 // gp.grid.render(el);
38099 // cfg.grid.render(el);
38105 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38106 // it was the old xcomponent building that caused this before.
38107 // espeically if border is the top element in the tree.
38117 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38119 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38120 this.add(region, ret);
38124 throw "Can not add '" + cfg.xtype + "' to Border";
38130 this.beginUpdate();
38134 Roo.each(xitems, function(i) {
38135 region = nb && i.region ? i.region : false;
38137 var add = ret.addxtype(i);
38140 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38141 if (!i.background) {
38142 abn[region] = nb[region] ;
38149 // make the last non-background panel active..
38150 //if (nb) { Roo.log(abn); }
38153 for(var r in abn) {
38154 region = this.getRegion(r);
38156 // tried using nb[r], but it does not work..
38158 region.showPanel(abn[r]);
38169 factory : function(cfg)
38172 var validRegions = Roo.bootstrap.layout.Border.regions;
38174 var target = cfg.region;
38177 var r = Roo.bootstrap.layout;
38181 return new r.North(cfg);
38183 return new r.South(cfg);
38185 return new r.East(cfg);
38187 return new r.West(cfg);
38189 return new r.Center(cfg);
38191 throw 'Layout region "'+target+'" not supported.';
38198 * Ext JS Library 1.1.1
38199 * Copyright(c) 2006-2007, Ext JS, LLC.
38201 * Originally Released Under LGPL - original licence link has changed is not relivant.
38204 * <script type="text/javascript">
38208 * @class Roo.bootstrap.layout.Basic
38209 * @extends Roo.util.Observable
38210 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38211 * and does not have a titlebar, tabs or any other features. All it does is size and position
38212 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38213 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38214 * @cfg {string} region the region that it inhabits..
38215 * @cfg {bool} skipConfig skip config?
38219 Roo.bootstrap.layout.Basic = function(config){
38221 this.mgr = config.mgr;
38223 this.position = config.region;
38225 var skipConfig = config.skipConfig;
38229 * @scope Roo.BasicLayoutRegion
38233 * @event beforeremove
38234 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38235 * @param {Roo.LayoutRegion} this
38236 * @param {Roo.ContentPanel} panel The panel
38237 * @param {Object} e The cancel event object
38239 "beforeremove" : true,
38241 * @event invalidated
38242 * Fires when the layout for this region is changed.
38243 * @param {Roo.LayoutRegion} this
38245 "invalidated" : true,
38247 * @event visibilitychange
38248 * Fires when this region is shown or hidden
38249 * @param {Roo.LayoutRegion} this
38250 * @param {Boolean} visibility true or false
38252 "visibilitychange" : true,
38254 * @event paneladded
38255 * Fires when a panel is added.
38256 * @param {Roo.LayoutRegion} this
38257 * @param {Roo.ContentPanel} panel The panel
38259 "paneladded" : true,
38261 * @event panelremoved
38262 * Fires when a panel is removed.
38263 * @param {Roo.LayoutRegion} this
38264 * @param {Roo.ContentPanel} panel The panel
38266 "panelremoved" : true,
38268 * @event beforecollapse
38269 * Fires when this region before collapse.
38270 * @param {Roo.LayoutRegion} this
38272 "beforecollapse" : true,
38275 * Fires when this region is collapsed.
38276 * @param {Roo.LayoutRegion} this
38278 "collapsed" : true,
38281 * Fires when this region is expanded.
38282 * @param {Roo.LayoutRegion} this
38287 * Fires when this region is slid into view.
38288 * @param {Roo.LayoutRegion} this
38290 "slideshow" : true,
38293 * Fires when this region slides out of view.
38294 * @param {Roo.LayoutRegion} this
38296 "slidehide" : true,
38298 * @event panelactivated
38299 * Fires when a panel is activated.
38300 * @param {Roo.LayoutRegion} this
38301 * @param {Roo.ContentPanel} panel The activated panel
38303 "panelactivated" : true,
38306 * Fires when the user resizes this region.
38307 * @param {Roo.LayoutRegion} this
38308 * @param {Number} newSize The new size (width for east/west, height for north/south)
38312 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38313 this.panels = new Roo.util.MixedCollection();
38314 this.panels.getKey = this.getPanelId.createDelegate(this);
38316 this.activePanel = null;
38317 // ensure listeners are added...
38319 if (config.listeners || config.events) {
38320 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38321 listeners : config.listeners || {},
38322 events : config.events || {}
38326 if(skipConfig !== true){
38327 this.applyConfig(config);
38331 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38333 getPanelId : function(p){
38337 applyConfig : function(config){
38338 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38339 this.config = config;
38344 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38345 * the width, for horizontal (north, south) the height.
38346 * @param {Number} newSize The new width or height
38348 resizeTo : function(newSize){
38349 var el = this.el ? this.el :
38350 (this.activePanel ? this.activePanel.getEl() : null);
38352 switch(this.position){
38355 el.setWidth(newSize);
38356 this.fireEvent("resized", this, newSize);
38360 el.setHeight(newSize);
38361 this.fireEvent("resized", this, newSize);
38367 getBox : function(){
38368 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38371 getMargins : function(){
38372 return this.margins;
38375 updateBox : function(box){
38377 var el = this.activePanel.getEl();
38378 el.dom.style.left = box.x + "px";
38379 el.dom.style.top = box.y + "px";
38380 this.activePanel.setSize(box.width, box.height);
38384 * Returns the container element for this region.
38385 * @return {Roo.Element}
38387 getEl : function(){
38388 return this.activePanel;
38392 * Returns true if this region is currently visible.
38393 * @return {Boolean}
38395 isVisible : function(){
38396 return this.activePanel ? true : false;
38399 setActivePanel : function(panel){
38400 panel = this.getPanel(panel);
38401 if(this.activePanel && this.activePanel != panel){
38402 this.activePanel.setActiveState(false);
38403 this.activePanel.getEl().setLeftTop(-10000,-10000);
38405 this.activePanel = panel;
38406 panel.setActiveState(true);
38408 panel.setSize(this.box.width, this.box.height);
38410 this.fireEvent("panelactivated", this, panel);
38411 this.fireEvent("invalidated");
38415 * Show the specified panel.
38416 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38417 * @return {Roo.ContentPanel} The shown panel or null
38419 showPanel : function(panel){
38420 panel = this.getPanel(panel);
38422 this.setActivePanel(panel);
38428 * Get the active panel for this region.
38429 * @return {Roo.ContentPanel} The active panel or null
38431 getActivePanel : function(){
38432 return this.activePanel;
38436 * Add the passed ContentPanel(s)
38437 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38438 * @return {Roo.ContentPanel} The panel added (if only one was added)
38440 add : function(panel){
38441 if(arguments.length > 1){
38442 for(var i = 0, len = arguments.length; i < len; i++) {
38443 this.add(arguments[i]);
38447 if(this.hasPanel(panel)){
38448 this.showPanel(panel);
38451 var el = panel.getEl();
38452 if(el.dom.parentNode != this.mgr.el.dom){
38453 this.mgr.el.dom.appendChild(el.dom);
38455 if(panel.setRegion){
38456 panel.setRegion(this);
38458 this.panels.add(panel);
38459 el.setStyle("position", "absolute");
38460 if(!panel.background){
38461 this.setActivePanel(panel);
38462 if(this.config.initialSize && this.panels.getCount()==1){
38463 this.resizeTo(this.config.initialSize);
38466 this.fireEvent("paneladded", this, panel);
38471 * Returns true if the panel is in this region.
38472 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38473 * @return {Boolean}
38475 hasPanel : function(panel){
38476 if(typeof panel == "object"){ // must be panel obj
38477 panel = panel.getId();
38479 return this.getPanel(panel) ? true : false;
38483 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38484 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38485 * @param {Boolean} preservePanel Overrides the config preservePanel option
38486 * @return {Roo.ContentPanel} The panel that was removed
38488 remove : function(panel, preservePanel){
38489 panel = this.getPanel(panel);
38494 this.fireEvent("beforeremove", this, panel, e);
38495 if(e.cancel === true){
38498 var panelId = panel.getId();
38499 this.panels.removeKey(panelId);
38504 * Returns the panel specified or null if it's not in this region.
38505 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38506 * @return {Roo.ContentPanel}
38508 getPanel : function(id){
38509 if(typeof id == "object"){ // must be panel obj
38512 return this.panels.get(id);
38516 * Returns this regions position (north/south/east/west/center).
38519 getPosition: function(){
38520 return this.position;
38524 * Ext JS Library 1.1.1
38525 * Copyright(c) 2006-2007, Ext JS, LLC.
38527 * Originally Released Under LGPL - original licence link has changed is not relivant.
38530 * <script type="text/javascript">
38534 * @class Roo.bootstrap.layout.Region
38535 * @extends Roo.bootstrap.layout.Basic
38536 * This class represents a region in a layout manager.
38538 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38539 * @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})
38540 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38541 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38542 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38543 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38544 * @cfg {String} title The title for the region (overrides panel titles)
38545 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38546 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38547 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38548 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38549 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38550 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38551 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38552 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38553 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38554 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38556 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38557 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38558 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38559 * @cfg {Number} width For East/West panels
38560 * @cfg {Number} height For North/South panels
38561 * @cfg {Boolean} split To show the splitter
38562 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38564 * @cfg {string} cls Extra CSS classes to add to region
38566 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38567 * @cfg {string} region the region that it inhabits..
38570 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38571 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38573 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38574 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38575 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38577 Roo.bootstrap.layout.Region = function(config)
38579 this.applyConfig(config);
38581 var mgr = config.mgr;
38582 var pos = config.region;
38583 config.skipConfig = true;
38584 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38587 this.onRender(mgr.el);
38590 this.visible = true;
38591 this.collapsed = false;
38592 this.unrendered_panels = [];
38595 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38597 position: '', // set by wrapper (eg. north/south etc..)
38598 unrendered_panels : null, // unrendered panels.
38600 tabPosition : false,
38602 mgr: false, // points to 'Border'
38605 createBody : function(){
38606 /** This region's body element
38607 * @type Roo.Element */
38608 this.bodyEl = this.el.createChild({
38610 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38614 onRender: function(ctr, pos)
38616 var dh = Roo.DomHelper;
38617 /** This region's container element
38618 * @type Roo.Element */
38619 this.el = dh.append(ctr.dom, {
38621 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38623 /** This region's title element
38624 * @type Roo.Element */
38626 this.titleEl = dh.append(this.el.dom, {
38628 unselectable: "on",
38629 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38631 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38632 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38636 this.titleEl.enableDisplayMode();
38637 /** This region's title text element
38638 * @type HTMLElement */
38639 this.titleTextEl = this.titleEl.dom.firstChild;
38640 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38642 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38643 this.closeBtn.enableDisplayMode();
38644 this.closeBtn.on("click", this.closeClicked, this);
38645 this.closeBtn.hide();
38647 this.createBody(this.config);
38648 if(this.config.hideWhenEmpty){
38650 this.on("paneladded", this.validateVisibility, this);
38651 this.on("panelremoved", this.validateVisibility, this);
38653 if(this.autoScroll){
38654 this.bodyEl.setStyle("overflow", "auto");
38656 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38658 //if(c.titlebar !== false){
38659 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38660 this.titleEl.hide();
38662 this.titleEl.show();
38663 if(this.config.title){
38664 this.titleTextEl.innerHTML = this.config.title;
38668 if(this.config.collapsed){
38669 this.collapse(true);
38671 if(this.config.hidden){
38675 if (this.unrendered_panels && this.unrendered_panels.length) {
38676 for (var i =0;i< this.unrendered_panels.length; i++) {
38677 this.add(this.unrendered_panels[i]);
38679 this.unrendered_panels = null;
38685 applyConfig : function(c)
38688 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38689 var dh = Roo.DomHelper;
38690 if(c.titlebar !== false){
38691 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38692 this.collapseBtn.on("click", this.collapse, this);
38693 this.collapseBtn.enableDisplayMode();
38695 if(c.showPin === true || this.showPin){
38696 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38697 this.stickBtn.enableDisplayMode();
38698 this.stickBtn.on("click", this.expand, this);
38699 this.stickBtn.hide();
38704 /** This region's collapsed element
38705 * @type Roo.Element */
38708 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38709 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38712 if(c.floatable !== false){
38713 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38714 this.collapsedEl.on("click", this.collapseClick, this);
38717 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38718 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38719 id: "message", unselectable: "on", style:{"float":"left"}});
38720 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38722 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38723 this.expandBtn.on("click", this.expand, this);
38727 if(this.collapseBtn){
38728 this.collapseBtn.setVisible(c.collapsible == true);
38731 this.cmargins = c.cmargins || this.cmargins ||
38732 (this.position == "west" || this.position == "east" ?
38733 {top: 0, left: 2, right:2, bottom: 0} :
38734 {top: 2, left: 0, right:0, bottom: 2});
38736 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38739 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38741 this.autoScroll = c.autoScroll || false;
38746 this.duration = c.duration || .30;
38747 this.slideDuration = c.slideDuration || .45;
38752 * Returns true if this region is currently visible.
38753 * @return {Boolean}
38755 isVisible : function(){
38756 return this.visible;
38760 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38761 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38763 //setCollapsedTitle : function(title){
38764 // title = title || " ";
38765 // if(this.collapsedTitleTextEl){
38766 // this.collapsedTitleTextEl.innerHTML = title;
38770 getBox : function(){
38772 // if(!this.collapsed){
38773 b = this.el.getBox(false, true);
38775 // b = this.collapsedEl.getBox(false, true);
38780 getMargins : function(){
38781 return this.margins;
38782 //return this.collapsed ? this.cmargins : this.margins;
38785 highlight : function(){
38786 this.el.addClass("x-layout-panel-dragover");
38789 unhighlight : function(){
38790 this.el.removeClass("x-layout-panel-dragover");
38793 updateBox : function(box)
38795 if (!this.bodyEl) {
38796 return; // not rendered yet..
38800 if(!this.collapsed){
38801 this.el.dom.style.left = box.x + "px";
38802 this.el.dom.style.top = box.y + "px";
38803 this.updateBody(box.width, box.height);
38805 this.collapsedEl.dom.style.left = box.x + "px";
38806 this.collapsedEl.dom.style.top = box.y + "px";
38807 this.collapsedEl.setSize(box.width, box.height);
38810 this.tabs.autoSizeTabs();
38814 updateBody : function(w, h)
38817 this.el.setWidth(w);
38818 w -= this.el.getBorderWidth("rl");
38819 if(this.config.adjustments){
38820 w += this.config.adjustments[0];
38823 if(h !== null && h > 0){
38824 this.el.setHeight(h);
38825 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38826 h -= this.el.getBorderWidth("tb");
38827 if(this.config.adjustments){
38828 h += this.config.adjustments[1];
38830 this.bodyEl.setHeight(h);
38832 h = this.tabs.syncHeight(h);
38835 if(this.panelSize){
38836 w = w !== null ? w : this.panelSize.width;
38837 h = h !== null ? h : this.panelSize.height;
38839 if(this.activePanel){
38840 var el = this.activePanel.getEl();
38841 w = w !== null ? w : el.getWidth();
38842 h = h !== null ? h : el.getHeight();
38843 this.panelSize = {width: w, height: h};
38844 this.activePanel.setSize(w, h);
38846 if(Roo.isIE && this.tabs){
38847 this.tabs.el.repaint();
38852 * Returns the container element for this region.
38853 * @return {Roo.Element}
38855 getEl : function(){
38860 * Hides this region.
38863 //if(!this.collapsed){
38864 this.el.dom.style.left = "-2000px";
38867 // this.collapsedEl.dom.style.left = "-2000px";
38868 // this.collapsedEl.hide();
38870 this.visible = false;
38871 this.fireEvent("visibilitychange", this, false);
38875 * Shows this region if it was previously hidden.
38878 //if(!this.collapsed){
38881 // this.collapsedEl.show();
38883 this.visible = true;
38884 this.fireEvent("visibilitychange", this, true);
38887 closeClicked : function(){
38888 if(this.activePanel){
38889 this.remove(this.activePanel);
38893 collapseClick : function(e){
38895 e.stopPropagation();
38898 e.stopPropagation();
38904 * Collapses this region.
38905 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38908 collapse : function(skipAnim, skipCheck = false){
38909 if(this.collapsed) {
38913 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38915 this.collapsed = true;
38917 this.split.el.hide();
38919 if(this.config.animate && skipAnim !== true){
38920 this.fireEvent("invalidated", this);
38921 this.animateCollapse();
38923 this.el.setLocation(-20000,-20000);
38925 this.collapsedEl.show();
38926 this.fireEvent("collapsed", this);
38927 this.fireEvent("invalidated", this);
38933 animateCollapse : function(){
38938 * Expands this region if it was previously collapsed.
38939 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38940 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38943 expand : function(e, skipAnim){
38945 e.stopPropagation();
38947 if(!this.collapsed || this.el.hasActiveFx()) {
38951 this.afterSlideIn();
38954 this.collapsed = false;
38955 if(this.config.animate && skipAnim !== true){
38956 this.animateExpand();
38960 this.split.el.show();
38962 this.collapsedEl.setLocation(-2000,-2000);
38963 this.collapsedEl.hide();
38964 this.fireEvent("invalidated", this);
38965 this.fireEvent("expanded", this);
38969 animateExpand : function(){
38973 initTabs : function()
38975 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38977 var ts = new Roo.bootstrap.panel.Tabs({
38978 el: this.bodyEl.dom,
38980 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38981 disableTooltips: this.config.disableTabTips,
38982 toolbar : this.config.toolbar
38985 if(this.config.hideTabs){
38986 ts.stripWrap.setDisplayed(false);
38989 ts.resizeTabs = this.config.resizeTabs === true;
38990 ts.minTabWidth = this.config.minTabWidth || 40;
38991 ts.maxTabWidth = this.config.maxTabWidth || 250;
38992 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38993 ts.monitorResize = false;
38994 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38995 ts.bodyEl.addClass('roo-layout-tabs-body');
38996 this.panels.each(this.initPanelAsTab, this);
38999 initPanelAsTab : function(panel){
39000 var ti = this.tabs.addTab(
39004 this.config.closeOnTab && panel.isClosable(),
39007 if(panel.tabTip !== undefined){
39008 ti.setTooltip(panel.tabTip);
39010 ti.on("activate", function(){
39011 this.setActivePanel(panel);
39014 if(this.config.closeOnTab){
39015 ti.on("beforeclose", function(t, e){
39017 this.remove(panel);
39021 panel.tabItem = ti;
39026 updatePanelTitle : function(panel, title)
39028 if(this.activePanel == panel){
39029 this.updateTitle(title);
39032 var ti = this.tabs.getTab(panel.getEl().id);
39034 if(panel.tabTip !== undefined){
39035 ti.setTooltip(panel.tabTip);
39040 updateTitle : function(title){
39041 if(this.titleTextEl && !this.config.title){
39042 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39046 setActivePanel : function(panel)
39048 panel = this.getPanel(panel);
39049 if(this.activePanel && this.activePanel != panel){
39050 if(this.activePanel.setActiveState(false) === false){
39054 this.activePanel = panel;
39055 panel.setActiveState(true);
39056 if(this.panelSize){
39057 panel.setSize(this.panelSize.width, this.panelSize.height);
39060 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39062 this.updateTitle(panel.getTitle());
39064 this.fireEvent("invalidated", this);
39066 this.fireEvent("panelactivated", this, panel);
39070 * Shows the specified panel.
39071 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39072 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39074 showPanel : function(panel)
39076 panel = this.getPanel(panel);
39079 var tab = this.tabs.getTab(panel.getEl().id);
39080 if(tab.isHidden()){
39081 this.tabs.unhideTab(tab.id);
39085 this.setActivePanel(panel);
39092 * Get the active panel for this region.
39093 * @return {Roo.ContentPanel} The active panel or null
39095 getActivePanel : function(){
39096 return this.activePanel;
39099 validateVisibility : function(){
39100 if(this.panels.getCount() < 1){
39101 this.updateTitle(" ");
39102 this.closeBtn.hide();
39105 if(!this.isVisible()){
39112 * Adds the passed ContentPanel(s) to this region.
39113 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39114 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39116 add : function(panel)
39118 if(arguments.length > 1){
39119 for(var i = 0, len = arguments.length; i < len; i++) {
39120 this.add(arguments[i]);
39125 // if we have not been rendered yet, then we can not really do much of this..
39126 if (!this.bodyEl) {
39127 this.unrendered_panels.push(panel);
39134 if(this.hasPanel(panel)){
39135 this.showPanel(panel);
39138 panel.setRegion(this);
39139 this.panels.add(panel);
39140 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39141 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39142 // and hide them... ???
39143 this.bodyEl.dom.appendChild(panel.getEl().dom);
39144 if(panel.background !== true){
39145 this.setActivePanel(panel);
39147 this.fireEvent("paneladded", this, panel);
39154 this.initPanelAsTab(panel);
39158 if(panel.background !== true){
39159 this.tabs.activate(panel.getEl().id);
39161 this.fireEvent("paneladded", this, panel);
39166 * Hides the tab for the specified panel.
39167 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39169 hidePanel : function(panel){
39170 if(this.tabs && (panel = this.getPanel(panel))){
39171 this.tabs.hideTab(panel.getEl().id);
39176 * Unhides the tab for a previously hidden panel.
39177 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39179 unhidePanel : function(panel){
39180 if(this.tabs && (panel = this.getPanel(panel))){
39181 this.tabs.unhideTab(panel.getEl().id);
39185 clearPanels : function(){
39186 while(this.panels.getCount() > 0){
39187 this.remove(this.panels.first());
39192 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39193 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39194 * @param {Boolean} preservePanel Overrides the config preservePanel option
39195 * @return {Roo.ContentPanel} The panel that was removed
39197 remove : function(panel, preservePanel)
39199 panel = this.getPanel(panel);
39204 this.fireEvent("beforeremove", this, panel, e);
39205 if(e.cancel === true){
39208 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39209 var panelId = panel.getId();
39210 this.panels.removeKey(panelId);
39212 document.body.appendChild(panel.getEl().dom);
39215 this.tabs.removeTab(panel.getEl().id);
39216 }else if (!preservePanel){
39217 this.bodyEl.dom.removeChild(panel.getEl().dom);
39219 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39220 var p = this.panels.first();
39221 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39222 tempEl.appendChild(p.getEl().dom);
39223 this.bodyEl.update("");
39224 this.bodyEl.dom.appendChild(p.getEl().dom);
39226 this.updateTitle(p.getTitle());
39228 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39229 this.setActivePanel(p);
39231 panel.setRegion(null);
39232 if(this.activePanel == panel){
39233 this.activePanel = null;
39235 if(this.config.autoDestroy !== false && preservePanel !== true){
39236 try{panel.destroy();}catch(e){}
39238 this.fireEvent("panelremoved", this, panel);
39243 * Returns the TabPanel component used by this region
39244 * @return {Roo.TabPanel}
39246 getTabs : function(){
39250 createTool : function(parentEl, className){
39251 var btn = Roo.DomHelper.append(parentEl, {
39253 cls: "x-layout-tools-button",
39256 cls: "roo-layout-tools-button-inner " + className,
39260 btn.addClassOnOver("roo-layout-tools-button-over");
39265 * Ext JS Library 1.1.1
39266 * Copyright(c) 2006-2007, Ext JS, LLC.
39268 * Originally Released Under LGPL - original licence link has changed is not relivant.
39271 * <script type="text/javascript">
39277 * @class Roo.SplitLayoutRegion
39278 * @extends Roo.LayoutRegion
39279 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39281 Roo.bootstrap.layout.Split = function(config){
39282 this.cursor = config.cursor;
39283 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39286 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39288 splitTip : "Drag to resize.",
39289 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39290 useSplitTips : false,
39292 applyConfig : function(config){
39293 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39296 onRender : function(ctr,pos) {
39298 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39299 if(!this.config.split){
39304 var splitEl = Roo.DomHelper.append(ctr.dom, {
39306 id: this.el.id + "-split",
39307 cls: "roo-layout-split roo-layout-split-"+this.position,
39310 /** The SplitBar for this region
39311 * @type Roo.SplitBar */
39312 // does not exist yet...
39313 Roo.log([this.position, this.orientation]);
39315 this.split = new Roo.bootstrap.SplitBar({
39316 dragElement : splitEl,
39317 resizingElement: this.el,
39318 orientation : this.orientation
39321 this.split.on("moved", this.onSplitMove, this);
39322 this.split.useShim = this.config.useShim === true;
39323 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39324 if(this.useSplitTips){
39325 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39327 //if(config.collapsible){
39328 // this.split.el.on("dblclick", this.collapse, this);
39331 if(typeof this.config.minSize != "undefined"){
39332 this.split.minSize = this.config.minSize;
39334 if(typeof this.config.maxSize != "undefined"){
39335 this.split.maxSize = this.config.maxSize;
39337 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39338 this.hideSplitter();
39343 getHMaxSize : function(){
39344 var cmax = this.config.maxSize || 10000;
39345 var center = this.mgr.getRegion("center");
39346 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39349 getVMaxSize : function(){
39350 var cmax = this.config.maxSize || 10000;
39351 var center = this.mgr.getRegion("center");
39352 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39355 onSplitMove : function(split, newSize){
39356 this.fireEvent("resized", this, newSize);
39360 * Returns the {@link Roo.SplitBar} for this region.
39361 * @return {Roo.SplitBar}
39363 getSplitBar : function(){
39368 this.hideSplitter();
39369 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39372 hideSplitter : function(){
39374 this.split.el.setLocation(-2000,-2000);
39375 this.split.el.hide();
39381 this.split.el.show();
39383 Roo.bootstrap.layout.Split.superclass.show.call(this);
39386 beforeSlide: function(){
39387 if(Roo.isGecko){// firefox overflow auto bug workaround
39388 this.bodyEl.clip();
39390 this.tabs.bodyEl.clip();
39392 if(this.activePanel){
39393 this.activePanel.getEl().clip();
39395 if(this.activePanel.beforeSlide){
39396 this.activePanel.beforeSlide();
39402 afterSlide : function(){
39403 if(Roo.isGecko){// firefox overflow auto bug workaround
39404 this.bodyEl.unclip();
39406 this.tabs.bodyEl.unclip();
39408 if(this.activePanel){
39409 this.activePanel.getEl().unclip();
39410 if(this.activePanel.afterSlide){
39411 this.activePanel.afterSlide();
39417 initAutoHide : function(){
39418 if(this.autoHide !== false){
39419 if(!this.autoHideHd){
39420 var st = new Roo.util.DelayedTask(this.slideIn, this);
39421 this.autoHideHd = {
39422 "mouseout": function(e){
39423 if(!e.within(this.el, true)){
39427 "mouseover" : function(e){
39433 this.el.on(this.autoHideHd);
39437 clearAutoHide : function(){
39438 if(this.autoHide !== false){
39439 this.el.un("mouseout", this.autoHideHd.mouseout);
39440 this.el.un("mouseover", this.autoHideHd.mouseover);
39444 clearMonitor : function(){
39445 Roo.get(document).un("click", this.slideInIf, this);
39448 // these names are backwards but not changed for compat
39449 slideOut : function(){
39450 if(this.isSlid || this.el.hasActiveFx()){
39453 this.isSlid = true;
39454 if(this.collapseBtn){
39455 this.collapseBtn.hide();
39457 this.closeBtnState = this.closeBtn.getStyle('display');
39458 this.closeBtn.hide();
39460 this.stickBtn.show();
39463 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39464 this.beforeSlide();
39465 this.el.setStyle("z-index", 10001);
39466 this.el.slideIn(this.getSlideAnchor(), {
39467 callback: function(){
39469 this.initAutoHide();
39470 Roo.get(document).on("click", this.slideInIf, this);
39471 this.fireEvent("slideshow", this);
39478 afterSlideIn : function(){
39479 this.clearAutoHide();
39480 this.isSlid = false;
39481 this.clearMonitor();
39482 this.el.setStyle("z-index", "");
39483 if(this.collapseBtn){
39484 this.collapseBtn.show();
39486 this.closeBtn.setStyle('display', this.closeBtnState);
39488 this.stickBtn.hide();
39490 this.fireEvent("slidehide", this);
39493 slideIn : function(cb){
39494 if(!this.isSlid || this.el.hasActiveFx()){
39498 this.isSlid = false;
39499 this.beforeSlide();
39500 this.el.slideOut(this.getSlideAnchor(), {
39501 callback: function(){
39502 this.el.setLeftTop(-10000, -10000);
39504 this.afterSlideIn();
39512 slideInIf : function(e){
39513 if(!e.within(this.el)){
39518 animateCollapse : function(){
39519 this.beforeSlide();
39520 this.el.setStyle("z-index", 20000);
39521 var anchor = this.getSlideAnchor();
39522 this.el.slideOut(anchor, {
39523 callback : function(){
39524 this.el.setStyle("z-index", "");
39525 this.collapsedEl.slideIn(anchor, {duration:.3});
39527 this.el.setLocation(-10000,-10000);
39529 this.fireEvent("collapsed", this);
39536 animateExpand : function(){
39537 this.beforeSlide();
39538 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39539 this.el.setStyle("z-index", 20000);
39540 this.collapsedEl.hide({
39543 this.el.slideIn(this.getSlideAnchor(), {
39544 callback : function(){
39545 this.el.setStyle("z-index", "");
39548 this.split.el.show();
39550 this.fireEvent("invalidated", this);
39551 this.fireEvent("expanded", this);
39579 getAnchor : function(){
39580 return this.anchors[this.position];
39583 getCollapseAnchor : function(){
39584 return this.canchors[this.position];
39587 getSlideAnchor : function(){
39588 return this.sanchors[this.position];
39591 getAlignAdj : function(){
39592 var cm = this.cmargins;
39593 switch(this.position){
39609 getExpandAdj : function(){
39610 var c = this.collapsedEl, cm = this.cmargins;
39611 switch(this.position){
39613 return [-(cm.right+c.getWidth()+cm.left), 0];
39616 return [cm.right+c.getWidth()+cm.left, 0];
39619 return [0, -(cm.top+cm.bottom+c.getHeight())];
39622 return [0, cm.top+cm.bottom+c.getHeight()];
39628 * Ext JS Library 1.1.1
39629 * Copyright(c) 2006-2007, Ext JS, LLC.
39631 * Originally Released Under LGPL - original licence link has changed is not relivant.
39634 * <script type="text/javascript">
39637 * These classes are private internal classes
39639 Roo.bootstrap.layout.Center = function(config){
39640 config.region = "center";
39641 Roo.bootstrap.layout.Region.call(this, config);
39642 this.visible = true;
39643 this.minWidth = config.minWidth || 20;
39644 this.minHeight = config.minHeight || 20;
39647 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39649 // center panel can't be hidden
39653 // center panel can't be hidden
39656 getMinWidth: function(){
39657 return this.minWidth;
39660 getMinHeight: function(){
39661 return this.minHeight;
39675 Roo.bootstrap.layout.North = function(config)
39677 config.region = 'north';
39678 config.cursor = 'n-resize';
39680 Roo.bootstrap.layout.Split.call(this, config);
39684 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39685 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39686 this.split.el.addClass("roo-layout-split-v");
39688 //var size = config.initialSize || config.height;
39689 //if(this.el && typeof size != "undefined"){
39690 // this.el.setHeight(size);
39693 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39695 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39698 onRender : function(ctr, pos)
39700 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39701 var size = this.config.initialSize || this.config.height;
39702 if(this.el && typeof size != "undefined"){
39703 this.el.setHeight(size);
39708 getBox : function(){
39709 if(this.collapsed){
39710 return this.collapsedEl.getBox();
39712 var box = this.el.getBox();
39714 box.height += this.split.el.getHeight();
39719 updateBox : function(box){
39720 if(this.split && !this.collapsed){
39721 box.height -= this.split.el.getHeight();
39722 this.split.el.setLeft(box.x);
39723 this.split.el.setTop(box.y+box.height);
39724 this.split.el.setWidth(box.width);
39726 if(this.collapsed){
39727 this.updateBody(box.width, null);
39729 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39737 Roo.bootstrap.layout.South = function(config){
39738 config.region = 'south';
39739 config.cursor = 's-resize';
39740 Roo.bootstrap.layout.Split.call(this, config);
39742 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39743 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39744 this.split.el.addClass("roo-layout-split-v");
39749 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39750 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39752 onRender : function(ctr, pos)
39754 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39755 var size = this.config.initialSize || this.config.height;
39756 if(this.el && typeof size != "undefined"){
39757 this.el.setHeight(size);
39762 getBox : function(){
39763 if(this.collapsed){
39764 return this.collapsedEl.getBox();
39766 var box = this.el.getBox();
39768 var sh = this.split.el.getHeight();
39775 updateBox : function(box){
39776 if(this.split && !this.collapsed){
39777 var sh = this.split.el.getHeight();
39780 this.split.el.setLeft(box.x);
39781 this.split.el.setTop(box.y-sh);
39782 this.split.el.setWidth(box.width);
39784 if(this.collapsed){
39785 this.updateBody(box.width, null);
39787 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39791 Roo.bootstrap.layout.East = function(config){
39792 config.region = "east";
39793 config.cursor = "e-resize";
39794 Roo.bootstrap.layout.Split.call(this, config);
39796 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39797 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39798 this.split.el.addClass("roo-layout-split-h");
39802 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39803 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39805 onRender : function(ctr, pos)
39807 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39808 var size = this.config.initialSize || this.config.width;
39809 if(this.el && typeof size != "undefined"){
39810 this.el.setWidth(size);
39815 getBox : function(){
39816 if(this.collapsed){
39817 return this.collapsedEl.getBox();
39819 var box = this.el.getBox();
39821 var sw = this.split.el.getWidth();
39828 updateBox : function(box){
39829 if(this.split && !this.collapsed){
39830 var sw = this.split.el.getWidth();
39832 this.split.el.setLeft(box.x);
39833 this.split.el.setTop(box.y);
39834 this.split.el.setHeight(box.height);
39837 if(this.collapsed){
39838 this.updateBody(null, box.height);
39840 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39844 Roo.bootstrap.layout.West = function(config){
39845 config.region = "west";
39846 config.cursor = "w-resize";
39848 Roo.bootstrap.layout.Split.call(this, config);
39850 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39851 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39852 this.split.el.addClass("roo-layout-split-h");
39856 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39857 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39859 onRender: function(ctr, pos)
39861 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39862 var size = this.config.initialSize || this.config.width;
39863 if(typeof size != "undefined"){
39864 this.el.setWidth(size);
39868 getBox : function(){
39869 if(this.collapsed){
39870 return this.collapsedEl.getBox();
39872 var box = this.el.getBox();
39873 if (box.width == 0) {
39874 box.width = this.config.width; // kludge?
39877 box.width += this.split.el.getWidth();
39882 updateBox : function(box){
39883 if(this.split && !this.collapsed){
39884 var sw = this.split.el.getWidth();
39886 this.split.el.setLeft(box.x+box.width);
39887 this.split.el.setTop(box.y);
39888 this.split.el.setHeight(box.height);
39890 if(this.collapsed){
39891 this.updateBody(null, box.height);
39893 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39895 });Roo.namespace("Roo.bootstrap.panel");/*
39897 * Ext JS Library 1.1.1
39898 * Copyright(c) 2006-2007, Ext JS, LLC.
39900 * Originally Released Under LGPL - original licence link has changed is not relivant.
39903 * <script type="text/javascript">
39906 * @class Roo.ContentPanel
39907 * @extends Roo.util.Observable
39908 * A basic ContentPanel element.
39909 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39910 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39911 * @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
39912 * @cfg {Boolean} closable True if the panel can be closed/removed
39913 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39914 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39915 * @cfg {Toolbar} toolbar A toolbar for this panel
39916 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39917 * @cfg {String} title The title for this panel
39918 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39919 * @cfg {String} url Calls {@link #setUrl} with this value
39920 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39921 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39922 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39923 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39924 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39925 * @cfg {Boolean} badges render the badges
39926 * @cfg {String} cls extra classes to use
39927 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39930 * Create a new ContentPanel.
39931 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39932 * @param {String/Object} config A string to set only the title or a config object
39933 * @param {String} content (optional) Set the HTML content for this panel
39934 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39936 Roo.bootstrap.panel.Content = function( config){
39938 this.tpl = config.tpl || false;
39940 var el = config.el;
39941 var content = config.content;
39943 if(config.autoCreate){ // xtype is available if this is called from factory
39946 this.el = Roo.get(el);
39947 if(!this.el && config && config.autoCreate){
39948 if(typeof config.autoCreate == "object"){
39949 if(!config.autoCreate.id){
39950 config.autoCreate.id = config.id||el;
39952 this.el = Roo.DomHelper.append(document.body,
39953 config.autoCreate, true);
39957 cls: (config.cls || '') +
39958 (config.background ? ' bg-' + config.background : '') +
39959 " roo-layout-inactive-content",
39962 if (config.iframe) {
39966 style : 'border: 0px',
39967 src : 'about:blank'
39973 elcfg.html = config.html;
39977 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39978 if (config.iframe) {
39979 this.iframeEl = this.el.select('iframe',true).first();
39984 this.closable = false;
39985 this.loaded = false;
39986 this.active = false;
39989 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39991 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39993 this.wrapEl = this.el; //this.el.wrap();
39995 if (config.toolbar.items) {
39996 ti = config.toolbar.items ;
39997 delete config.toolbar.items ;
40001 this.toolbar.render(this.wrapEl, 'before');
40002 for(var i =0;i < ti.length;i++) {
40003 // Roo.log(['add child', items[i]]);
40004 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40006 this.toolbar.items = nitems;
40007 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40008 delete config.toolbar;
40012 // xtype created footer. - not sure if will work as we normally have to render first..
40013 if (this.footer && !this.footer.el && this.footer.xtype) {
40014 if (!this.wrapEl) {
40015 this.wrapEl = this.el.wrap();
40018 this.footer.container = this.wrapEl.createChild();
40020 this.footer = Roo.factory(this.footer, Roo);
40025 if(typeof config == "string"){
40026 this.title = config;
40028 Roo.apply(this, config);
40032 this.resizeEl = Roo.get(this.resizeEl, true);
40034 this.resizeEl = this.el;
40036 // handle view.xtype
40044 * Fires when this panel is activated.
40045 * @param {Roo.ContentPanel} this
40049 * @event deactivate
40050 * Fires when this panel is activated.
40051 * @param {Roo.ContentPanel} this
40053 "deactivate" : true,
40057 * Fires when this panel is resized if fitToFrame is true.
40058 * @param {Roo.ContentPanel} this
40059 * @param {Number} width The width after any component adjustments
40060 * @param {Number} height The height after any component adjustments
40066 * Fires when this tab is created
40067 * @param {Roo.ContentPanel} this
40078 if(this.autoScroll && !this.iframe){
40079 this.resizeEl.setStyle("overflow", "auto");
40081 // fix randome scrolling
40082 //this.el.on('scroll', function() {
40083 // Roo.log('fix random scolling');
40084 // this.scrollTo('top',0);
40087 content = content || this.content;
40089 this.setContent(content);
40091 if(config && config.url){
40092 this.setUrl(this.url, this.params, this.loadOnce);
40097 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40099 if (this.view && typeof(this.view.xtype) != 'undefined') {
40100 this.view.el = this.el.appendChild(document.createElement("div"));
40101 this.view = Roo.factory(this.view);
40102 this.view.render && this.view.render(false, '');
40106 this.fireEvent('render', this);
40109 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40119 setRegion : function(region){
40120 this.region = region;
40121 this.setActiveClass(region && !this.background);
40125 setActiveClass: function(state)
40128 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40129 this.el.setStyle('position','relative');
40131 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40132 this.el.setStyle('position', 'absolute');
40137 * Returns the toolbar for this Panel if one was configured.
40138 * @return {Roo.Toolbar}
40140 getToolbar : function(){
40141 return this.toolbar;
40144 setActiveState : function(active)
40146 this.active = active;
40147 this.setActiveClass(active);
40149 if(this.fireEvent("deactivate", this) === false){
40154 this.fireEvent("activate", this);
40158 * Updates this panel's element (not for iframe)
40159 * @param {String} content The new content
40160 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40162 setContent : function(content, loadScripts){
40167 this.el.update(content, loadScripts);
40170 ignoreResize : function(w, h){
40171 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40174 this.lastSize = {width: w, height: h};
40179 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40180 * @return {Roo.UpdateManager} The UpdateManager
40182 getUpdateManager : function(){
40186 return this.el.getUpdateManager();
40189 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40190 * Does not work with IFRAME contents
40191 * @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:
40194 url: "your-url.php",
40195 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40196 callback: yourFunction,
40197 scope: yourObject, //(optional scope)
40200 text: "Loading...",
40206 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40207 * 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.
40208 * @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}
40209 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40210 * @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.
40211 * @return {Roo.ContentPanel} this
40219 var um = this.el.getUpdateManager();
40220 um.update.apply(um, arguments);
40226 * 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.
40227 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40228 * @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)
40229 * @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)
40230 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40232 setUrl : function(url, params, loadOnce){
40234 this.iframeEl.dom.src = url;
40238 if(this.refreshDelegate){
40239 this.removeListener("activate", this.refreshDelegate);
40241 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40242 this.on("activate", this.refreshDelegate);
40243 return this.el.getUpdateManager();
40246 _handleRefresh : function(url, params, loadOnce){
40247 if(!loadOnce || !this.loaded){
40248 var updater = this.el.getUpdateManager();
40249 updater.update(url, params, this._setLoaded.createDelegate(this));
40253 _setLoaded : function(){
40254 this.loaded = true;
40258 * Returns this panel's id
40261 getId : function(){
40266 * Returns this panel's element - used by regiosn to add.
40267 * @return {Roo.Element}
40269 getEl : function(){
40270 return this.wrapEl || this.el;
40275 adjustForComponents : function(width, height)
40277 //Roo.log('adjustForComponents ');
40278 if(this.resizeEl != this.el){
40279 width -= this.el.getFrameWidth('lr');
40280 height -= this.el.getFrameWidth('tb');
40283 var te = this.toolbar.getEl();
40284 te.setWidth(width);
40285 height -= te.getHeight();
40288 var te = this.footer.getEl();
40289 te.setWidth(width);
40290 height -= te.getHeight();
40294 if(this.adjustments){
40295 width += this.adjustments[0];
40296 height += this.adjustments[1];
40298 return {"width": width, "height": height};
40301 setSize : function(width, height){
40302 if(this.fitToFrame && !this.ignoreResize(width, height)){
40303 if(this.fitContainer && this.resizeEl != this.el){
40304 this.el.setSize(width, height);
40306 var size = this.adjustForComponents(width, height);
40308 this.iframeEl.setSize(width,height);
40311 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40312 this.fireEvent('resize', this, size.width, size.height);
40319 * Returns this panel's title
40322 getTitle : function(){
40324 if (typeof(this.title) != 'object') {
40329 for (var k in this.title) {
40330 if (!this.title.hasOwnProperty(k)) {
40334 if (k.indexOf('-') >= 0) {
40335 var s = k.split('-');
40336 for (var i = 0; i<s.length; i++) {
40337 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40340 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40347 * Set this panel's title
40348 * @param {String} title
40350 setTitle : function(title){
40351 this.title = title;
40353 this.region.updatePanelTitle(this, title);
40358 * Returns true is this panel was configured to be closable
40359 * @return {Boolean}
40361 isClosable : function(){
40362 return this.closable;
40365 beforeSlide : function(){
40367 this.resizeEl.clip();
40370 afterSlide : function(){
40372 this.resizeEl.unclip();
40376 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40377 * Will fail silently if the {@link #setUrl} method has not been called.
40378 * This does not activate the panel, just updates its content.
40380 refresh : function(){
40381 if(this.refreshDelegate){
40382 this.loaded = false;
40383 this.refreshDelegate();
40388 * Destroys this panel
40390 destroy : function(){
40391 this.el.removeAllListeners();
40392 var tempEl = document.createElement("span");
40393 tempEl.appendChild(this.el.dom);
40394 tempEl.innerHTML = "";
40400 * form - if the content panel contains a form - this is a reference to it.
40401 * @type {Roo.form.Form}
40405 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40406 * This contains a reference to it.
40412 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40422 * @param {Object} cfg Xtype definition of item to add.
40426 getChildContainer: function () {
40427 return this.getEl();
40432 var ret = new Roo.factory(cfg);
40437 if (cfg.xtype.match(/^Form$/)) {
40440 //if (this.footer) {
40441 // el = this.footer.container.insertSibling(false, 'before');
40443 el = this.el.createChild();
40446 this.form = new Roo.form.Form(cfg);
40449 if ( this.form.allItems.length) {
40450 this.form.render(el.dom);
40454 // should only have one of theses..
40455 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40456 // views.. should not be just added - used named prop 'view''
40458 cfg.el = this.el.appendChild(document.createElement("div"));
40461 var ret = new Roo.factory(cfg);
40463 ret.render && ret.render(false, ''); // render blank..
40473 * @class Roo.bootstrap.panel.Grid
40474 * @extends Roo.bootstrap.panel.Content
40476 * Create a new GridPanel.
40477 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40478 * @param {Object} config A the config object
40484 Roo.bootstrap.panel.Grid = function(config)
40488 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40489 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40491 config.el = this.wrapper;
40492 //this.el = this.wrapper;
40494 if (config.container) {
40495 // ctor'ed from a Border/panel.grid
40498 this.wrapper.setStyle("overflow", "hidden");
40499 this.wrapper.addClass('roo-grid-container');
40504 if(config.toolbar){
40505 var tool_el = this.wrapper.createChild();
40506 this.toolbar = Roo.factory(config.toolbar);
40508 if (config.toolbar.items) {
40509 ti = config.toolbar.items ;
40510 delete config.toolbar.items ;
40514 this.toolbar.render(tool_el);
40515 for(var i =0;i < ti.length;i++) {
40516 // Roo.log(['add child', items[i]]);
40517 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40519 this.toolbar.items = nitems;
40521 delete config.toolbar;
40524 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40525 config.grid.scrollBody = true;;
40526 config.grid.monitorWindowResize = false; // turn off autosizing
40527 config.grid.autoHeight = false;
40528 config.grid.autoWidth = false;
40530 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40532 if (config.background) {
40533 // render grid on panel activation (if panel background)
40534 this.on('activate', function(gp) {
40535 if (!gp.grid.rendered) {
40536 gp.grid.render(this.wrapper);
40537 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40542 this.grid.render(this.wrapper);
40543 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40546 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40547 // ??? needed ??? config.el = this.wrapper;
40552 // xtype created footer. - not sure if will work as we normally have to render first..
40553 if (this.footer && !this.footer.el && this.footer.xtype) {
40555 var ctr = this.grid.getView().getFooterPanel(true);
40556 this.footer.dataSource = this.grid.dataSource;
40557 this.footer = Roo.factory(this.footer, Roo);
40558 this.footer.render(ctr);
40568 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40569 getId : function(){
40570 return this.grid.id;
40574 * Returns the grid for this panel
40575 * @return {Roo.bootstrap.Table}
40577 getGrid : function(){
40581 setSize : function(width, height){
40582 if(!this.ignoreResize(width, height)){
40583 var grid = this.grid;
40584 var size = this.adjustForComponents(width, height);
40585 // tfoot is not a footer?
40588 var gridel = grid.getGridEl();
40589 gridel.setSize(size.width, size.height);
40591 var tbd = grid.getGridEl().select('tbody', true).first();
40592 var thd = grid.getGridEl().select('thead',true).first();
40593 var tbf= grid.getGridEl().select('tfoot', true).first();
40596 size.height -= tbf.getHeight();
40599 size.height -= thd.getHeight();
40602 tbd.setSize(size.width, size.height );
40603 // this is for the account management tab -seems to work there.
40604 var thd = grid.getGridEl().select('thead',true).first();
40606 // tbd.setSize(size.width, size.height - thd.getHeight());
40615 beforeSlide : function(){
40616 this.grid.getView().scroller.clip();
40619 afterSlide : function(){
40620 this.grid.getView().scroller.unclip();
40623 destroy : function(){
40624 this.grid.destroy();
40626 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40631 * @class Roo.bootstrap.panel.Nest
40632 * @extends Roo.bootstrap.panel.Content
40634 * Create a new Panel, that can contain a layout.Border.
40637 * @param {Roo.BorderLayout} layout The layout for this panel
40638 * @param {String/Object} config A string to set only the title or a config object
40640 Roo.bootstrap.panel.Nest = function(config)
40642 // construct with only one argument..
40643 /* FIXME - implement nicer consturctors
40644 if (layout.layout) {
40646 layout = config.layout;
40647 delete config.layout;
40649 if (layout.xtype && !layout.getEl) {
40650 // then layout needs constructing..
40651 layout = Roo.factory(layout, Roo);
40655 config.el = config.layout.getEl();
40657 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40659 config.layout.monitorWindowResize = false; // turn off autosizing
40660 this.layout = config.layout;
40661 this.layout.getEl().addClass("roo-layout-nested-layout");
40662 this.layout.parent = this;
40669 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40671 setSize : function(width, height){
40672 if(!this.ignoreResize(width, height)){
40673 var size = this.adjustForComponents(width, height);
40674 var el = this.layout.getEl();
40675 if (size.height < 1) {
40676 el.setWidth(size.width);
40678 el.setSize(size.width, size.height);
40680 var touch = el.dom.offsetWidth;
40681 this.layout.layout();
40682 // ie requires a double layout on the first pass
40683 if(Roo.isIE && !this.initialized){
40684 this.initialized = true;
40685 this.layout.layout();
40690 // activate all subpanels if not currently active..
40692 setActiveState : function(active){
40693 this.active = active;
40694 this.setActiveClass(active);
40697 this.fireEvent("deactivate", this);
40701 this.fireEvent("activate", this);
40702 // not sure if this should happen before or after..
40703 if (!this.layout) {
40704 return; // should not happen..
40707 for (var r in this.layout.regions) {
40708 reg = this.layout.getRegion(r);
40709 if (reg.getActivePanel()) {
40710 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40711 reg.setActivePanel(reg.getActivePanel());
40714 if (!reg.panels.length) {
40717 reg.showPanel(reg.getPanel(0));
40726 * Returns the nested BorderLayout for this panel
40727 * @return {Roo.BorderLayout}
40729 getLayout : function(){
40730 return this.layout;
40734 * Adds a xtype elements to the layout of the nested panel
40738 xtype : 'ContentPanel',
40745 xtype : 'NestedLayoutPanel',
40751 items : [ ... list of content panels or nested layout panels.. ]
40755 * @param {Object} cfg Xtype definition of item to add.
40757 addxtype : function(cfg) {
40758 return this.layout.addxtype(cfg);
40763 * Ext JS Library 1.1.1
40764 * Copyright(c) 2006-2007, Ext JS, LLC.
40766 * Originally Released Under LGPL - original licence link has changed is not relivant.
40769 * <script type="text/javascript">
40772 * @class Roo.TabPanel
40773 * @extends Roo.util.Observable
40774 * A lightweight tab container.
40778 // basic tabs 1, built from existing content
40779 var tabs = new Roo.TabPanel("tabs1");
40780 tabs.addTab("script", "View Script");
40781 tabs.addTab("markup", "View Markup");
40782 tabs.activate("script");
40784 // more advanced tabs, built from javascript
40785 var jtabs = new Roo.TabPanel("jtabs");
40786 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40788 // set up the UpdateManager
40789 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40790 var updater = tab2.getUpdateManager();
40791 updater.setDefaultUrl("ajax1.htm");
40792 tab2.on('activate', updater.refresh, updater, true);
40794 // Use setUrl for Ajax loading
40795 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40796 tab3.setUrl("ajax2.htm", null, true);
40799 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40802 jtabs.activate("jtabs-1");
40805 * Create a new TabPanel.
40806 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40807 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40809 Roo.bootstrap.panel.Tabs = function(config){
40811 * The container element for this TabPanel.
40812 * @type Roo.Element
40814 this.el = Roo.get(config.el);
40817 if(typeof config == "boolean"){
40818 this.tabPosition = config ? "bottom" : "top";
40820 Roo.apply(this, config);
40824 if(this.tabPosition == "bottom"){
40825 // if tabs are at the bottom = create the body first.
40826 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40827 this.el.addClass("roo-tabs-bottom");
40829 // next create the tabs holders
40831 if (this.tabPosition == "west"){
40833 var reg = this.region; // fake it..
40835 if (!reg.mgr.parent) {
40838 reg = reg.mgr.parent.region;
40840 Roo.log("got nest?");
40842 if (reg.mgr.getRegion('west')) {
40843 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40844 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40845 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40846 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40847 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40855 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40856 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40857 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40858 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40863 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40866 // finally - if tabs are at the top, then create the body last..
40867 if(this.tabPosition != "bottom"){
40868 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40869 * @type Roo.Element
40871 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40872 this.el.addClass("roo-tabs-top");
40876 this.bodyEl.setStyle("position", "relative");
40878 this.active = null;
40879 this.activateDelegate = this.activate.createDelegate(this);
40884 * Fires when the active tab changes
40885 * @param {Roo.TabPanel} this
40886 * @param {Roo.TabPanelItem} activePanel The new active tab
40890 * @event beforetabchange
40891 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40892 * @param {Roo.TabPanel} this
40893 * @param {Object} e Set cancel to true on this object to cancel the tab change
40894 * @param {Roo.TabPanelItem} tab The tab being changed to
40896 "beforetabchange" : true
40899 Roo.EventManager.onWindowResize(this.onResize, this);
40900 this.cpad = this.el.getPadding("lr");
40901 this.hiddenCount = 0;
40904 // toolbar on the tabbar support...
40905 if (this.toolbar) {
40906 alert("no toolbar support yet");
40907 this.toolbar = false;
40909 var tcfg = this.toolbar;
40910 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40911 this.toolbar = new Roo.Toolbar(tcfg);
40912 if (Roo.isSafari) {
40913 var tbl = tcfg.container.child('table', true);
40914 tbl.setAttribute('width', '100%');
40922 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40925 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40927 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40929 tabPosition : "top",
40931 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40933 currentTabWidth : 0,
40935 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40939 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40943 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40945 preferredTabWidth : 175,
40947 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40949 resizeTabs : false,
40951 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40953 monitorResize : true,
40955 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40957 toolbar : false, // set by caller..
40959 region : false, /// set by caller
40961 disableTooltips : true, // not used yet...
40964 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40965 * @param {String} id The id of the div to use <b>or create</b>
40966 * @param {String} text The text for the tab
40967 * @param {String} content (optional) Content to put in the TabPanelItem body
40968 * @param {Boolean} closable (optional) True to create a close icon on the tab
40969 * @return {Roo.TabPanelItem} The created TabPanelItem
40971 addTab : function(id, text, content, closable, tpl)
40973 var item = new Roo.bootstrap.panel.TabItem({
40977 closable : closable,
40980 this.addTabItem(item);
40982 item.setContent(content);
40988 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40989 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40990 * @return {Roo.TabPanelItem}
40992 getTab : function(id){
40993 return this.items[id];
40997 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40998 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41000 hideTab : function(id){
41001 var t = this.items[id];
41004 this.hiddenCount++;
41005 this.autoSizeTabs();
41010 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41011 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41013 unhideTab : function(id){
41014 var t = this.items[id];
41016 t.setHidden(false);
41017 this.hiddenCount--;
41018 this.autoSizeTabs();
41023 * Adds an existing {@link Roo.TabPanelItem}.
41024 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41026 addTabItem : function(item)
41028 this.items[item.id] = item;
41029 this.items.push(item);
41030 this.autoSizeTabs();
41031 // if(this.resizeTabs){
41032 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41033 // this.autoSizeTabs();
41035 // item.autoSize();
41040 * Removes a {@link Roo.TabPanelItem}.
41041 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41043 removeTab : function(id){
41044 var items = this.items;
41045 var tab = items[id];
41046 if(!tab) { return; }
41047 var index = items.indexOf(tab);
41048 if(this.active == tab && items.length > 1){
41049 var newTab = this.getNextAvailable(index);
41054 this.stripEl.dom.removeChild(tab.pnode.dom);
41055 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41056 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41058 items.splice(index, 1);
41059 delete this.items[tab.id];
41060 tab.fireEvent("close", tab);
41061 tab.purgeListeners();
41062 this.autoSizeTabs();
41065 getNextAvailable : function(start){
41066 var items = this.items;
41068 // look for a next tab that will slide over to
41069 // replace the one being removed
41070 while(index < items.length){
41071 var item = items[++index];
41072 if(item && !item.isHidden()){
41076 // if one isn't found select the previous tab (on the left)
41079 var item = items[--index];
41080 if(item && !item.isHidden()){
41088 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41089 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41091 disableTab : function(id){
41092 var tab = this.items[id];
41093 if(tab && this.active != tab){
41099 * Enables a {@link Roo.TabPanelItem} that is disabled.
41100 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41102 enableTab : function(id){
41103 var tab = this.items[id];
41108 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41109 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41110 * @return {Roo.TabPanelItem} The TabPanelItem.
41112 activate : function(id)
41114 //Roo.log('activite:' + id);
41116 var tab = this.items[id];
41120 if(tab == this.active || tab.disabled){
41124 this.fireEvent("beforetabchange", this, e, tab);
41125 if(e.cancel !== true && !tab.disabled){
41127 this.active.hide();
41129 this.active = this.items[id];
41130 this.active.show();
41131 this.fireEvent("tabchange", this, this.active);
41137 * Gets the active {@link Roo.TabPanelItem}.
41138 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41140 getActiveTab : function(){
41141 return this.active;
41145 * Updates the tab body element to fit the height of the container element
41146 * for overflow scrolling
41147 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41149 syncHeight : function(targetHeight){
41150 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41151 var bm = this.bodyEl.getMargins();
41152 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41153 this.bodyEl.setHeight(newHeight);
41157 onResize : function(){
41158 if(this.monitorResize){
41159 this.autoSizeTabs();
41164 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41166 beginUpdate : function(){
41167 this.updating = true;
41171 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41173 endUpdate : function(){
41174 this.updating = false;
41175 this.autoSizeTabs();
41179 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41181 autoSizeTabs : function()
41183 var count = this.items.length;
41184 var vcount = count - this.hiddenCount;
41187 this.stripEl.hide();
41189 this.stripEl.show();
41192 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41197 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41198 var availWidth = Math.floor(w / vcount);
41199 var b = this.stripBody;
41200 if(b.getWidth() > w){
41201 var tabs = this.items;
41202 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41203 if(availWidth < this.minTabWidth){
41204 /*if(!this.sleft){ // incomplete scrolling code
41205 this.createScrollButtons();
41208 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41211 if(this.currentTabWidth < this.preferredTabWidth){
41212 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41218 * Returns the number of tabs in this TabPanel.
41221 getCount : function(){
41222 return this.items.length;
41226 * Resizes all the tabs to the passed width
41227 * @param {Number} The new width
41229 setTabWidth : function(width){
41230 this.currentTabWidth = width;
41231 for(var i = 0, len = this.items.length; i < len; i++) {
41232 if(!this.items[i].isHidden()) {
41233 this.items[i].setWidth(width);
41239 * Destroys this TabPanel
41240 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41242 destroy : function(removeEl){
41243 Roo.EventManager.removeResizeListener(this.onResize, this);
41244 for(var i = 0, len = this.items.length; i < len; i++){
41245 this.items[i].purgeListeners();
41247 if(removeEl === true){
41248 this.el.update("");
41253 createStrip : function(container)
41255 var strip = document.createElement("nav");
41256 strip.className = Roo.bootstrap.version == 4 ?
41257 "navbar-light bg-light" :
41258 "navbar navbar-default"; //"x-tabs-wrap";
41259 container.appendChild(strip);
41263 createStripList : function(strip)
41265 // div wrapper for retard IE
41266 // returns the "tr" element.
41267 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41268 //'<div class="x-tabs-strip-wrap">'+
41269 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41270 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41271 return strip.firstChild; //.firstChild.firstChild.firstChild;
41273 createBody : function(container)
41275 var body = document.createElement("div");
41276 Roo.id(body, "tab-body");
41277 //Roo.fly(body).addClass("x-tabs-body");
41278 Roo.fly(body).addClass("tab-content");
41279 container.appendChild(body);
41282 createItemBody :function(bodyEl, id){
41283 var body = Roo.getDom(id);
41285 body = document.createElement("div");
41288 //Roo.fly(body).addClass("x-tabs-item-body");
41289 Roo.fly(body).addClass("tab-pane");
41290 bodyEl.insertBefore(body, bodyEl.firstChild);
41294 createStripElements : function(stripEl, text, closable, tpl)
41296 var td = document.createElement("li"); // was td..
41297 td.className = 'nav-item';
41299 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41302 stripEl.appendChild(td);
41304 td.className = "x-tabs-closable";
41305 if(!this.closeTpl){
41306 this.closeTpl = new Roo.Template(
41307 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41308 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41309 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41312 var el = this.closeTpl.overwrite(td, {"text": text});
41313 var close = el.getElementsByTagName("div")[0];
41314 var inner = el.getElementsByTagName("em")[0];
41315 return {"el": el, "close": close, "inner": inner};
41318 // not sure what this is..
41319 // if(!this.tabTpl){
41320 //this.tabTpl = new Roo.Template(
41321 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41322 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41324 // this.tabTpl = new Roo.Template(
41325 // '<a href="#">' +
41326 // '<span unselectable="on"' +
41327 // (this.disableTooltips ? '' : ' title="{text}"') +
41328 // ' >{text}</span></a>'
41334 var template = tpl || this.tabTpl || false;
41337 template = new Roo.Template(
41338 Roo.bootstrap.version == 4 ?
41340 '<a class="nav-link" href="#" unselectable="on"' +
41341 (this.disableTooltips ? '' : ' title="{text}"') +
41344 '<a class="nav-link" href="#">' +
41345 '<span unselectable="on"' +
41346 (this.disableTooltips ? '' : ' title="{text}"') +
41347 ' >{text}</span></a>'
41352 switch (typeof(template)) {
41356 template = new Roo.Template(template);
41362 var el = template.overwrite(td, {"text": text});
41364 var inner = el.getElementsByTagName("span")[0];
41366 return {"el": el, "inner": inner};
41374 * @class Roo.TabPanelItem
41375 * @extends Roo.util.Observable
41376 * Represents an individual item (tab plus body) in a TabPanel.
41377 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41378 * @param {String} id The id of this TabPanelItem
41379 * @param {String} text The text for the tab of this TabPanelItem
41380 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41382 Roo.bootstrap.panel.TabItem = function(config){
41384 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41385 * @type Roo.TabPanel
41387 this.tabPanel = config.panel;
41389 * The id for this TabPanelItem
41392 this.id = config.id;
41394 this.disabled = false;
41396 this.text = config.text;
41398 this.loaded = false;
41399 this.closable = config.closable;
41402 * The body element for this TabPanelItem.
41403 * @type Roo.Element
41405 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41406 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41407 this.bodyEl.setStyle("display", "block");
41408 this.bodyEl.setStyle("zoom", "1");
41409 //this.hideAction();
41411 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41413 this.el = Roo.get(els.el);
41414 this.inner = Roo.get(els.inner, true);
41415 this.textEl = Roo.bootstrap.version == 4 ?
41416 this.el : Roo.get(this.el.dom.firstChild, true);
41418 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41419 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41422 // this.el.on("mousedown", this.onTabMouseDown, this);
41423 this.el.on("click", this.onTabClick, this);
41425 if(config.closable){
41426 var c = Roo.get(els.close, true);
41427 c.dom.title = this.closeText;
41428 c.addClassOnOver("close-over");
41429 c.on("click", this.closeClick, this);
41435 * Fires when this tab becomes the active tab.
41436 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41437 * @param {Roo.TabPanelItem} this
41441 * @event beforeclose
41442 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41443 * @param {Roo.TabPanelItem} this
41444 * @param {Object} e Set cancel to true on this object to cancel the close.
41446 "beforeclose": true,
41449 * Fires when this tab is closed.
41450 * @param {Roo.TabPanelItem} this
41454 * @event deactivate
41455 * Fires when this tab is no longer the active tab.
41456 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41457 * @param {Roo.TabPanelItem} this
41459 "deactivate" : true
41461 this.hidden = false;
41463 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41466 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41468 purgeListeners : function(){
41469 Roo.util.Observable.prototype.purgeListeners.call(this);
41470 this.el.removeAllListeners();
41473 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41476 this.status_node.addClass("active");
41479 this.tabPanel.stripWrap.repaint();
41481 this.fireEvent("activate", this.tabPanel, this);
41485 * Returns true if this tab is the active tab.
41486 * @return {Boolean}
41488 isActive : function(){
41489 return this.tabPanel.getActiveTab() == this;
41493 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41496 this.status_node.removeClass("active");
41498 this.fireEvent("deactivate", this.tabPanel, this);
41501 hideAction : function(){
41502 this.bodyEl.hide();
41503 this.bodyEl.setStyle("position", "absolute");
41504 this.bodyEl.setLeft("-20000px");
41505 this.bodyEl.setTop("-20000px");
41508 showAction : function(){
41509 this.bodyEl.setStyle("position", "relative");
41510 this.bodyEl.setTop("");
41511 this.bodyEl.setLeft("");
41512 this.bodyEl.show();
41516 * Set the tooltip for the tab.
41517 * @param {String} tooltip The tab's tooltip
41519 setTooltip : function(text){
41520 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41521 this.textEl.dom.qtip = text;
41522 this.textEl.dom.removeAttribute('title');
41524 this.textEl.dom.title = text;
41528 onTabClick : function(e){
41529 e.preventDefault();
41530 this.tabPanel.activate(this.id);
41533 onTabMouseDown : function(e){
41534 e.preventDefault();
41535 this.tabPanel.activate(this.id);
41538 getWidth : function(){
41539 return this.inner.getWidth();
41542 setWidth : function(width){
41543 var iwidth = width - this.linode.getPadding("lr");
41544 this.inner.setWidth(iwidth);
41545 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41546 this.linode.setWidth(width);
41550 * Show or hide the tab
41551 * @param {Boolean} hidden True to hide or false to show.
41553 setHidden : function(hidden){
41554 this.hidden = hidden;
41555 this.linode.setStyle("display", hidden ? "none" : "");
41559 * Returns true if this tab is "hidden"
41560 * @return {Boolean}
41562 isHidden : function(){
41563 return this.hidden;
41567 * Returns the text for this tab
41570 getText : function(){
41574 autoSize : function(){
41575 //this.el.beginMeasure();
41576 this.textEl.setWidth(1);
41578 * #2804 [new] Tabs in Roojs
41579 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41581 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41582 //this.el.endMeasure();
41586 * Sets the text for the tab (Note: this also sets the tooltip text)
41587 * @param {String} text The tab's text and tooltip
41589 setText : function(text){
41591 this.textEl.update(text);
41592 this.setTooltip(text);
41593 //if(!this.tabPanel.resizeTabs){
41594 // this.autoSize();
41598 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41600 activate : function(){
41601 this.tabPanel.activate(this.id);
41605 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41607 disable : function(){
41608 if(this.tabPanel.active != this){
41609 this.disabled = true;
41610 this.status_node.addClass("disabled");
41615 * Enables this TabPanelItem if it was previously disabled.
41617 enable : function(){
41618 this.disabled = false;
41619 this.status_node.removeClass("disabled");
41623 * Sets the content for this TabPanelItem.
41624 * @param {String} content The content
41625 * @param {Boolean} loadScripts true to look for and load scripts
41627 setContent : function(content, loadScripts){
41628 this.bodyEl.update(content, loadScripts);
41632 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41633 * @return {Roo.UpdateManager} The UpdateManager
41635 getUpdateManager : function(){
41636 return this.bodyEl.getUpdateManager();
41640 * Set a URL to be used to load the content for this TabPanelItem.
41641 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41642 * @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)
41643 * @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)
41644 * @return {Roo.UpdateManager} The UpdateManager
41646 setUrl : function(url, params, loadOnce){
41647 if(this.refreshDelegate){
41648 this.un('activate', this.refreshDelegate);
41650 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41651 this.on("activate", this.refreshDelegate);
41652 return this.bodyEl.getUpdateManager();
41656 _handleRefresh : function(url, params, loadOnce){
41657 if(!loadOnce || !this.loaded){
41658 var updater = this.bodyEl.getUpdateManager();
41659 updater.update(url, params, this._setLoaded.createDelegate(this));
41664 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41665 * Will fail silently if the setUrl method has not been called.
41666 * This does not activate the panel, just updates its content.
41668 refresh : function(){
41669 if(this.refreshDelegate){
41670 this.loaded = false;
41671 this.refreshDelegate();
41676 _setLoaded : function(){
41677 this.loaded = true;
41681 closeClick : function(e){
41684 this.fireEvent("beforeclose", this, o);
41685 if(o.cancel !== true){
41686 this.tabPanel.removeTab(this.id);
41690 * The text displayed in the tooltip for the close icon.
41693 closeText : "Close this tab"
41696 * This script refer to:
41697 * Title: International Telephone Input
41698 * Author: Jack O'Connor
41699 * Code version: v12.1.12
41700 * Availability: https://github.com/jackocnr/intl-tel-input.git
41703 Roo.bootstrap.PhoneInputData = function() {
41706 "Afghanistan (افغانستان)",
41711 "Albania (Shqipëri)",
41716 "Algeria (الجزائر)",
41741 "Antigua and Barbuda",
41751 "Armenia (Հայաստան)",
41767 "Austria (Österreich)",
41772 "Azerbaijan (Azərbaycan)",
41782 "Bahrain (البحرين)",
41787 "Bangladesh (বাংলাদেশ)",
41797 "Belarus (Беларусь)",
41802 "Belgium (België)",
41832 "Bosnia and Herzegovina (Босна и Херцеговина)",
41847 "British Indian Ocean Territory",
41852 "British Virgin Islands",
41862 "Bulgaria (България)",
41872 "Burundi (Uburundi)",
41877 "Cambodia (កម្ពុជា)",
41882 "Cameroon (Cameroun)",
41891 ["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"]
41894 "Cape Verde (Kabu Verdi)",
41899 "Caribbean Netherlands",
41910 "Central African Republic (République centrafricaine)",
41930 "Christmas Island",
41936 "Cocos (Keeling) Islands",
41947 "Comoros (جزر القمر)",
41952 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41957 "Congo (Republic) (Congo-Brazzaville)",
41977 "Croatia (Hrvatska)",
41998 "Czech Republic (Česká republika)",
42003 "Denmark (Danmark)",
42018 "Dominican Republic (República Dominicana)",
42022 ["809", "829", "849"]
42040 "Equatorial Guinea (Guinea Ecuatorial)",
42060 "Falkland Islands (Islas Malvinas)",
42065 "Faroe Islands (Føroyar)",
42086 "French Guiana (Guyane française)",
42091 "French Polynesia (Polynésie française)",
42106 "Georgia (საქართველო)",
42111 "Germany (Deutschland)",
42131 "Greenland (Kalaallit Nunaat)",
42168 "Guinea-Bissau (Guiné Bissau)",
42193 "Hungary (Magyarország)",
42198 "Iceland (Ísland)",
42218 "Iraq (العراق)",
42234 "Israel (ישראל)",
42261 "Jordan (الأردن)",
42266 "Kazakhstan (Казахстан)",
42287 "Kuwait (الكويت)",
42292 "Kyrgyzstan (Кыргызстан)",
42302 "Latvia (Latvija)",
42307 "Lebanon (لبنان)",
42322 "Libya (ليبيا)",
42332 "Lithuania (Lietuva)",
42347 "Macedonia (FYROM) (Македонија)",
42352 "Madagascar (Madagasikara)",
42382 "Marshall Islands",
42392 "Mauritania (موريتانيا)",
42397 "Mauritius (Moris)",
42418 "Moldova (Republica Moldova)",
42428 "Mongolia (Монгол)",
42433 "Montenegro (Crna Gora)",
42443 "Morocco (المغرب)",
42449 "Mozambique (Moçambique)",
42454 "Myanmar (Burma) (မြန်မာ)",
42459 "Namibia (Namibië)",
42474 "Netherlands (Nederland)",
42479 "New Caledonia (Nouvelle-Calédonie)",
42514 "North Korea (조선 민주주의 인민 공화국)",
42519 "Northern Mariana Islands",
42535 "Pakistan (پاکستان)",
42545 "Palestine (فلسطين)",
42555 "Papua New Guinea",
42597 "Réunion (La Réunion)",
42603 "Romania (România)",
42619 "Saint Barthélemy",
42630 "Saint Kitts and Nevis",
42640 "Saint Martin (Saint-Martin (partie française))",
42646 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42651 "Saint Vincent and the Grenadines",
42666 "São Tomé and Príncipe (São Tomé e Príncipe)",
42671 "Saudi Arabia (المملكة العربية السعودية)",
42676 "Senegal (Sénégal)",
42706 "Slovakia (Slovensko)",
42711 "Slovenia (Slovenija)",
42721 "Somalia (Soomaaliya)",
42731 "South Korea (대한민국)",
42736 "South Sudan (جنوب السودان)",
42746 "Sri Lanka (ශ්රී ලංකාව)",
42751 "Sudan (السودان)",
42761 "Svalbard and Jan Mayen",
42772 "Sweden (Sverige)",
42777 "Switzerland (Schweiz)",
42782 "Syria (سوريا)",
42827 "Trinidad and Tobago",
42832 "Tunisia (تونس)",
42837 "Turkey (Türkiye)",
42847 "Turks and Caicos Islands",
42857 "U.S. Virgin Islands",
42867 "Ukraine (Україна)",
42872 "United Arab Emirates (الإمارات العربية المتحدة)",
42894 "Uzbekistan (Oʻzbekiston)",
42904 "Vatican City (Città del Vaticano)",
42915 "Vietnam (Việt Nam)",
42920 "Wallis and Futuna (Wallis-et-Futuna)",
42925 "Western Sahara (الصحراء الغربية)",
42931 "Yemen (اليمن)",
42955 * This script refer to:
42956 * Title: International Telephone Input
42957 * Author: Jack O'Connor
42958 * Code version: v12.1.12
42959 * Availability: https://github.com/jackocnr/intl-tel-input.git
42963 * @class Roo.bootstrap.PhoneInput
42964 * @extends Roo.bootstrap.TriggerField
42965 * An input with International dial-code selection
42967 * @cfg {String} defaultDialCode default '+852'
42968 * @cfg {Array} preferedCountries default []
42971 * Create a new PhoneInput.
42972 * @param {Object} config Configuration options
42975 Roo.bootstrap.PhoneInput = function(config) {
42976 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42979 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42981 listWidth: undefined,
42983 selectedClass: 'active',
42985 invalidClass : "has-warning",
42987 validClass: 'has-success',
42989 allowed: '0123456789',
42994 * @cfg {String} defaultDialCode The default dial code when initializing the input
42996 defaultDialCode: '+852',
42999 * @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
43001 preferedCountries: false,
43003 getAutoCreate : function()
43005 var data = Roo.bootstrap.PhoneInputData();
43006 var align = this.labelAlign || this.parentLabelAlign();
43009 this.allCountries = [];
43010 this.dialCodeMapping = [];
43012 for (var i = 0; i < data.length; i++) {
43014 this.allCountries[i] = {
43018 priority: c[3] || 0,
43019 areaCodes: c[4] || null
43021 this.dialCodeMapping[c[2]] = {
43024 priority: c[3] || 0,
43025 areaCodes: c[4] || null
43037 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43038 maxlength: this.max_length,
43039 cls : 'form-control tel-input',
43040 autocomplete: 'new-password'
43043 var hiddenInput = {
43046 cls: 'hidden-tel-input'
43050 hiddenInput.name = this.name;
43053 if (this.disabled) {
43054 input.disabled = true;
43057 var flag_container = {
43074 cls: this.hasFeedback ? 'has-feedback' : '',
43080 cls: 'dial-code-holder',
43087 cls: 'roo-select2-container input-group',
43094 if (this.fieldLabel.length) {
43097 tooltip: 'This field is required'
43103 cls: 'control-label',
43109 html: this.fieldLabel
43112 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43118 if(this.indicatorpos == 'right') {
43119 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43126 if(align == 'left') {
43134 if(this.labelWidth > 12){
43135 label.style = "width: " + this.labelWidth + 'px';
43137 if(this.labelWidth < 13 && this.labelmd == 0){
43138 this.labelmd = this.labelWidth;
43140 if(this.labellg > 0){
43141 label.cls += ' col-lg-' + this.labellg;
43142 input.cls += ' col-lg-' + (12 - this.labellg);
43144 if(this.labelmd > 0){
43145 label.cls += ' col-md-' + this.labelmd;
43146 container.cls += ' col-md-' + (12 - this.labelmd);
43148 if(this.labelsm > 0){
43149 label.cls += ' col-sm-' + this.labelsm;
43150 container.cls += ' col-sm-' + (12 - this.labelsm);
43152 if(this.labelxs > 0){
43153 label.cls += ' col-xs-' + this.labelxs;
43154 container.cls += ' col-xs-' + (12 - this.labelxs);
43164 var settings = this;
43166 ['xs','sm','md','lg'].map(function(size){
43167 if (settings[size]) {
43168 cfg.cls += ' col-' + size + '-' + settings[size];
43172 this.store = new Roo.data.Store({
43173 proxy : new Roo.data.MemoryProxy({}),
43174 reader : new Roo.data.JsonReader({
43185 'name' : 'dialCode',
43189 'name' : 'priority',
43193 'name' : 'areaCodes',
43200 if(!this.preferedCountries) {
43201 this.preferedCountries = [
43208 var p = this.preferedCountries.reverse();
43211 for (var i = 0; i < p.length; i++) {
43212 for (var j = 0; j < this.allCountries.length; j++) {
43213 if(this.allCountries[j].iso2 == p[i]) {
43214 var t = this.allCountries[j];
43215 this.allCountries.splice(j,1);
43216 this.allCountries.unshift(t);
43222 this.store.proxy.data = {
43224 data: this.allCountries
43230 initEvents : function()
43233 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43235 this.indicator = this.indicatorEl();
43236 this.flag = this.flagEl();
43237 this.dialCodeHolder = this.dialCodeHolderEl();
43239 this.trigger = this.el.select('div.flag-box',true).first();
43240 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43245 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43246 _this.list.setWidth(lw);
43249 this.list.on('mouseover', this.onViewOver, this);
43250 this.list.on('mousemove', this.onViewMove, this);
43251 this.inputEl().on("keyup", this.onKeyUp, this);
43252 this.inputEl().on("keypress", this.onKeyPress, this);
43254 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43256 this.view = new Roo.View(this.list, this.tpl, {
43257 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43260 this.view.on('click', this.onViewClick, this);
43261 this.setValue(this.defaultDialCode);
43264 onTriggerClick : function(e)
43266 Roo.log('trigger click');
43271 if(this.isExpanded()){
43273 this.hasFocus = false;
43275 this.store.load({});
43276 this.hasFocus = true;
43281 isExpanded : function()
43283 return this.list.isVisible();
43286 collapse : function()
43288 if(!this.isExpanded()){
43292 Roo.get(document).un('mousedown', this.collapseIf, this);
43293 Roo.get(document).un('mousewheel', this.collapseIf, this);
43294 this.fireEvent('collapse', this);
43298 expand : function()
43302 if(this.isExpanded() || !this.hasFocus){
43306 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43307 this.list.setWidth(lw);
43310 this.restrictHeight();
43312 Roo.get(document).on('mousedown', this.collapseIf, this);
43313 Roo.get(document).on('mousewheel', this.collapseIf, this);
43315 this.fireEvent('expand', this);
43318 restrictHeight : function()
43320 this.list.alignTo(this.inputEl(), this.listAlign);
43321 this.list.alignTo(this.inputEl(), this.listAlign);
43324 onViewOver : function(e, t)
43326 if(this.inKeyMode){
43329 var item = this.view.findItemFromChild(t);
43332 var index = this.view.indexOf(item);
43333 this.select(index, false);
43338 onViewClick : function(view, doFocus, el, e)
43340 var index = this.view.getSelectedIndexes()[0];
43342 var r = this.store.getAt(index);
43345 this.onSelect(r, index);
43347 if(doFocus !== false && !this.blockFocus){
43348 this.inputEl().focus();
43352 onViewMove : function(e, t)
43354 this.inKeyMode = false;
43357 select : function(index, scrollIntoView)
43359 this.selectedIndex = index;
43360 this.view.select(index);
43361 if(scrollIntoView !== false){
43362 var el = this.view.getNode(index);
43364 this.list.scrollChildIntoView(el, false);
43369 createList : function()
43371 this.list = Roo.get(document.body).createChild({
43373 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43374 style: 'display:none'
43377 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43380 collapseIf : function(e)
43382 var in_combo = e.within(this.el);
43383 var in_list = e.within(this.list);
43384 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43386 if (in_combo || in_list || is_list) {
43392 onSelect : function(record, index)
43394 if(this.fireEvent('beforeselect', this, record, index) !== false){
43396 this.setFlagClass(record.data.iso2);
43397 this.setDialCode(record.data.dialCode);
43398 this.hasFocus = false;
43400 this.fireEvent('select', this, record, index);
43404 flagEl : function()
43406 var flag = this.el.select('div.flag',true).first();
43413 dialCodeHolderEl : function()
43415 var d = this.el.select('input.dial-code-holder',true).first();
43422 setDialCode : function(v)
43424 this.dialCodeHolder.dom.value = '+'+v;
43427 setFlagClass : function(n)
43429 this.flag.dom.className = 'flag '+n;
43432 getValue : function()
43434 var v = this.inputEl().getValue();
43435 if(this.dialCodeHolder) {
43436 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43441 setValue : function(v)
43443 var d = this.getDialCode(v);
43445 //invalid dial code
43446 if(v.length == 0 || !d || d.length == 0) {
43448 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43449 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43455 this.setFlagClass(this.dialCodeMapping[d].iso2);
43456 this.setDialCode(d);
43457 this.inputEl().dom.value = v.replace('+'+d,'');
43458 this.hiddenEl().dom.value = this.getValue();
43463 getDialCode : function(v)
43467 if (v.length == 0) {
43468 return this.dialCodeHolder.dom.value;
43472 if (v.charAt(0) != "+") {
43475 var numericChars = "";
43476 for (var i = 1; i < v.length; i++) {
43477 var c = v.charAt(i);
43480 if (this.dialCodeMapping[numericChars]) {
43481 dialCode = v.substr(1, i);
43483 if (numericChars.length == 4) {
43493 this.setValue(this.defaultDialCode);
43497 hiddenEl : function()
43499 return this.el.select('input.hidden-tel-input',true).first();
43502 // after setting val
43503 onKeyUp : function(e){
43504 this.setValue(this.getValue());
43507 onKeyPress : function(e){
43508 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43515 * @class Roo.bootstrap.MoneyField
43516 * @extends Roo.bootstrap.ComboBox
43517 * Bootstrap MoneyField class
43520 * Create a new MoneyField.
43521 * @param {Object} config Configuration options
43524 Roo.bootstrap.MoneyField = function(config) {
43526 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43530 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43533 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43535 allowDecimals : true,
43537 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43539 decimalSeparator : ".",
43541 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43543 decimalPrecision : 0,
43545 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43547 allowNegative : true,
43549 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43553 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43555 minValue : Number.NEGATIVE_INFINITY,
43557 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43559 maxValue : Number.MAX_VALUE,
43561 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43563 minText : "The minimum value for this field is {0}",
43565 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43567 maxText : "The maximum value for this field is {0}",
43569 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43570 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43572 nanText : "{0} is not a valid number",
43574 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43578 * @cfg {String} defaults currency of the MoneyField
43579 * value should be in lkey
43581 defaultCurrency : false,
43583 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43585 thousandsDelimiter : false,
43587 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43598 getAutoCreate : function()
43600 var align = this.labelAlign || this.parentLabelAlign();
43612 cls : 'form-control roo-money-amount-input',
43613 autocomplete: 'new-password'
43616 var hiddenInput = {
43620 cls: 'hidden-number-input'
43623 if(this.max_length) {
43624 input.maxlength = this.max_length;
43628 hiddenInput.name = this.name;
43631 if (this.disabled) {
43632 input.disabled = true;
43635 var clg = 12 - this.inputlg;
43636 var cmd = 12 - this.inputmd;
43637 var csm = 12 - this.inputsm;
43638 var cxs = 12 - this.inputxs;
43642 cls : 'row roo-money-field',
43646 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43650 cls: 'roo-select2-container input-group',
43654 cls : 'form-control roo-money-currency-input',
43655 autocomplete: 'new-password',
43657 name : this.currencyName
43661 cls : 'input-group-addon',
43675 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43679 cls: this.hasFeedback ? 'has-feedback' : '',
43690 if (this.fieldLabel.length) {
43693 tooltip: 'This field is required'
43699 cls: 'control-label',
43705 html: this.fieldLabel
43708 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43714 if(this.indicatorpos == 'right') {
43715 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43722 if(align == 'left') {
43730 if(this.labelWidth > 12){
43731 label.style = "width: " + this.labelWidth + 'px';
43733 if(this.labelWidth < 13 && this.labelmd == 0){
43734 this.labelmd = this.labelWidth;
43736 if(this.labellg > 0){
43737 label.cls += ' col-lg-' + this.labellg;
43738 input.cls += ' col-lg-' + (12 - this.labellg);
43740 if(this.labelmd > 0){
43741 label.cls += ' col-md-' + this.labelmd;
43742 container.cls += ' col-md-' + (12 - this.labelmd);
43744 if(this.labelsm > 0){
43745 label.cls += ' col-sm-' + this.labelsm;
43746 container.cls += ' col-sm-' + (12 - this.labelsm);
43748 if(this.labelxs > 0){
43749 label.cls += ' col-xs-' + this.labelxs;
43750 container.cls += ' col-xs-' + (12 - this.labelxs);
43761 var settings = this;
43763 ['xs','sm','md','lg'].map(function(size){
43764 if (settings[size]) {
43765 cfg.cls += ' col-' + size + '-' + settings[size];
43772 initEvents : function()
43774 this.indicator = this.indicatorEl();
43776 this.initCurrencyEvent();
43778 this.initNumberEvent();
43781 initCurrencyEvent : function()
43784 throw "can not find store for combo";
43787 this.store = Roo.factory(this.store, Roo.data);
43788 this.store.parent = this;
43792 this.triggerEl = this.el.select('.input-group-addon', true).first();
43794 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43799 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43800 _this.list.setWidth(lw);
43803 this.list.on('mouseover', this.onViewOver, this);
43804 this.list.on('mousemove', this.onViewMove, this);
43805 this.list.on('scroll', this.onViewScroll, this);
43808 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43811 this.view = new Roo.View(this.list, this.tpl, {
43812 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43815 this.view.on('click', this.onViewClick, this);
43817 this.store.on('beforeload', this.onBeforeLoad, this);
43818 this.store.on('load', this.onLoad, this);
43819 this.store.on('loadexception', this.onLoadException, this);
43821 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43822 "up" : function(e){
43823 this.inKeyMode = true;
43827 "down" : function(e){
43828 if(!this.isExpanded()){
43829 this.onTriggerClick();
43831 this.inKeyMode = true;
43836 "enter" : function(e){
43839 if(this.fireEvent("specialkey", this, e)){
43840 this.onViewClick(false);
43846 "esc" : function(e){
43850 "tab" : function(e){
43853 if(this.fireEvent("specialkey", this, e)){
43854 this.onViewClick(false);
43862 doRelay : function(foo, bar, hname){
43863 if(hname == 'down' || this.scope.isExpanded()){
43864 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43872 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43876 initNumberEvent : function(e)
43878 this.inputEl().on("keydown" , this.fireKey, this);
43879 this.inputEl().on("focus", this.onFocus, this);
43880 this.inputEl().on("blur", this.onBlur, this);
43882 this.inputEl().relayEvent('keyup', this);
43884 if(this.indicator){
43885 this.indicator.addClass('invisible');
43888 this.originalValue = this.getValue();
43890 if(this.validationEvent == 'keyup'){
43891 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43892 this.inputEl().on('keyup', this.filterValidation, this);
43894 else if(this.validationEvent !== false){
43895 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43898 if(this.selectOnFocus){
43899 this.on("focus", this.preFocus, this);
43902 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43903 this.inputEl().on("keypress", this.filterKeys, this);
43905 this.inputEl().relayEvent('keypress', this);
43908 var allowed = "0123456789";
43910 if(this.allowDecimals){
43911 allowed += this.decimalSeparator;
43914 if(this.allowNegative){
43918 if(this.thousandsDelimiter) {
43922 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43924 var keyPress = function(e){
43926 var k = e.getKey();
43928 var c = e.getCharCode();
43931 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43932 allowed.indexOf(String.fromCharCode(c)) === -1
43938 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43942 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43947 this.inputEl().on("keypress", keyPress, this);
43951 onTriggerClick : function(e)
43958 this.loadNext = false;
43960 if(this.isExpanded()){
43965 this.hasFocus = true;
43967 if(this.triggerAction == 'all') {
43968 this.doQuery(this.allQuery, true);
43972 this.doQuery(this.getRawValue());
43975 getCurrency : function()
43977 var v = this.currencyEl().getValue();
43982 restrictHeight : function()
43984 this.list.alignTo(this.currencyEl(), this.listAlign);
43985 this.list.alignTo(this.currencyEl(), this.listAlign);
43988 onViewClick : function(view, doFocus, el, e)
43990 var index = this.view.getSelectedIndexes()[0];
43992 var r = this.store.getAt(index);
43995 this.onSelect(r, index);
43999 onSelect : function(record, index){
44001 if(this.fireEvent('beforeselect', this, record, index) !== false){
44003 this.setFromCurrencyData(index > -1 ? record.data : false);
44007 this.fireEvent('select', this, record, index);
44011 setFromCurrencyData : function(o)
44015 this.lastCurrency = o;
44017 if (this.currencyField) {
44018 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44020 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44023 this.lastSelectionText = currency;
44025 //setting default currency
44026 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44027 this.setCurrency(this.defaultCurrency);
44031 this.setCurrency(currency);
44034 setFromData : function(o)
44038 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44040 this.setFromCurrencyData(c);
44045 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44047 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44050 this.setValue(value);
44054 setCurrency : function(v)
44056 this.currencyValue = v;
44059 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44064 setValue : function(v)
44066 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44072 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44074 this.inputEl().dom.value = (v == '') ? '' :
44075 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44077 if(!this.allowZero && v === '0') {
44078 this.hiddenEl().dom.value = '';
44079 this.inputEl().dom.value = '';
44086 getRawValue : function()
44088 var v = this.inputEl().getValue();
44093 getValue : function()
44095 return this.fixPrecision(this.parseValue(this.getRawValue()));
44098 parseValue : function(value)
44100 if(this.thousandsDelimiter) {
44102 r = new RegExp(",", "g");
44103 value = value.replace(r, "");
44106 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44107 return isNaN(value) ? '' : value;
44111 fixPrecision : function(value)
44113 if(this.thousandsDelimiter) {
44115 r = new RegExp(",", "g");
44116 value = value.replace(r, "");
44119 var nan = isNaN(value);
44121 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44122 return nan ? '' : value;
44124 return parseFloat(value).toFixed(this.decimalPrecision);
44127 decimalPrecisionFcn : function(v)
44129 return Math.floor(v);
44132 validateValue : function(value)
44134 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44138 var num = this.parseValue(value);
44141 this.markInvalid(String.format(this.nanText, value));
44145 if(num < this.minValue){
44146 this.markInvalid(String.format(this.minText, this.minValue));
44150 if(num > this.maxValue){
44151 this.markInvalid(String.format(this.maxText, this.maxValue));
44158 validate : function()
44160 if(this.disabled || this.allowBlank){
44165 var currency = this.getCurrency();
44167 if(this.validateValue(this.getRawValue()) && currency.length){
44172 this.markInvalid();
44176 getName: function()
44181 beforeBlur : function()
44187 var v = this.parseValue(this.getRawValue());
44194 onBlur : function()
44198 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44199 //this.el.removeClass(this.focusClass);
44202 this.hasFocus = false;
44204 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44208 var v = this.getValue();
44210 if(String(v) !== String(this.startValue)){
44211 this.fireEvent('change', this, v, this.startValue);
44214 this.fireEvent("blur", this);
44217 inputEl : function()
44219 return this.el.select('.roo-money-amount-input', true).first();
44222 currencyEl : function()
44224 return this.el.select('.roo-money-currency-input', true).first();
44227 hiddenEl : function()
44229 return this.el.select('input.hidden-number-input',true).first();
44233 * @class Roo.bootstrap.BezierSignature
44234 * @extends Roo.bootstrap.Component
44235 * Bootstrap BezierSignature class
44236 * This script refer to:
44237 * Title: Signature Pad
44239 * Availability: https://github.com/szimek/signature_pad
44242 * Create a new BezierSignature
44243 * @param {Object} config The config object
44246 Roo.bootstrap.BezierSignature = function(config){
44247 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44253 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44260 mouse_btn_down: true,
44263 * @cfg {int} canvas height
44265 canvas_height: '200px',
44268 * @cfg {float|function} Radius of a single dot.
44273 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44278 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44283 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44288 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44293 * @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.
44295 bg_color: 'rgba(0, 0, 0, 0)',
44298 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44300 dot_color: 'black',
44303 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44305 velocity_filter_weight: 0.7,
44308 * @cfg {function} Callback when stroke begin.
44313 * @cfg {function} Callback when stroke end.
44317 getAutoCreate : function()
44319 var cls = 'roo-signature column';
44322 cls += ' ' + this.cls;
44332 for(var i = 0; i < col_sizes.length; i++) {
44333 if(this[col_sizes[i]]) {
44334 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44344 cls: 'roo-signature-body',
44348 cls: 'roo-signature-body-canvas',
44349 height: this.canvas_height,
44350 width: this.canvas_width
44357 style: 'display: none'
44365 initEvents: function()
44367 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44369 var canvas = this.canvasEl();
44371 // mouse && touch event swapping...
44372 canvas.dom.style.touchAction = 'none';
44373 canvas.dom.style.msTouchAction = 'none';
44375 this.mouse_btn_down = false;
44376 canvas.on('mousedown', this._handleMouseDown, this);
44377 canvas.on('mousemove', this._handleMouseMove, this);
44378 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44380 if (window.PointerEvent) {
44381 canvas.on('pointerdown', this._handleMouseDown, this);
44382 canvas.on('pointermove', this._handleMouseMove, this);
44383 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44386 if ('ontouchstart' in window) {
44387 canvas.on('touchstart', this._handleTouchStart, this);
44388 canvas.on('touchmove', this._handleTouchMove, this);
44389 canvas.on('touchend', this._handleTouchEnd, this);
44392 Roo.EventManager.onWindowResize(this.resize, this, true);
44394 // file input event
44395 this.fileEl().on('change', this.uploadImage, this);
44402 resize: function(){
44404 var canvas = this.canvasEl().dom;
44405 var ctx = this.canvasElCtx();
44406 var img_data = false;
44408 if(canvas.width > 0) {
44409 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44411 // setting canvas width will clean img data
44414 var style = window.getComputedStyle ?
44415 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44417 var padding_left = parseInt(style.paddingLeft) || 0;
44418 var padding_right = parseInt(style.paddingRight) || 0;
44420 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44423 ctx.putImageData(img_data, 0, 0);
44427 _handleMouseDown: function(e)
44429 if (e.browserEvent.which === 1) {
44430 this.mouse_btn_down = true;
44431 this.strokeBegin(e);
44435 _handleMouseMove: function (e)
44437 if (this.mouse_btn_down) {
44438 this.strokeMoveUpdate(e);
44442 _handleMouseUp: function (e)
44444 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44445 this.mouse_btn_down = false;
44450 _handleTouchStart: function (e) {
44452 e.preventDefault();
44453 if (e.browserEvent.targetTouches.length === 1) {
44454 // var touch = e.browserEvent.changedTouches[0];
44455 // this.strokeBegin(touch);
44457 this.strokeBegin(e); // assume e catching the correct xy...
44461 _handleTouchMove: function (e) {
44462 e.preventDefault();
44463 // var touch = event.targetTouches[0];
44464 // _this._strokeMoveUpdate(touch);
44465 this.strokeMoveUpdate(e);
44468 _handleTouchEnd: function (e) {
44469 var wasCanvasTouched = e.target === this.canvasEl().dom;
44470 if (wasCanvasTouched) {
44471 e.preventDefault();
44472 // var touch = event.changedTouches[0];
44473 // _this._strokeEnd(touch);
44478 reset: function () {
44479 this._lastPoints = [];
44480 this._lastVelocity = 0;
44481 this._lastWidth = (this.min_width + this.max_width) / 2;
44482 this.canvasElCtx().fillStyle = this.dot_color;
44485 strokeMoveUpdate: function(e)
44487 this.strokeUpdate(e);
44489 if (this.throttle) {
44490 this.throttleStroke(this.strokeUpdate, this.throttle);
44493 this.strokeUpdate(e);
44497 strokeBegin: function(e)
44499 var newPointGroup = {
44500 color: this.dot_color,
44504 if (typeof this.onBegin === 'function') {
44508 this.curve_data.push(newPointGroup);
44510 this.strokeUpdate(e);
44513 strokeUpdate: function(e)
44515 var rect = this.canvasEl().dom.getBoundingClientRect();
44516 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44517 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44518 var lastPoints = lastPointGroup.points;
44519 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44520 var isLastPointTooClose = lastPoint
44521 ? point.distanceTo(lastPoint) <= this.min_distance
44523 var color = lastPointGroup.color;
44524 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44525 var curve = this.addPoint(point);
44527 this.drawDot({color: color, point: point});
44530 this.drawCurve({color: color, curve: curve});
44540 strokeEnd: function(e)
44542 this.strokeUpdate(e);
44543 if (typeof this.onEnd === 'function') {
44548 addPoint: function (point) {
44549 var _lastPoints = this._lastPoints;
44550 _lastPoints.push(point);
44551 if (_lastPoints.length > 2) {
44552 if (_lastPoints.length === 3) {
44553 _lastPoints.unshift(_lastPoints[0]);
44555 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44556 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44557 _lastPoints.shift();
44563 calculateCurveWidths: function (startPoint, endPoint) {
44564 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44565 (1 - this.velocity_filter_weight) * this._lastVelocity;
44567 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44570 start: this._lastWidth
44573 this._lastVelocity = velocity;
44574 this._lastWidth = newWidth;
44578 drawDot: function (_a) {
44579 var color = _a.color, point = _a.point;
44580 var ctx = this.canvasElCtx();
44581 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44583 this.drawCurveSegment(point.x, point.y, width);
44585 ctx.fillStyle = color;
44589 drawCurve: function (_a) {
44590 var color = _a.color, curve = _a.curve;
44591 var ctx = this.canvasElCtx();
44592 var widthDelta = curve.endWidth - curve.startWidth;
44593 var drawSteps = Math.floor(curve.length()) * 2;
44595 ctx.fillStyle = color;
44596 for (var i = 0; i < drawSteps; i += 1) {
44597 var t = i / drawSteps;
44603 var x = uuu * curve.startPoint.x;
44604 x += 3 * uu * t * curve.control1.x;
44605 x += 3 * u * tt * curve.control2.x;
44606 x += ttt * curve.endPoint.x;
44607 var y = uuu * curve.startPoint.y;
44608 y += 3 * uu * t * curve.control1.y;
44609 y += 3 * u * tt * curve.control2.y;
44610 y += ttt * curve.endPoint.y;
44611 var width = curve.startWidth + ttt * widthDelta;
44612 this.drawCurveSegment(x, y, width);
44618 drawCurveSegment: function (x, y, width) {
44619 var ctx = this.canvasElCtx();
44621 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44622 this.is_empty = false;
44627 var ctx = this.canvasElCtx();
44628 var canvas = this.canvasEl().dom;
44629 ctx.fillStyle = this.bg_color;
44630 ctx.clearRect(0, 0, canvas.width, canvas.height);
44631 ctx.fillRect(0, 0, canvas.width, canvas.height);
44632 this.curve_data = [];
44634 this.is_empty = true;
44639 return this.el.select('input',true).first();
44642 canvasEl: function()
44644 return this.el.select('canvas',true).first();
44647 canvasElCtx: function()
44649 return this.el.select('canvas',true).first().dom.getContext('2d');
44652 getImage: function(type)
44654 if(this.is_empty) {
44659 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44662 drawFromImage: function(img_src)
44664 var img = new Image();
44666 img.onload = function(){
44667 this.canvasElCtx().drawImage(img, 0, 0);
44672 this.is_empty = false;
44675 selectImage: function()
44677 this.fileEl().dom.click();
44680 uploadImage: function(e)
44682 var reader = new FileReader();
44684 reader.onload = function(e){
44685 var img = new Image();
44686 img.onload = function(){
44688 this.canvasElCtx().drawImage(img, 0, 0);
44690 img.src = e.target.result;
44693 reader.readAsDataURL(e.target.files[0]);
44696 // Bezier Point Constructor
44697 Point: (function () {
44698 function Point(x, y, time) {
44701 this.time = time || Date.now();
44703 Point.prototype.distanceTo = function (start) {
44704 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44706 Point.prototype.equals = function (other) {
44707 return this.x === other.x && this.y === other.y && this.time === other.time;
44709 Point.prototype.velocityFrom = function (start) {
44710 return this.time !== start.time
44711 ? this.distanceTo(start) / (this.time - start.time)
44718 // Bezier Constructor
44719 Bezier: (function () {
44720 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44721 this.startPoint = startPoint;
44722 this.control2 = control2;
44723 this.control1 = control1;
44724 this.endPoint = endPoint;
44725 this.startWidth = startWidth;
44726 this.endWidth = endWidth;
44728 Bezier.fromPoints = function (points, widths, scope) {
44729 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44730 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44731 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44733 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44734 var dx1 = s1.x - s2.x;
44735 var dy1 = s1.y - s2.y;
44736 var dx2 = s2.x - s3.x;
44737 var dy2 = s2.y - s3.y;
44738 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44739 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44740 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44741 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44742 var dxm = m1.x - m2.x;
44743 var dym = m1.y - m2.y;
44744 var k = l2 / (l1 + l2);
44745 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44746 var tx = s2.x - cm.x;
44747 var ty = s2.y - cm.y;
44749 c1: new scope.Point(m1.x + tx, m1.y + ty),
44750 c2: new scope.Point(m2.x + tx, m2.y + ty)
44753 Bezier.prototype.length = function () {
44758 for (var i = 0; i <= steps; i += 1) {
44760 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44761 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44763 var xdiff = cx - px;
44764 var ydiff = cy - py;
44765 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44772 Bezier.prototype.point = function (t, start, c1, c2, end) {
44773 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44774 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44775 + (3.0 * c2 * (1.0 - t) * t * t)
44776 + (end * t * t * t);
44781 throttleStroke: function(fn, wait) {
44782 if (wait === void 0) { wait = 250; }
44784 var timeout = null;
44788 var later = function () {
44789 previous = Date.now();
44791 result = fn.apply(storedContext, storedArgs);
44793 storedContext = null;
44797 return function wrapper() {
44799 for (var _i = 0; _i < arguments.length; _i++) {
44800 args[_i] = arguments[_i];
44802 var now = Date.now();
44803 var remaining = wait - (now - previous);
44804 storedContext = this;
44806 if (remaining <= 0 || remaining > wait) {
44808 clearTimeout(timeout);
44812 result = fn.apply(storedContext, storedArgs);
44814 storedContext = null;
44818 else if (!timeout) {
44819 timeout = window.setTimeout(later, remaining);